Jump to content
View in the app

A better way to browse. Learn more.

Forge Forums

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Featured Replies

Posted

Is there a way to give text a gradient effect? Similar to the game's drawGradientRect() Method

Would I have to use openGL methods to achieve this and if so which ones?

 

Edit: Kinda related but is there a way to set the opacity of multiple objects being drawn on the screen? Example, I have 4 strings of text and I want to set all of their opacity to 50%

 

 

Edited by -TheLittleGuy

The drawGradientRect method uses the following trick to render a gradient: The top two vertices of the quad are in one color, and the bottom two vertices are in another. Then, the shadeModel GL11.GL_SMOOTH is used, so that these colors are interpolated and create a gradient effect. To render a String, the drawString method calls the renderString method, which calls the renderStringAtPos method which ultimately calls the renderGlyph method, which calls the render method of the TexturedGlyph class. In that last render method, the color is set via the function parameters. If you created a custom TexturedGlyph that hat a render method with two colors as input, you could enable the above mentioned shadeModel to render a text with a gradient. It's a bit of work but seems definitely possible.

Just have a look at

net.minecraft.client.gui.fonts.TexturedGlyph::render

and

net.minecraftforge.fml.client.config.GuiUtils::drawGradientRect

PS: Since I'm currently working with 1.14, the class names (any maybe the function names) differ, but apart from that, everything I said should be applicable.

  • Author
2 hours ago, deerangle said:

The drawGradientRect method uses the following trick to render a gradient: The top two vertices of the quad are in one color, and the bottom two vertices are in another. Then, the shadeModel GL11.GL_SMOOTH is used, so that these colors are interpolated and create a gradient effect. To render a String, the drawString method calls the renderString method, which calls the renderStringAtPos method which ultimately calls the renderGlyph method, which calls the render method of the TexturedGlyph class. In that last render method, the color is set via the function parameters. If you created a custom TexturedGlyph that hat a render method with two colors as input, you could enable the above mentioned shadeModel to render a text with a gradient. It's a bit of work but seems definitely possible.

Just have a look at


net.minecraft.client.gui.fonts.TexturedGlyph::render

and


net.minecraftforge.fml.client.config.GuiUtils::drawGradientRect

PS: Since I'm currently working with 1.14, the class names (any maybe the function names) differ, but apart from that, everything I said should be applicable.

It seems a bit different in 1.12.2. There doesnt seem to be a TexturedGlyph class.  After the renderStringAtPos method the method renderChar is called which then either calls renderDefaultChar or renderUnicodeChar

which is where the location of the actual glyphs are "cut out" from the font texture (default.png). The coloring of the text doesnt get set until the renderString method (§ codes excluded those seem to get set inside renderStringAtPos)

So after a little testing I came up with this (for 1.12.2):

I created a Subclass of FontRenderer called GradientFontRenderer. In this class, I override the method renderDefaultChar (you should probably override the unicode method too.) so that It uses the above mentioned shadeModel and has the correct vertex order. The code for this is the following, and creates a gradient from red to blue:

@Override
protected float renderDefaultChar(int ch, boolean italic) {
    float charXPos = ch % 16 * 8f;
    float charYPos = (ch / 16) * 8f;
    float italicOffset = italic ? 1f : 0f;
    bindTexture(this.locationFontTexture);
    int charWidth = this.charWidth[ch];
    float width = (float)charWidth - 0.01F;

    GlStateManager.shadeModel(GL11.GL_SMOOTH);
    GlStateManager.glBegin(GL11.GL_QUADS);

    GlStateManager.color(1.0f, 0.0f, 0.0f);
    GlStateManager.glTexCoord2f(charXPos / 128.0F, charYPos / 128.0F); // 0 0
    GlStateManager.glVertex3f(this.posX + italicOffset, this.posY, 0.0F);

    GlStateManager.color(0.0f, 0.0f, 1.0f);
    GlStateManager.glTexCoord2f(charXPos / 128.0F, (charYPos + 7.99F) / 128.0F); // 0 1
    GlStateManager.glVertex3f(this.posX - italicOffset, this.posY + 7.99F, 0.0F);

    GlStateManager.color(0.0f, 0.0f, 1.0f);
    GlStateManager.glTexCoord2f((charXPos + width - 1.0F) / 128.0F, (charYPos + 7.99F) / 128.0F); // 1 1
    GlStateManager.glVertex3f(this.posX + width - 1.0F - italicOffset, this.posY + 7.99F, 0.0F);

    GlStateManager.color(1.0f, 0.0f, 0.0f);
    GlStateManager.glTexCoord2f((charXPos + width - 1.0F) / 128.0F, charYPos / 128.0F); // 1 0
    GlStateManager.glVertex3f(this.posX + width - 1.0F + italicOffset, this.posY, 0.0F);

    GlStateManager.glEnd();
    GlStateManager.shadeModel(GL11.GL_FLAT);
    return (float)charWidth;
}

