Ejemplo n.º 1
0
    override public void PopulateRenderLayer()
    {
        if (_isOnStage && _firstFacetIndex != -1)
        {
            _isMeshDirty = false;

            Vector3[] vertices = _renderLayer.vertices;
            Vector2[] uvs      = _renderLayer.uvs;
            Color[]   colors   = _renderLayer.colors;

            int vertexIndex0 = _firstFacetIndex * 4;
            int vertexIndex1 = vertexIndex0 + 1;
            int vertexIndex2 = vertexIndex0 + 2;
            int vertexIndex3 = vertexIndex0 + 3;

            int lineCount = _letterQuadLines.Length;
            for (int i = 0; i < lineCount; i++)
            {
                FLetterQuad[] quads = _letterQuadLines[i].quads;


                int quadCount = quads.Length;

                for (int q = 0; q < quadCount; q++)
                {
                    FLetterQuad quad     = quads[q];
                    FCharInfo   charInfo = quad.charInfo;

                    _concatenatedMatrix.ApplyVector3FromLocalVector2(ref vertices[vertexIndex0], quad.topLeft, 0);
                    _concatenatedMatrix.ApplyVector3FromLocalVector2(ref vertices[vertexIndex1], quad.topRight, 0);
                    _concatenatedMatrix.ApplyVector3FromLocalVector2(ref vertices[vertexIndex2], quad.bottomRight, 0);
                    _concatenatedMatrix.ApplyVector3FromLocalVector2(ref vertices[vertexIndex3], quad.bottomLeft, 0);

                    uvs[vertexIndex0] = charInfo.uvTopLeft;
                    uvs[vertexIndex1] = charInfo.uvTopRight;
                    uvs[vertexIndex2] = charInfo.uvBottomRight;
                    uvs[vertexIndex3] = charInfo.uvBottomLeft;

                    colors[vertexIndex0] = _alphaColor;
                    colors[vertexIndex1] = _alphaColor;
                    colors[vertexIndex2] = _alphaColor;
                    colors[vertexIndex3] = _alphaColor;

                    vertexIndex0 += 4;
                    vertexIndex1 += 4;
                    vertexIndex2 += 4;
                    vertexIndex3 += 4;
                }
            }

            _renderLayer.HandleVertsChange();
        }
    }
