Java tutorial
/* * Copyright (C) 2013 UnluckyNinja * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.github.unluckyninja.mousekiller.graphics; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.math.Matrix4; import java.awt.image.BufferedImage; import java.io.BufferedReader; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Random; import java.util.logging.Level; import java.util.logging.Logger; import javax.imageio.ImageIO; /** * * @author UnluckyNinja */ public class TextRenderer { private float posX; private float posY; private boolean randomStyle; private boolean boldStyle; private boolean strikethroughStyle; private boolean underlineStyle; private boolean italicStyle; /** * Array of the start/end column (in upper/lower nibble) for every glyph in * the /font directory. */ private byte[] glyphWidth; private int[] colorCode = new int[32]; private int textColor; private float alpha = 1.0f; private int pixelWeight = 15; // for rendering private Pixmap[] pixmaps; private Texture texture; private int currentPage; private SpriteBatch batch; // never log this!! private static String allowedCharacters; private Random fontRandom; private boolean unicodeFlag = true; static { allowedCharacters = getAllowedCharacters(); } { glyphWidth = new byte[65536]; readGlyphSizes(); pixmaps = new Pixmap[256]; readTextures(0); initColor(); } public TextRenderer() { batch = new SpriteBatch(); batch.enableBlending(); } // INT??byte private static byte[] readColor(int[] intcolor) { int i = 0; byte[] bytecolor = new byte[65535 * 4]; for (int j = 0; j < 65535; j++) { int index = intcolor[j]; if (index == 0) { bytecolor[i++] = 0; bytecolor[i++] = 0; bytecolor[i++] = 0; bytecolor[i++] = 0; } else { bytecolor[i++] = -1; bytecolor[i++] = -1; bytecolor[i++] = -1; bytecolor[i++] = -1; } } return bytecolor; } /** * ???? * * @param i */ public void readTextures(int i) { if (pixmaps[i] != null) { return; } String s = String.format("res/textures/font/unicode_page_%02X.png", new Object[] { Integer.valueOf(i) }); FileHandle file = Gdx.files.internal(s); if (file.exists()) { try { BufferedImage image = ImageIO.read(file.read()); Pixmap map = new Pixmap(256, 256, Pixmap.Format.RGBA8888); int[] colors = new int[65536]; for (int j = 0; j < 65536; j++) { colors[j] = 0; } image.getData().getPixels(0, 0, 256, 256, (int[]) colors); ByteBuffer pixels = map.getPixels(); pixels.clear(); pixels.put(readColor(colors)); pixels.position(0); pixmaps[i] = map; } catch (IOException ex) { ex.printStackTrace(); } } } /** * ????? * * @param toRender * @param x * @param y * @param size */ public void drawString(String toRender, float x, float y, float size) { posX = x; posY = y; pixelWeight = getPixelWeight(size); batch.begin(); this.renderString(toRender, false); batch.end(); } /** * Render a single line string at the current (posX,posY) and update posX * posX, posY ? posX */ private void renderString(String toRender, boolean isShadow) { // ??? for (int i = 0; i < toRender.length(); ++i) { // ? char c = toRender.charAt(i); // ? int index; // ??? int RGB ? int color; // (alt+1 6 7)? if (c == 167 && i + 1 < toRender.length()) { // 16?+6? index = "0123456789abcdefklmnor".indexOf(toRender.toLowerCase().charAt(i + 1)); // if (index < 16) { // ???? this.randomStyle = false; this.boldStyle = false; this.strikethroughStyle = false; this.underlineStyle = false; this.italicStyle = false; // ???? if (index < 0 || index > 15) { index = 15; } // ? if (isShadow) { index += 16; } // colorCode? int 32??16??16?? color = this.colorCode[index]; this.textColor = color; // LWJGL? int4?RGB batch.setColor((float) (color >> 16) / 255.0F, (float) (color >> 8 & 255) / 255.0F, (float) (color & 255) / 255.0F, this.alpha); // k } else if (index == 16) { this.randomStyle = true; // l } else if (index == 17) { this.boldStyle = true; // m } else if (index == 18) { this.strikethroughStyle = true; // n } else if (index == 19) { this.underlineStyle = true; // o } else if (index == 20) { this.italicStyle = true; // r } else if (index == 21) { this.randomStyle = false; this.boldStyle = false; this.strikethroughStyle = false; this.underlineStyle = false; this.italicStyle = false; batch.setColor((float) (textColor >> 16) / 255.0F, (float) (textColor >> 8 & 255) / 255.0F, (float) (textColor & 255) / 255.0F, this.alpha); } // ?? ++i; // } else { index = allowedCharacters.indexOf(c); // ????????allowedCharacters if (this.randomStyle && index > 0) { int w = 0; do { //?? color = this.fontRandom.nextInt(allowedCharacters.length()); i++; if (i == 32) { break; } // } while (this.glyphWidth[index + 32] != this.glyphWidth[color + 32]); // ? index = color; } // Unicode?? Unicode? float offset = this.unicodeFlag ? 0.5F * pixelWeight / 16.0f : 1.0F * pixelWeight / 16.0f; boolean compact = (index <= 0 || this.unicodeFlag) && isShadow; // Unicode? if (compact) { this.posX -= offset; this.posY -= offset; } // ?? float offsetX = this.renderCharAtPos(index, c, this.italicStyle); // Unicode??? if (compact) { this.posX += offset; this.posY += offset; } // ?? if (this.boldStyle) { this.posX += offset; // ? if (compact) { this.posX -= offset; this.posY -= offset; } this.renderCharAtPos(index, c, this.italicStyle); this.posX -= offset; if (compact) { this.posX += offset; this.posY += offset; } // ?? ++offsetX; } /* ? // Tessellator drawer; if (this.strikethroughStyle) { drawer = Tessellator.instance; // OPENGL??? GL11.glDisable(GL11.GL_TEXTURE_2D); drawer.startDrawingQuads(); drawer.addVertex((double) this.posX, (double) (this.posY + (float) (this.FONT_HEIGHT / 2)), 0.0D); drawer.addVertex((double) (this.posX + offsetX), (double) (this.posY + (float) (this.FONT_HEIGHT / 2)), 0.0D); drawer.addVertex((double) (this.posX + offsetX), (double) (this.posY + (float) (this.FONT_HEIGHT / 2) - 1.0F), 0.0D); drawer.addVertex((double) this.posX, (double) (this.posY + (float) (this.FONT_HEIGHT / 2) - 1.0F), 0.0D); drawer.draw(); // ???? GL11.glEnable(GL11.GL_TEXTURE_2D); } if (this.underlineStyle) { drawer = Tessellator.instance; // OPENGL??? GL11.glDisable(GL11.GL_TEXTURE_2D); drawer.startDrawingQuads(); // -1?? // "int offset = this.italicStyle ? -1 : 0;" // 1.4BUG int underlineOffset = this.underlineStyle ? -1 : 0; drawer.addVertex((double) (this.posX + (float) underlineOffset), (double) (this.posY + (float) this.FONT_HEIGHT), 0.0D); drawer.addVertex((double) (this.posX + offsetX), (double) (this.posY + (float) this.FONT_HEIGHT), 0.0D); drawer.addVertex((double) (this.posX + offsetX), (double) (this.posY + (float) this.FONT_HEIGHT - 1.0F), 0.0D); drawer.addVertex((double) (this.posX + (float) underlineOffset), (double) (this.posY + (float) this.FONT_HEIGHT - 1.0F), 0.0D); drawer.draw(); // ???? GL11.glEnable(GL11.GL_TEXTURE_2D); } */ this.posX += (float) ((int) offsetX); } } } /** * Pick how to render a single character and return the width used. * ??Unicode?? */ private float renderCharAtPos(int index, char c, boolean isItalic) { // ???? unicode return c == 32 ? 4.0F * pixelWeight / 16.0f : (index > 0 && !this.unicodeFlag ? /*this.renderDefaultChar(index + 32, isItalic)*/ 0.0f : this.renderUnicodeChar(c, isItalic)); } /** * Render a single character with the default.png font at current * (posX,posY) location... *//* ?UTF-8 private float renderDefaultChar(int index, boolean isItalic) { float pixelX = (float) (index % 16 * 8); float pixelY = (float) (index / 16 * 8); float offset = isItalic ? 1.0F : 0.0F; // ? OPENGL???? this.renderEngine.bindTexture(this.fontTextureName); // ? float width = (float) this.charWidth[index] - 0.01F; GL11.glBegin(GL11.GL_TRIANGLE_STRIP);// GL11.glTexCoord2f(pixelX / 128.0F, pixelY / 128.0F); GL11.glVertex3f(this.posX + offset, this.posY, 0.0F); GL11.glTexCoord2f(pixelX / 128.0F, (pixelY + 7.99F) / 128.0F); GL11.glVertex3f(this.posX - offset, this.posY + 7.99F, 0.0F); GL11.glTexCoord2f((pixelX + width) / 128.0F, pixelY / 128.0F); GL11.glVertex3f(this.posX + width + offset, this.posY, 0.0F); GL11.glTexCoord2f((pixelX + width) / 128.0F, (pixelY + 7.99F) / 128.0F); GL11.glVertex3f(this.posX + width - offset, this.posY + 7.99F, 0.0F); GL11.glEnd(); return (float) this.charWidth[index]; }*/ /** * Render a single Unicode character at current (posX,posY) location using * one of the /font/glyph_XX.png files... ??(posX,posY)? Unicode * assets/minecraft/textures/font glyph_XX.png */ private float renderUnicodeChar(char c, boolean isItalic) { // glyphWidth glyph_sizes.bin?? // ?? Unicode if (this.glyphWidth[c] == 0) { return 0.0F; } else { // ?1256 int page = c / 256; if (currentPage != page) { batch.flush(); if (texture != null) { texture.dispose(); } readTextures(page); texture = new Texture(pixmaps[page]); currentPage = page; } // ??? // glyphWidth??(16p*16p)???? // ??? int highNibble = this.glyphWidth[c] >>> 4; int lowNibble = this.glyphWidth[c] & 15; float leftColumn = (float) highNibble; float rightColumn = (float) (lowNibble + 1); // +????? x float pixelX = (float) (c % 16 * 16) + leftColumn; // ??*(16p)???? float pixelY = (float) ((c & 255) / 16 * 16); float width = rightColumn - leftColumn - 0.02F; float offset = isItalic ? 1.0F : 0.0F; // opengl // GL11.glBegin(GL11.GL_TRIANGLE_STRIP); // GL11.glTexCoord2f(pixelX / 256.0F, pixelY / 256.0F); // GL11.glVertex3f(this.posX + offset, this.posY, 0.0F); // GL11.glTexCoord2f(pixelX / 256.0F, (pixelY + 15.98F) / 256.0F); // GL11.glVertex3f(this.posX - offset, this.posY + 7.99F, 0.0F); // GL11.glTexCoord2f((pixelX + width) / 256.0F, pixelY / 256.0F); // GL11.glVertex3f(this.posX + width / 2.0F + offset, this.posY, 0.0F); // GL11.glTexCoord2f((pixelX + width) / 256.0F, (pixelY + 15.98F) / 256.0F); // GL11.glVertex3f(this.posX + width / 2.0F - offset, this.posY + 7.99F, 0.0F); // GL11.glEnd(); TextureRegion region = new TextureRegion(texture, pixelX / 256.0F, pixelY / 256.0F, (pixelX + width) / 256.0F, (pixelY + 15.98F) / 256.0F); batch.draw(region, posX, posY, width * pixelWeight / 16.0f, 16f * pixelWeight / 16.0f); // batch.draw(texture, posX, posY); return ((rightColumn - leftColumn) + 1.0F) * pixelWeight / 16.0f; } } public static int getPixelWeight(float point) { return (int) ((point / 72f) * 96); } private void readGlyphSizes() { this.glyphWidth = Gdx.files.internal("res/font/glyph_sizes.bin").readBytes(); } private void initColor() { for (int i = 0; i < 32; ++i) { int j = (i >> 3 & 1) * 85; int k = (i >> 2 & 1) * 170 + j; int l = (i >> 1 & 1) * 170 + j; int i1 = (i & 1) * 170 + j; if (i == 6) { k += 85; } if (i >= 16) { k /= 4; l /= 4; i1 /= 4; } this.colorCode[i] = (k & 255) << 16 | (l & 255) << 8 | i1 & 255; } } /** * Load the font.txt resource file, that is on UTF-8 format. This file * contains the characters that minecraft can render Strings on screen. */ private static String getAllowedCharacters() { StringBuilder s = new StringBuilder(4096); try (BufferedReader bufferedreader = Gdx.files.internal("res/font.txt").reader(1024, "UTF-8")) { String s1 = ""; while ((s1 = bufferedreader.readLine()) != null) { if (!s1.startsWith("#")) { s = s.append(s1); } } } catch (IOException ex) { Logger.getLogger(TextRenderer.class.getName()).log(Level.SEVERE, null, ex); } return s.toString(); } /** * ??????? * * @param matrix */ public void setProjectionMatrix(Matrix4 matrix) { batch.setProjectionMatrix(matrix); } }