I'm sure you can expand on this code by adding parameters and auxiliary functions to control the gradient colors to your desire.

For instantiating the GradientFontRenderer, I just created an Instance in the constructor of my Gui class. You also need to call onResourceManagerReload to load the texture for the fontrenderer and all. This is the GuiScreen code:

public class TestGuiScreen extends GuiScreen {

    private GradientFontRenderer gradientFontRenderer;

    public TestGuiScreen() {
        Minecraft mc = Minecraft.getMinecraft();
        gradientFontRenderer = new GradientFontRenderer(mc.gameSettings, new ResourceLocation("textures/font/ascii.png"), mc.renderEngine, false);
        gradientFontRenderer.onResourceManagerReload(null);
    }

    public void render() {
        ScaledResolution res = new ScaledResolution(Minecraft.getMinecraft());
        this.width = res.getScaledWidth();
        this.height = res.getScaledHeight();
        int x = (this.width) / 2;
        int y = (this.height) / 2;
        drawHelloTextWithGradient(x, y-16, 0xFFFFFFFF, 0xFFFF0000);
    }

    private void drawHelloTextWithGradient(int x, int y, int topColor, int bottomColor) {
        drawRect(x-20, y-8, x+20, y+8, 0x7F000000);
        drawCenteredGradientString(gradientFontRenderer, "hello", x, y-4, topColor, bottomColor);
    }

    public void drawCenteredGradientString(GradientFontRenderer fontRendererIn, String text, int x, int y, int color, int colorBottom)
    {
        fontRendererIn.drawString(text, x - fontRendererIn.getStringWidth(text) / 2, y, color);
    }

}

Here is a screenshot of how it looks.

2019-11-23_23_32_17.png.48402de27839505a1f207babf16feb79.png

I hope this was helpful. If you have any further questions, just ask :D

So I decided to make all the functions to render with any gradient, and here's the code (it only supports normal text, no italics, no bold, no formatting or coloring, no unicode, but you can add that with some extra work if you need it):

public class GradientFontRenderer extends FontRenderer {

    public GradientFontRenderer(GameSettings gameSettingsIn, ResourceLocation location, TextureManager textureManagerIn, boolean unicode) {
        super(gameSettingsIn, location, textureManagerIn, unicode);
    }

    public int drawGradientString(String text, float x, float y, int topColor, int bottomColor, boolean dropShadow)
    {
        enableAlpha();
        int i;

        if (dropShadow)
        {
            i = this.renderGradientString(text, x + 1.0F, y + 1.0F, topColor, bottomColor, true);
            i = Math.max(i, this.renderGradientString(text, x, y, topColor, bottomColor, false));
        }
        else
        {
            i = this.renderGradientString(text, x, y, topColor, bottomColor, false);
        }

        return i;
    }

    private int renderGradientString(String text, float x, float y, int topColor, int bottomColor, boolean dropShadow)
    {
        if (text == null)
        {
            return 0;
        }
        else
        {

            if ((topColor & -67108864) == 0)
            {
                topColor |= -16777216;
            }

            if ((bottomColor & -67108864) == 0)
            {
                bottomColor |= -16777216;
            }

            if (dropShadow)
            {
                topColor = (topColor & 16579836) >> 2 | topColor & -16777216;
                bottomColor = (bottomColor & 16579836) >> 2 | bottomColor & -16777216;
            }

            this.posX = x;
            this.posY = y;
            this.renderGradientStringAtPos(text, dropShadow, topColor, bottomColor);
            return (int)this.posX;
        }
    }

    private void renderGradientStringAtPos(String text, boolean shadow, int topColor, int bottomColor) {
        for (int i = 0; i < text.length(); ++i) {
            char c0 = text.charAt(i);
            int j = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000".indexOf(c0);

            float f1 = j == -1 ? 0.5f : 1f;
            boolean flag = (c0 == 0 || j == -1) && shadow;

            if (flag) {
                this.posX -= f1;
                this.posY -= f1;
            }

            float f = this.renderGradientChar(c0, topColor, bottomColor);

            if (flag) {
                this.posX += f1;
                this.posY += f1;
            }

            doDraw(f);
        }
    }