Ejemplo n.º 2
0
    private void LoadAndParseConfigFile()
    {
        TextAsset asset = (TextAsset) Resources.Load(_configPath, typeof(TextAsset));

        if(asset == null)
        {
            throw new FutileException("Couldn't find font config file " + _configPath);
        }

        string[] separators = new string[1];

        separators[0] = "\n"; //osx
        string[] lines = asset.text.Split(separators,StringSplitOptions.RemoveEmptyEntries);

        if(lines.Length <= 1) //osx line endings didn't work, try windows
        {
            separators[0] = "\r\n";
            lines = asset.text.Split(separators,StringSplitOptions.RemoveEmptyEntries);
        }

        if(lines.Length <= 1) //those line endings didn't work, so we're on a magical OS
        {
            separators[0] = "\r";
            lines = asset.text.Split(separators,StringSplitOptions.RemoveEmptyEntries);
        }

        if(lines.Length <= 1) //WHAT
        {
            throw new FutileException("Your font file is messed up");
        }

        int wordCount = 0;
        int c = 0;
        int k = 0;

        _charInfosByID = new Dictionary<uint, FCharInfo>(127);

        //insert an empty char to be used when a character isn't in the font data file
        FCharInfo emptyChar = new FCharInfo();
        _charInfosByID[0] = emptyChar;

        float resourceScale = Futile.resourceScale;

        Vector2 textureSize = _element.atlas.textureSize;

        bool wasKerningFound = false;

        int lineCount = lines.Length;

        for(int n = 0; n<lineCount; ++n)
        {
            string line = lines[n];

            string [] words = line.Split(new char[] {' '}, StringSplitOptions.RemoveEmptyEntries);

            /* we don't care about these, or else they could be in the elseif
            if(words[0] == "info") //info face="Franchise Bold" size=160 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1
            {
                //do nothing
            }
            else if(words[0] == "page") //page id=0 file="FranchiseLarge.png"
            {
                //do nothing
            }
            */

            if(words[0] == "common") //common lineHeight=168 base=26 scaleW=1024 scaleH=1024 pages=1 packed=0
            {
                //these are the height and width of the original atlas built by Hiero
                _configWidth = int.Parse(words[3].Split('=')[1]);
                //_configHeight = int.Parse(words[4].Split('=')[1]);

                //this is the ratio of the config vs the size of the actual texture element
                _configRatio = _element.sourceSize.x/_configWidth;

                _lineHeight = int.Parse(words[1].Split('=')[1]) * _configRatio;
                //_lineBase = int.Parse(words[2].Split('=')[1]) * _configRatio;
            }
            else if(words[0] == "chars") //chars count=92
            {
                int charCount = int.Parse(words[1].Split('=')[1]);
                _charInfos = new FCharInfo[charCount+1]; //gotta add 1 because the charCount seems to be off by 1
            }
            else if(words[0] == "char") //char id=32   x=0     y=0     width=0     height=0     xoffset=0     yoffset=120    xadvance=29     page=0  chnl=0 letter=a
            {
                FCharInfo charInfo = new FCharInfo();

                wordCount = words.Length;

                for(int w = 1; w<wordCount; ++w)
                {
                    string[] parts = words[w].Split('=');
                    string partName = parts[0];

                    if(partName == "letter")
                    {
                        if(parts[1].Length >= 3)
                        {
                            charInfo.letter = parts[1].Substring(1,1);
                        }
                        continue; //we don't care about the letter
                    }

                    if(partName == "\r") continue; //something weird happened with linebreaks, meh!

                    int partValue = int.Parse(parts[1]);

                    if(partName == "id")
                    {
                        charInfo.charID = partValue;
                    }
                    else if(partName == "x")
                    {
                        charInfo.x = partValue*_configRatio - _element.sourceRect.x; //offset to account for the trimmed atlas
                    }
                    else if(partName == "y")
                    {
                        charInfo.y = partValue*_configRatio - _element.sourceRect.y; //offset to account for the trimmed atlas
                    }
                    else if(partName == "width")
                    {
                        charInfo.width = partValue*_configRatio;
                    }
                    else if(partName == "height")
                    {
                        charInfo.height = partValue*_configRatio;
                    }
                    else if(partName == "xoffset")
                    {
                        charInfo.offsetX = partValue*_configRatio;
                    }
                    else if(partName == "yoffset")
                    {
                        charInfo.offsetY = partValue*_configRatio;
                    }
                    else if(partName == "xadvance")
                    {
                        charInfo.xadvance = partValue*_configRatio;
                    }
                    else if(partName == "page")
                    {
                        charInfo.page = partValue;
                    }
                }

                Rect uvRect = new Rect
                (
                    _element.uvRect.x + charInfo.x/textureSize.x*resourceScale,
                    (textureSize.y-charInfo.y-charInfo.height)/textureSize.y*resourceScale - (1.0f - _element.uvRect.yMax),
                    charInfo.width/textureSize.x*resourceScale,
                    charInfo.height/textureSize.y*resourceScale
                );

                charInfo.uvRect = uvRect;

                charInfo.uvTopLeft.Set(uvRect.xMin,uvRect.yMax);
                charInfo.uvTopRight.Set(uvRect.xMax,uvRect.yMax);
                charInfo.uvBottomRight.Set(uvRect.xMax,uvRect.yMin);
                charInfo.uvBottomLeft.Set(uvRect.xMin,uvRect.yMin);

                _charInfosByID[(uint)charInfo.charID] = charInfo;
                _charInfos[c] = charInfo;

                c++;
            }
            else if(words[0] == "kernings") //kernings count=169
            {
                wasKerningFound = true;
                int kerningCount = int.Parse(words[1].Split('=')[1]);
                _kerningInfos = new FKerningInfo[kerningCount+100]; //kerning count can be wrong so just add 100 items of potential fudge factor
            }
            else if(words[0] == "kerning") //kerning first=56  second=57  amount=-1
            {
                FKerningInfo kerningInfo = new FKerningInfo();

                kerningInfo.first = -1;

                wordCount = words.Length;

                for(int w = 1; w<wordCount; w++)
                {
                    string[] parts = words[w].Split('=');
                    if(parts.Length >= 2)
                    {
                        string partName = parts[0];
                        int partValue = int.Parse(parts[1]);

                        if(partName == "first")
                        {
                            kerningInfo.first = partValue;
                        }
                        else if(partName == "second")
                        {
                            kerningInfo.second = partValue;
                        }
                        else if(partName == "amount")
                        {
                            kerningInfo.amount = partValue * _configRatio;
                        }
                    }
                }

                if(kerningInfo.first != -1)
                {
                    _kerningInfos[k] = kerningInfo;
                }

                k++;
            }

        }

        _kerningCount = k;

        if(!wasKerningFound) //if there are no kernings at all (like in a pixel font), then make an empty kerning array
        {
            _kerningInfos = new FKerningInfo[0];
        }

        //make sure the space character doesn't have offsetY and offsetX
        if(_charInfosByID[32] != null)
        {
            _charInfosByID[32].offsetX = 0;
            _charInfosByID[32].offsetY = 0;
        }
    }
Ejemplo n.º 3
0
    private void LoadAndParseConfigFile()
    {
        TextAsset asset = (TextAsset)Resources.Load(_configPath, typeof(TextAsset));

        if (asset == null)
        {
            throw new FutileException("Couldn't find font config file " + _configPath);
        }

        string[] separators = new string[1];

        separators[0] = "\n";         //osx
        string[] lines = asset.text.Split(separators, StringSplitOptions.RemoveEmptyEntries);

        if (lines.Length <= 1)        //osx line endings didn't work, try windows
        {
            separators[0] = "\r\n";
            lines         = asset.text.Split(separators, StringSplitOptions.RemoveEmptyEntries);
        }

        if (lines.Length <= 1)        //those line endings didn't work, so we're on a magical OS
        {
            separators[0] = "\r";
            lines         = asset.text.Split(separators, StringSplitOptions.RemoveEmptyEntries);
        }

        if (lines.Length <= 1)        //WHAT
        {
            throw new FutileException("Your font file is messed up");
        }

        int wordCount = 0;
        int c         = 0;
        int k         = 0;

        _charInfosByID = new Dictionary <uint, FCharInfo>(127);

        //insert an empty char to be used when a character isn't in the font data file
        FCharInfo emptyChar = new FCharInfo();

        _charInfosByID[0] = emptyChar;

        float resourceScaleInverse = Futile.resourceScaleInverse;

        Vector2 textureSize = _element.atlas.textureSize;

        Debug.Log("texture width " + textureSize.x);

        bool wasKerningFound = false;

        int lineCount = lines.Length;

        for (int n = 0; n < lineCount; ++n)
        {
            string line = lines[n];

            string [] words = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

            /* we don't care about these, or else they could be in the elseif
             * if(words[0] == "info") //info face="Franchise Bold" size=160 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1
             * {
             *      //do nothing
             * }
             * else if(words[0] == "page") //page id=0 file="FranchiseLarge"
             * {
             *      //do nothing
             * }
             */

            if (words[0] == "common")            //common lineHeight=168 base=26 scaleW=1024 scaleH=1024 pages=1 packed=0
            {
                //these are the height and width of the original atlas built by Hiero
                _configWidth = int.Parse(words[3].Split('=')[1]);
                //_configHeight = int.Parse(words[4].Split('=')[1]);

                //this is the ratio of the config vs the size of the actual texture element
                _configRatio = _element.sourcePixelSize.x / (float)_configWidth;

                _lineHeight = ((float)int.Parse(words[1].Split('=')[1])) * _configRatio * resourceScaleInverse;
                //_lineBase = int.Parse(words[2].Split('=')[1]) * _configRatio;
            }
            else if (words[0] == "chars")            //chars count=92
            {
                int charCount = int.Parse(words[1].Split('=')[1]);
                _charInfos = new FCharInfo[charCount + 1]; //gotta add 1 because the charCount seems to be off by 1
            }
            else if (words[0] == "char")                   //char id=32   x=0     y=0     width=0     height=0     xoffset=0     yoffset=120    xadvance=29     page=0  chnl=0 letter=a
            {
                FCharInfo charInfo = new FCharInfo();

                wordCount = words.Length;

                for (int w = 1; w < wordCount; ++w)
                {
                    string[] parts    = words[w].Split('=');
                    string   partName = parts[0];

                    if (partName == "letter")
                    {
                        if (parts[1].Length >= 3)
                        {
                            charInfo.letter = parts[1].Substring(1, 1);
                        }
                        continue;                         //we don't care about the letter
                    }

                    if (partName == "\r")
                    {
                        continue;                                      //something weird happened with linebreaks, meh!
                    }
                    int   partIntValue   = int.Parse(parts[1]);
                    float partFloatValue = (float)partIntValue;

                    if (partName == "id")
                    {
                        charInfo.charID = partIntValue;
                    }
                    else if (partName == "x")
                    {
                        charInfo.x = partFloatValue * _configRatio - _element.sourceRect.x * Futile.resourceScale;                       //offset to account for the trimmed atlas
                    }
                    else if (partName == "y")
                    {
                        charInfo.y = partFloatValue * _configRatio - _element.sourceRect.y * Futile.resourceScale;                       //offset to account for the trimmed atlas
                    }
                    else if (partName == "width")
                    {
                        charInfo.width = partFloatValue * _configRatio;
                    }
                    else if (partName == "height")
                    {
                        charInfo.height = partFloatValue * _configRatio;
                    }
                    else if (partName == "xoffset")
                    {
                        charInfo.offsetX = partFloatValue * _configRatio;
                    }
                    else if (partName == "yoffset")
                    {
                        charInfo.offsetY = partFloatValue * _configRatio;
                    }
                    else if (partName == "xadvance")
                    {
                        charInfo.xadvance = partFloatValue * _configRatio;
                    }
                    else if (partName == "page")
                    {
                        charInfo.page = partIntValue;
                    }
                }

                Rect uvRect = new Rect
                              (
                    _element.uvRect.x + charInfo.x / textureSize.x,
                    (textureSize.y - charInfo.y - charInfo.height) / textureSize.y - (1.0f - _element.uvRect.yMax),
                    charInfo.width / textureSize.x,
                    charInfo.height / textureSize.y
                              );

                charInfo.uvRect = uvRect;

                charInfo.uvTopLeft.Set(uvRect.xMin, uvRect.yMax);
                charInfo.uvTopRight.Set(uvRect.xMax, uvRect.yMax);
                charInfo.uvBottomRight.Set(uvRect.xMax, uvRect.yMin);
                charInfo.uvBottomLeft.Set(uvRect.xMin, uvRect.yMin);

                //scale them AFTER they've been used for uvs
                charInfo.width    *= resourceScaleInverse;
                charInfo.height   *= resourceScaleInverse;
                charInfo.offsetX  *= resourceScaleInverse;
                charInfo.offsetY  *= resourceScaleInverse;
                charInfo.xadvance *= resourceScaleInverse;

                _charInfosByID[(uint)charInfo.charID] = charInfo;
                _charInfos[c] = charInfo;

                c++;
            }
            else if (words[0] == "kernings")            //kernings count=169
            {
                wasKerningFound = true;
                int kerningCount = int.Parse(words[1].Split('=')[1]);
                _kerningInfos = new FKerningInfo[kerningCount + 100]; //kerning count can be wrong so just add 100 items of potential fudge factor
            }
            else if (words[0] == "kerning")                           //kerning first=56  second=57  amount=-1
            {
                FKerningInfo kerningInfo = new FKerningInfo();

                kerningInfo.first = -1;

                wordCount = words.Length;

                for (int w = 1; w < wordCount; w++)
                {
                    string[] parts = words[w].Split('=');
                    if (parts.Length >= 2)
                    {
                        string partName  = parts[0];
                        int    partValue = int.Parse(parts[1]);

                        if (partName == "first")
                        {
                            kerningInfo.first = partValue;
                        }
                        else if (partName == "second")
                        {
                            kerningInfo.second = partValue;
                        }
                        else if (partName == "amount")
                        {
                            kerningInfo.amount = ((float)partValue) * _configRatio * resourceScaleInverse;
                        }
                    }
                }

                if (kerningInfo.first != -1)
                {
                    _kerningInfos[k] = kerningInfo;
                }

                k++;
            }
        }

        _kerningCount = k;


        if (!wasKerningFound)        //if there are no kernings at all (like in a pixel font), then make an empty kerning array
        {
            _kerningInfos = new FKerningInfo[0];
        }

        //make sure the space character doesn't have offsetY and offsetX
        if (_charInfosByID.ContainsKey(ASCII_SPACE))
        {
            _charInfosByID[ASCII_SPACE].offsetX = 0;
            _charInfosByID[ASCII_SPACE].offsetY = 0;
        }
    }