    private float renderGradientChar(char ch, int topColor, int bottomColor) {
        if (ch == 160) return 4.0F; // forge: display nbsp as space. MC-2595
        if (ch == ' ') {
            return 4.0F;
        } else {
            int i = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000".indexOf(ch);
            if (i != -1) {
                return this.renderGradientDefaultChar(i, topColor, bottomColor);
            } else {
                throw new RuntimeException("Unrecognized char: " + ch);
            }
        }
    }

    protected float renderGradientDefaultChar(int ch, int topColor, int bottomColor) {
        float topAlpha = ((topColor >> 24) & 0xFF) / 255f;
        float topRed = ((topColor >> 16) & 0xFF) / 255f;
        float topGreen = ((topColor >> 8) & 0xFF) / 255f;
        float topBlue = (topColor & 0xFF) / 255f;

        float bottomAlpha = ((bottomColor >> 24) & 0xFF) / 255f;
        float bottomRed = ((bottomColor >> 16) & 0xFF) / 255f;
        float bottomGreen = ((bottomColor >> 8) & 0xFF) / 255f;
        float bottomBlue = (bottomColor & 0xFF) / 255f;

        float charXPos = ch % 16 * 8f;
        float charYPos = (ch / 16) * 8f;
        bindTexture(this.locationFontTexture);
        int charWidth = this.charWidth[ch];
        float width = (float) charWidth - 0.01F;

        GlStateManager.shadeModel(GL11.GL_SMOOTH);
        GlStateManager.glBegin(GL11.GL_QUADS);

        GlStateManager.color(topRed, topGreen, topBlue, topAlpha);
        GlStateManager.glTexCoord2f(charXPos / 128.0F, charYPos / 128.0F); // 0 0
        GlStateManager.glVertex3f(this.posX, this.posY, 0.0F);

        GlStateManager.color(bottomRed, bottomGreen, bottomBlue, bottomAlpha);
        GlStateManager.glTexCoord2f(charXPos / 128.0F, (charYPos + 7.99F) / 128.0F); // 0 1
        GlStateManager.glVertex3f(this.posX, this.posY + 7.99F, 0.0F);

        GlStateManager.color(bottomRed, bottomGreen, bottomBlue, bottomAlpha);
        GlStateManager.glTexCoord2f((charXPos + width - 1.0F) / 128.0F, (charYPos + 7.99F) / 128.0F); // 1 1
        GlStateManager.glVertex3f(this.posX + width - 1.0F, this.posY + 7.99F, 0.0F);

        GlStateManager.color(topRed, topGreen, topBlue, topAlpha);
        GlStateManager.glTexCoord2f((charXPos + width - 1.0F) / 128.0F, charYPos / 128.0F); // 1 0
        GlStateManager.glVertex3f(this.posX + width - 1.0F, this.posY, 0.0F);

        GlStateManager.glEnd();
        GlStateManager.shadeModel(GL11.GL_FLAT);
        return (float) charWidth;
    }
}

Then change this code from above to the following:

public void drawCenteredGradientString(GradientFontRenderer fontRendererIn, String text, int x, int y, int color, int colorBottom)
{
    fontRendererIn.drawGradientString(text, x - fontRendererIn.getStringWidth(text) / 2, y, color, colorBottom, true);
}

And it should work.

  • Author
2 hours ago, deerangle said:

So I decided to make all the functions to render with any gradient, and here's the code (it only supports normal text, no italics, no bold, no formatting or coloring, no unicode, but you can add that with some extra work if you need it):


public class GradientFontRenderer extends FontRenderer {

    public GradientFontRenderer(GameSettings gameSettingsIn, ResourceLocation location, TextureManager textureManagerIn, boolean unicode) {
        super(gameSettingsIn, location, textureManagerIn, unicode);
    }

    public int drawGradientString(String text, float x, float y, int topColor, int bottomColor, boolean dropShadow)
    {
        enableAlpha();
        int i;

        if (dropShadow)
        {
            i = this.renderGradientString(text, x + 1.0F, y + 1.0F, topColor, bottomColor, true);
            i = Math.max(i, this.renderGradientString(text, x, y, topColor, bottomColor, false));
        }
        else
        {
            i = this.renderGradientString(text, x, y, topColor, bottomColor, false);
        }

        return i;
    }