Ejemplo n.º 4
0
    private void LoadAndParseConfigFile()
    {
        TextAsset asset = (TextAsset)Resources.Load(_configPath, typeof(TextAsset));

        if (asset == null)
        {
            throw new Exception("Couldn't find font config file " + _configPath);
        }

        string[] separators = new string[1];

        separators[0] = "\n";         //osx
        string[] lines = asset.text.Split(separators, StringSplitOptions.RemoveEmptyEntries);

        if (lines.Length <= 1)        //osx line endings didn't work, try windows
        {
            separators[0] = "\r\n";
            lines         = asset.text.Split(separators, StringSplitOptions.RemoveEmptyEntries);
        }

        if (lines.Length <= 1)        //those line endings didn't work, so we're on a magical OS
        {
            separators[0] = "\r";
            lines         = asset.text.Split(separators, StringSplitOptions.RemoveEmptyEntries);
        }

        if (lines.Length <= 1)        //WHAT
        {
            throw new Exception("Your font file is messed up");
        }

        int wordCount = 0;
        int c         = 0;
        int k         = 0;

        _charInfosByID = new FCharInfo[127];

        float resourceScale = Futile.resourceScale;

        Vector2 textureSize = _element.atlas.textureSize;

        bool wasKerningFound = false;

        int lint = -1;

        foreach (string line in lines)
        {
            lint++;
            string [] words = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

            /* we don't care about these, or else they could be in the elseif
             * if(words[0] == "info") //info face="Franchise Bold" size=160 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1
             * {
             *      //do nothing
             * }
             * else if(words[0] == "page") //page id=0 file="FranchiseLarge.png"
             * {
             *      //do nothing
             * }
             */

            if (words[0] == "common")            //common lineHeight=168 base=26 scaleW=1024 scaleH=1024 pages=1 packed=0
            {
                //these are the height and width of the original atlas built by Hiero
                _configWidth = int.Parse(words[3].Split('=')[1]);
                //_configHeight = int.Parse(words[4].Split('=')[1]);

                //this is the ratio of the config vs the size of the actual texture element
                _configRatio = _element.sourceSize.x / _configWidth;

                _lineHeight = int.Parse(words[1].Split('=')[1]) * _configRatio;
            }
            else if (words[0] == "chars")            //chars count=92
            {
                int charCount = int.Parse(words[1].Split('=')[1]);
                _charInfos = new FCharInfo[charCount + 1]; //gotta add 1 because the charCount seems to be off by 1
            }
            else if (words[0] == "char")                   //char id=32   x=0     y=0     width=0     height=0     xoffset=0     yoffset=120    xadvance=29     page=0  chnl=0 letter=a
            {
                FCharInfo charInfo = new FCharInfo();

                wordCount = words.Length;

                for (int w = 1; w < wordCount; ++w)
                {
                    string[] parts    = words[w].Split('=');
                    string   partName = parts[0];

                    if (partName == "letter")
                    {
                        if (parts[1].Length >= 3)
                        {
                            charInfo.letter = parts[1].Substring(1, 1);
                        }
                        continue;                         //we don't care about the letter
                    }

                    if (partName == "\r")
                    {
                        continue;                                      //something weird happened with linebreaks, meh!
                    }
                    int partValue = int.Parse(parts[1]);

                    if (partName == "id")
                    {
                        charInfo.charID = partValue;
                    }
                    else if (partName == "x")
                    {
                        charInfo.x = partValue * _configRatio - _element.sourceRect.x;                       //offset to account for the trimmed atlas
                    }
                    else if (partName == "y")
                    {
                        charInfo.y = partValue * _configRatio - _element.sourceRect.y;                       //offset to account for the trimmed atlas
                    }
                    else if (partName == "width")
                    {
                        charInfo.width = partValue * _configRatio;
                    }
                    else if (partName == "height")
                    {
                        charInfo.height = partValue * _configRatio;
                    }
                    else if (partName == "xoffset")
                    {
                        charInfo.offsetX = partValue * _configRatio;
                    }
                    else if (partName == "yoffset")
                    {
                        charInfo.offsetY = partValue * _configRatio;
                    }
                    else if (partName == "xadvance")
                    {
                        charInfo.xadvance = partValue * _configRatio;
                    }
                    else if (partName == "page")
                    {
                        charInfo.page = partValue;
                    }
                }

                if (element.isRotated)
                {
                    Rect uvRect = new Rect
                                  (
                        _element.uvRect.x + _element.uvRect.width - ((charInfo.y + charInfo.height + 0.5f) / textureSize.x * resourceScale),
                        _element.uvRect.y + _element.uvRect.height - ((charInfo.x + charInfo.width + 0.5f) / textureSize.y * resourceScale),
                        charInfo.height / textureSize.x * resourceScale,
                        charInfo.width / textureSize.y * resourceScale
                                  );

                    charInfo.uvRect = uvRect;

                    charInfo.uvBottomLeft.Set(uvRect.xMin, uvRect.yMax);
                    charInfo.uvTopLeft.Set(uvRect.xMax, uvRect.yMax);
                    charInfo.uvTopRight.Set(uvRect.xMax, uvRect.yMin);
                    charInfo.uvBottomRight.Set(uvRect.xMin, uvRect.yMin);
                }
                else
                {
                    Rect uvRect = new Rect
                                  (
                        _element.uvRect.x + charInfo.x / textureSize.x * resourceScale,
                        (textureSize.y - charInfo.y - charInfo.height) / textureSize.y * resourceScale - (1.0f - _element.uvRect.yMax),
                        charInfo.width / textureSize.x * resourceScale,
                        charInfo.height / textureSize.y * resourceScale
                                  );

                    charInfo.uvRect = uvRect;

                    charInfo.uvTopLeft.Set(uvRect.xMin, uvRect.yMax);
                    charInfo.uvTopRight.Set(uvRect.xMax, uvRect.yMax);
                    charInfo.uvBottomRight.Set(uvRect.xMax, uvRect.yMin);
                    charInfo.uvBottomLeft.Set(uvRect.xMin, uvRect.yMin);
                }

                _charInfosByID[charInfo.charID] = charInfo;
                _charInfos[c] = charInfo;

                c++;
            }
            else if (words[0] == "kernings")            //kernings count=169
            {
                wasKerningFound = true;
                int kerningCount = int.Parse(words[1].Split('=')[1]);
                _kerningInfos = new FKerningInfo[kerningCount + 1]; //gotta add 1 because it's off by 1
            }
            else if (words[0] == "kerning")                         //kerning first=56  second=57  amount=-1
            {
                FKerningInfo kerningInfo = new FKerningInfo();

                wordCount = words.Length;

                for (int w = 1; w < wordCount; w++)
                {
                    string[] parts     = words[w].Split('=');
                    string   partName  = parts[0];
                    int      partValue = int.Parse(parts[1]);

                    if (partName == "first")
                    {
                        kerningInfo.first = partValue;
                    }
                    else if (partName == "second")
                    {
                        kerningInfo.second = partValue;
                    }
                    else if (partName == "amount")
                    {
                        kerningInfo.amount = partValue * _configRatio;
                    }
                }

                _kerningInfos[k] = kerningInfo;

                k++;
            }
        }

        if (!wasKerningFound)        //if there are no kernings at all (like in a pixel font), then make an empty kerning array
        {
            _kerningInfos = new FKerningInfo[0];
        }

        //make sure the space character doesn't have offsetY and offsetX
        _charInfosByID[32].offsetX = 0;
        _charInfosByID[32].offsetY = 0;
    }