    private int renderGradientString(String text, float x, float y, int topColor, int bottomColor, boolean dropShadow)
    {
        if (text == null)
        {
            return 0;
        }
        else
        {

            if ((topColor & -67108864) == 0)
            {
                topColor |= -16777216;
            }

            if ((bottomColor & -67108864) == 0)
            {
                bottomColor |= -16777216;
            }

            if (dropShadow)
            {
                topColor = (topColor & 16579836) >> 2 | topColor & -16777216;
                bottomColor = (bottomColor & 16579836) >> 2 | bottomColor & -16777216;
            }

            this.posX = x;
            this.posY = y;
            this.renderGradientStringAtPos(text, dropShadow, topColor, bottomColor);
            return (int)this.posX;
        }
    }

    private void renderGradientStringAtPos(String text, boolean shadow, int topColor, int bottomColor) {
        for (int i = 0; i < text.length(); ++i) {
            char c0 = text.charAt(i);
            int j = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000".indexOf(c0);

            float f1 = j == -1 ? 0.5f : 1f;
            boolean flag = (c0 == 0 || j == -1) && shadow;

            if (flag) {
                this.posX -= f1;
                this.posY -= f1;
            }

            float f = this.renderGradientChar(c0, topColor, bottomColor);

            if (flag) {
                this.posX += f1;
                this.posY += f1;
            }

            doDraw(f);
        }
    }

    private float renderGradientChar(char ch, int topColor, int bottomColor) {
        if (ch == 160) return 4.0F; // forge: display nbsp as space. MC-2595
        if (ch == ' ') {
            return 4.0F;
        } else {
            int i = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000".indexOf(ch);
            if (i != -1) {
                return this.renderGradientDefaultChar(i, topColor, bottomColor);
            } else {
                throw new RuntimeException("Unrecognized char: " + ch);
            }
        }
    }

    protected float renderGradientDefaultChar(int ch, int topColor, int bottomColor) {
        float topAlpha = ((topColor >> 24) & 0xFF) / 255f;
        float topRed = ((topColor >> 16) & 0xFF) / 255f;
        float topGreen = ((topColor >> 8) & 0xFF) / 255f;
        float topBlue = (topColor & 0xFF) / 255f;

        float bottomAlpha = ((bottomColor >> 24) & 0xFF) / 255f;
        float bottomRed = ((bottomColor >> 16) & 0xFF) / 255f;
        float bottomGreen = ((bottomColor >> 8) & 0xFF) / 255f;
        float bottomBlue = (bottomColor & 0xFF) / 255f;

        float charXPos = ch % 16 * 8f;
        float charYPos = (ch / 16) * 8f;
        bindTexture(this.locationFontTexture);
        int charWidth = this.charWidth[ch];
        float width = (float) charWidth - 0.01F;

        GlStateManager.shadeModel(GL11.GL_SMOOTH);
        GlStateManager.glBegin(GL11.GL_QUADS);

        GlStateManager.color(topRed, topGreen, topBlue, topAlpha);
        GlStateManager.glTexCoord2f(charXPos / 128.0F, charYPos / 128.0F); // 0 0
        GlStateManager.glVertex3f(this.posX, this.posY, 0.0F);

        GlStateManager.color(bottomRed, bottomGreen, bottomBlue, bottomAlpha);
        GlStateManager.glTexCoord2f(charXPos / 128.0F, (charYPos + 7.99F) / 128.0F); // 0 1
        GlStateManager.glVertex3f(this.posX, this.posY + 7.99F, 0.0F);

        GlStateManager.color(bottomRed, bottomGreen, bottomBlue, bottomAlpha);
        GlStateManager.glTexCoord2f((charXPos + width - 1.0F) / 128.0F, (charYPos + 7.99F) / 128.0F); // 1 1
        GlStateManager.glVertex3f(this.posX + width - 1.0F, this.posY + 7.99F, 0.0F);

        GlStateManager.color(topRed, topGreen, topBlue, topAlpha);
        GlStateManager.glTexCoord2f((charXPos + width - 1.0F) / 128.0F, charYPos / 128.0F); // 1 0
        GlStateManager.glVertex3f(this.posX + width - 1.0F, this.posY, 0.0F);

        GlStateManager.glEnd();
        GlStateManager.shadeModel(GL11.GL_FLAT);
        return (float) charWidth;
    }
}

Then change this code from above to the following:


public void drawCenteredGradientString(GradientFontRenderer fontRendererIn, String text, int x, int y, int color, int colorBottom)
{
    fontRendererIn.drawGradientString(text, x - fontRendererIn.getStringWidth(text) / 2, y, color, colorBottom, true);
}

And it should work.

I actually do have a question: I changed the gradient to start from the left instead of the top however, the current method applies the gradient effect to the individual character. What would have to be changed in order to have the gradient be applied to the entire string? Where would the GL Functions go in that case? Where they are currently or would it have to go after the chars are "assembled" into the string? Ex: i have the string "Test String"  the start color is Green and the end color is Red, the first letter will start green and will fade to red by the time it gets to the last letter.

You would have to color different vertices differently based on the length they are through the string to have that type of gradient. You would need extra code to store the length of the string that you’re drawing and to use that data in the render char method

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

I've added a horizontal parameter to every gradient text function so that you can make horizontal and vertical gradients. This is the updated code:

public class GradientFontRenderer extends FontRenderer {

    private static final String charmap = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";

    public GradientFontRenderer(GameSettings gameSettingsIn, ResourceLocation location, TextureManager textureManagerIn, boolean unicode) {
        super(gameSettingsIn, location, textureManagerIn, unicode);
    }

    public int drawGradientString(String text, float x, float y, int topColor, int bottomColor, boolean dropShadow, boolean horizontal) {
        enableAlpha();
        int i;

        if (dropShadow) {
            i = this.renderGradientString(text, x + 1.0F, y + 1.0F, topColor, bottomColor, true, horizontal);
            i = Math.max(i, this.renderGradientString(text, x, y, topColor, bottomColor, false, horizontal));
        } else {
            i = this.renderGradientString(text, x, y, topColor, bottomColor, false, horizontal);
        }

        return i;
    }

    private int renderGradientString(String text, float x, float y, int startColor, int endColor, boolean dropShadow, boolean horizontal) {
        if (text == null) {
            return 0;
        } else {

            if ((startColor & -67108864) == 0) {
                startColor |= -16777216;
            }

            if ((endColor & -67108864) == 0) {
                endColor |= -16777216;
            }

            if (dropShadow) {
                startColor = (startColor & 16579836) >> 2 | startColor & -16777216;
                endColor = (endColor & 16579836) >> 2 | endColor & -16777216;
            }

            this.posX = x;
            this.posY = y;
            this.renderGradientStringAtPos(text, dropShadow, startColor, endColor, horizontal);
            return (int) this.posX;
        }
    }

    private void renderGradientStringAtPos(String text, boolean shadow, int startColor, int endColor, boolean horizontal) {
        float totalWidth = this.getStringWidth(text);
        float currentCountWidth = 0;

        for (int i = 0; i < text.length(); ++i) {
            char c0 = text.charAt(i);
            int j = charmap.indexOf(c0);

            float f1 = j == -1 ? 0.5f : 1f;
            boolean flag = (c0 == 0 || j == -1) && shadow;

            if (flag) {
                this.posX -= f1;
                this.posY -= f1;
            }

            float f;
            if (horizontal) {
                float nextCharWidth = this.getCharWidth(c0);
                float firstMix = currentCountWidth / totalWidth;
                float lastMix = (currentCountWidth + nextCharWidth) / totalWidth;
                int firstColor = colorMix(startColor, endColor, firstMix);
                int lastColor = colorMix(startColor, endColor, lastMix);
                f = this.renderGradientChar(c0, firstColor, lastColor, true);
                currentCountWidth += f;
            } else {
                f = this.renderGradientChar(c0, startColor, endColor, false);
            }

            if (flag) {
                this.posX += f1;
                this.posY += f1;
            }

            doDraw(f);
        }
    }

    private int colorMix(int startColor, int endColor, float mix) {
        float startAlpha = ((startColor >> 24) & 0xFF) / 255f;
        float startRed = ((startColor >> 16) & 0xFF) / 255f;
        float startGreen = ((startColor >> 8) & 0xFF) / 255f;
        float startBlue = (startColor & 0xFF) / 255f;

        float endAlpha = ((endColor >> 24) & 0xFF) / 255f;
        float endRed = ((endColor >> 16) & 0xFF) / 255f;
        float endGreen = ((endColor >> 8) & 0xFF) / 255f;
        float endBlue = (endColor & 0xFF) / 255f;

        int mixAlpha = (int) (((1 - mix) * startAlpha + mix * endAlpha) * 255);
        int mixRed = (int) (((1 - mix) * startRed + mix * endRed) * 255);
        int mixGreen = (int) (((1 - mix) * startGreen + mix * endGreen) * 255);
        int mixBlue = (int) (((1 - mix) * startBlue + mix * endBlue) * 255);

        return (mixAlpha << 24) | (mixRed << 16) | (mixGreen << 8) | mixBlue;
    }