Ejemplo n.º 5
0
    override public void PopulateRenderLayer()
    {
        if (_isOnStage && _firstFacetIndex != -1)
        {
            _isMeshDirty = false;

            //check if the label is empty so we don't have to bother trying to draw anything
            if (_letterQuadLines.Length == 0 || _letterQuadLines[0].quads.Length == 0)
            {
                _renderLayer.HandleVertsChange();
                return;
            }

            Vector3[] vertices = _renderLayer.vertices;
            Vector2[] uvs      = _renderLayer.uvs;
            Color[]   colors   = _renderLayer.colors;

            int vertexIndex0 = _firstFacetIndex * 4;
            int vertexIndex1 = vertexIndex0 + 1;
            int vertexIndex2 = vertexIndex0 + 2;
            int vertexIndex3 = vertexIndex0 + 3;

            Vector2 topLeft = _letterQuadLines[0].quads[0].topLeft;

            FMatrix matrixToUse = _concatenatedMatrix;

            if (_shouldSnapToPixels)
            {
                matrixToUse = matrixToUse.Clone();

                matrixToUse.tx += (Mathf.Round(topLeft.x * Futile.displayScale) * Futile.displayScaleInverse) - topLeft.x;
                matrixToUse.ty += (Mathf.Round(topLeft.y * Futile.displayScale) * Futile.displayScaleInverse) - topLeft.y;
            }

            int lineCount = _letterQuadLines.Length;
            for (int i = 0; i < lineCount; i++)
            {
                FLetterQuad[] quads = _letterQuadLines[i].quads;


                int quadCount = quads.Length;

                for (int q = 0; q < quadCount; q++)
                {
                    FLetterQuad quad     = quads[q];
                    FCharInfo   charInfo = quad.charInfo;

                    matrixToUse.ApplyVector3FromLocalVector2(ref vertices[vertexIndex0], quad.topLeft, 0);
                    matrixToUse.ApplyVector3FromLocalVector2(ref vertices[vertexIndex1], quad.topRight, 0);
                    matrixToUse.ApplyVector3FromLocalVector2(ref vertices[vertexIndex2], quad.bottomRight, 0);
                    matrixToUse.ApplyVector3FromLocalVector2(ref vertices[vertexIndex3], quad.bottomLeft, 0);

                    uvs[vertexIndex0] = charInfo.uvTopLeft;
                    uvs[vertexIndex1] = charInfo.uvTopRight;
                    uvs[vertexIndex2] = charInfo.uvBottomRight;
                    uvs[vertexIndex3] = charInfo.uvBottomLeft;

                    colors[vertexIndex0] = _alphaColor;
                    colors[vertexIndex1] = _alphaColor;
                    colors[vertexIndex2] = _alphaColor;
                    colors[vertexIndex3] = _alphaColor;

                    vertexIndex0 += 4;
                    vertexIndex1 += 4;
                    vertexIndex2 += 4;
                    vertexIndex3 += 4;
                }
            }

            _renderLayer.HandleVertsChange();
        }
    }