    private float renderGradientChar(char ch, int startColor, int endColor, boolean horizontal) {
        if (ch == 160) return 4.0F; // forge: display nbsp as space. MC-2595
        if (ch == ' ') {
            return 4.0F;
        } else {
            int i = charmap.indexOf(ch);
            if (i != -1) {
                return this.renderGradientDefaultChar(i, startColor, endColor, horizontal);
            } else {
                throw new RuntimeException("Unrecognized char: " + ch);
            }
        }
    }

    protected float renderGradientDefaultChar(int ch, int startColor, int endColor, boolean horizontal) {
        float startAlpha = ((startColor >> 24) & 0xFF) / 255f;
        float startRed = ((startColor >> 16) & 0xFF) / 255f;
        float startGreen = ((startColor >> 8) & 0xFF) / 255f;
        float startBlue = (startColor & 0xFF) / 255f;

        float endAlpha = ((endColor >> 24) & 0xFF) / 255f;
        float endRed = ((endColor >> 16) & 0xFF) / 255f;
        float endGreen = ((endColor >> 8) & 0xFF) / 255f;
        float endBlue = (endColor & 0xFF) / 255f;

        float charXPos = ch % 16 * 8f;
        float charYPos = (ch / 16) * 8f;
        bindTexture(this.locationFontTexture);
        int charWidth = this.charWidth[ch];
        float width = (float) charWidth - 0.01F;

        GlStateManager.shadeModel(GL11.GL_SMOOTH);
        GlStateManager.glBegin(GL11.GL_QUADS);

        GlStateManager.color(startRed, startGreen, startBlue, startAlpha);
        GlStateManager.glTexCoord2f(charXPos / 128.0F, charYPos / 128.0F); // 0 0
        GlStateManager.glVertex3f(this.posX, this.posY, 0.0F);

        if (horizontal) {
            GlStateManager.color(startRed, startGreen, startBlue, startAlpha);
        } else {
            GlStateManager.color(endRed, endGreen, endBlue, endAlpha);
        }
        GlStateManager.glTexCoord2f(charXPos / 128.0F, (charYPos + 7.99F) / 128.0F); // 0 1
        GlStateManager.glVertex3f(this.posX, this.posY + 7.99F, 0.0F);

        GlStateManager.color(endRed, endGreen, endBlue, endAlpha);
        GlStateManager.glTexCoord2f((charXPos + width - 1.0F) / 128.0F, (charYPos + 7.99F) / 128.0F); // 1 1
        GlStateManager.glVertex3f(this.posX + width - 1.0F, this.posY + 7.99F, 0.0F);

        if (horizontal) {
            GlStateManager.color(endRed, endGreen, endBlue, endAlpha);
        } else {
            GlStateManager.color(startRed, startGreen, startBlue, startAlpha);
        }
        GlStateManager.glTexCoord2f((charXPos + width - 1.0F) / 128.0F, charYPos / 128.0F); // 1 0
        GlStateManager.glVertex3f(this.posX + width - 1.0F, this.posY, 0.0F);

        GlStateManager.glEnd();
        GlStateManager.shadeModel(GL11.GL_FLAT);
        return (float) charWidth;
    }
}

2019-11-24_10_23_33.png.431785ef4179a820f30edd88bbea722c.png

Pretty basic. It just linearly interpolates the colors for each char so that the gradient colors of each char match up.

You probably want to use hex instead of decimal for your color codes and masks.

-67108864 -> 0xFC000000

-16777216 -> 0xFF000000

16579836 -> 0xFCFCFC

It makes it a lot easier to understand as colours are packed 0x(AA)RRGGBBx

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

1 minute ago, Cadiboo said:

You probably want to use hex instead of decimal for your color codes and masks.

-67108864 -> 0xFC000000

-16777216 -> 0xFF000000

16579836 -> 0xFCFCFC

It makes it a lot easier to understand as colours are packed 0x(AA)RRGGBBx

Since I copied most of the code for the GradientFontRenderer from the original FontRenderer, which is decompiled and therefore doesn't use hex literals, the color values are in decimal.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

Important Information

By using this site, you agree to our Terms of Use.

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.