gdx-studio

#libgdx#java#desktop

git clone https://git.pyrossh.dev/gdx-studio

An IDE for creating Games using libgdx and Java supported on all platforms Android, iOS, Desktop


src_libs/com/badlogic/gdx/tools/hiero/BMFontUtil.java
a62d533 1
/*******************************************************************************
a62d533 2
 * Copyright 2011 See AUTHORS file.
a62d533 3
 * 
a62d533 4
 * Licensed under the Apache License, Version 2.0 (the "License");
a62d533 5
 * you may not use this file except in compliance with the License.
a62d533 6
 * You may obtain a copy of the License at
a62d533 7
 * 
a62d533 8
 *   http://www.apache.org/licenses/LICENSE-2.0
a62d533 9
 * 
a62d533 10
 * Unless required by applicable law or agreed to in writing, software
a62d533 11
 * distributed under the License is distributed on an "AS IS" BASIS,
a62d533 12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
a62d533 13
 * See the License for the specific language governing permissions and
a62d533 14
 * limitations under the License.
a62d533 15
 ******************************************************************************/
a62d533 16
a62d533 17
package com.badlogic.gdx.tools.hiero;
a62d533 18
a62d533 19
import java.awt.Font;
a62d533 20
import java.awt.font.GlyphMetrics;
a62d533 21
import java.awt.font.GlyphVector;
a62d533 22
import java.awt.image.BufferedImage;
a62d533 23
import java.awt.image.WritableRaster;
a62d533 24
import java.io.File;
a62d533 25
import java.io.FileOutputStream;
a62d533 26
import java.io.IOException;
a62d533 27
import java.io.PrintStream;
a62d533 28
import java.nio.IntBuffer;
a62d533 29
import java.util.ArrayList;
a62d533 30
import java.util.HashMap;
a62d533 31
import java.util.Iterator;
a62d533 32
import java.util.List;
a62d533 33
import java.util.Map;
a62d533 34
a62d533 35
import javax.imageio.ImageIO;
a62d533 36
a62d533 37
import org.lwjgl.BufferUtils;
a62d533 38
import org.lwjgl.opengl.GL11;
a62d533 39
import org.lwjgl.opengl.GL12;
a62d533 40
a62d533 41
import com.badlogic.gdx.Gdx;
a62d533 42
import com.badlogic.gdx.tools.hiero.unicodefont.Glyph;
a62d533 43
import com.badlogic.gdx.tools.hiero.unicodefont.GlyphPage;
a62d533 44
import com.badlogic.gdx.tools.hiero.unicodefont.UnicodeFont;
a62d533 45
a62d533 46
/** @author Nathan Sweet */
a62d533 47
public class BMFontUtil {
a62d533 48
	private final UnicodeFont unicodeFont;
a62d533 49
a62d533 50
	public BMFontUtil (UnicodeFont unicodeFont) {
a62d533 51
		this.unicodeFont = unicodeFont;
a62d533 52
	}
a62d533 53
a62d533 54
	public void save (File outputBMFontFile) throws IOException {
a62d533 55
		File outputDir = outputBMFontFile.getParentFile();
a62d533 56
		String outputName = outputBMFontFile.getName();
a62d533 57
		if (outputName.endsWith(".fnt")) outputName = outputName.substring(0, outputName.length() - 4);
a62d533 58
a62d533 59
		unicodeFont.loadGlyphs();
a62d533 60
a62d533 61
		PrintStream out = new PrintStream(new FileOutputStream(new File(outputDir, outputName + ".fnt")));
a62d533 62
		Font font = unicodeFont.getFont();
a62d533 63
		int pageWidth = unicodeFont.getGlyphPageWidth();
a62d533 64
		int pageHeight = unicodeFont.getGlyphPageHeight();
a62d533 65
		out.println("info face=\"" + font.getFontName() + "\" size=" + font.getSize() + " bold=" + (font.isBold() ? 1 : 0)
a62d533 66
			+ " italic=" + (font.isItalic() ? 1 : 0) + " charset=\"\" unicode=0 stretchH=100 smooth=1 aa=1 padding="
a62d533 67
			+ unicodeFont.getPaddingTop() + "," + unicodeFont.getPaddingLeft() + "," + unicodeFont.getPaddingBottom() + ","
a62d533 68
			+ unicodeFont.getPaddingRight() + " spacing=" + unicodeFont.getPaddingAdvanceX() + ","
a62d533 69
			+ unicodeFont.getPaddingAdvanceY());
a62d533 70
		out.println("common lineHeight=" + unicodeFont.getLineHeight() + " base=" + unicodeFont.getAscent() + " scaleW="
a62d533 71
			+ pageWidth + " scaleH=" + pageHeight + " pages=" + unicodeFont.getGlyphPages().size() + " packed=0");
a62d533 72
a62d533 73
		int pageIndex = 0, glyphCount = 0;
a62d533 74
		for (Iterator pageIter = unicodeFont.getGlyphPages().iterator(); pageIter.hasNext();) {
a62d533 75
			GlyphPage page = (GlyphPage)pageIter.next();
a62d533 76
			String fileName;
a62d533 77
			if (pageIndex == 0 && !pageIter.hasNext())
a62d533 78
				fileName = outputName + ".png";
a62d533 79
			else
a62d533 80
				fileName = outputName + (pageIndex + 1) + ".png";
a62d533 81
			out.println("page id=" + pageIndex + " file=\"" + fileName + "\"");
a62d533 82
			glyphCount += page.getGlyphs().size();
a62d533 83
			pageIndex++;
a62d533 84
		}
a62d533 85
a62d533 86
		out.println("chars count=" + glyphCount);
a62d533 87
a62d533 88
		// Always output space entry (codepoint 32).
a62d533 89
		int[] glyphMetrics = getGlyphMetrics(font, 32);
a62d533 90
		int xAdvance = glyphMetrics[1];
a62d533 91
		out.println("char id=32   x=0     y=0     width=0     height=0     xoffset=0     yoffset=" + unicodeFont.getAscent()
a62d533 92
			+ "    xadvance=" + xAdvance + "     page=0  chnl=0 ");
a62d533 93
a62d533 94
		pageIndex = 0;
a62d533 95
		List allGlyphs = new ArrayList(512);
a62d533 96
		for (Iterator pageIter = unicodeFont.getGlyphPages().iterator(); pageIter.hasNext();) {
a62d533 97
			GlyphPage page = (GlyphPage)pageIter.next();
a62d533 98
			for (Iterator glyphIter = page.getGlyphs().iterator(); glyphIter.hasNext();) {
a62d533 99
				Glyph glyph = (Glyph)glyphIter.next();
a62d533 100
a62d533 101
				glyphMetrics = getGlyphMetrics(font, glyph.getCodePoint());
a62d533 102
				int xOffset = glyphMetrics[0];
a62d533 103
				xAdvance = glyphMetrics[1];
a62d533 104
a62d533 105
				out.println("char id=" + glyph.getCodePoint() + "   " + "x=" + (int)(glyph.getU() * pageWidth) + "     y="
a62d533 106
					+ (int)(glyph.getV() * pageHeight) + "     width=" + glyph.getWidth() + "     height=" + glyph.getHeight()
a62d533 107
					+ "     xoffset=" + xOffset + "     yoffset=" + glyph.getYOffset() + "    xadvance=" + xAdvance + "     page="
a62d533 108
					+ pageIndex + "  chnl=0 ");
a62d533 109
			}
a62d533 110
			allGlyphs.addAll(page.getGlyphs());
a62d533 111
			pageIndex++;
a62d533 112
		}
a62d533 113
a62d533 114
		String ttfFileRef = unicodeFont.getFontFile();
a62d533 115
		if (ttfFileRef == null)
a62d533 116
			System.out.println("Kerning information could not be output because a TTF font file was not specified.");
a62d533 117
		else {
a62d533 118
			Kerning kerning = new Kerning();
a62d533 119
			try {
a62d533 120
				kerning.load(Gdx.files.internal(ttfFileRef).read(), font.getSize());
a62d533 121
			} catch (IOException ex) {
a62d533 122
				System.out.println("Unable to read kerning information from font: " + ttfFileRef);
a62d533 123
			}
a62d533 124
a62d533 125
			Map glyphCodeToCodePoint = new HashMap();
a62d533 126
			for (Iterator iter = allGlyphs.iterator(); iter.hasNext();) {
a62d533 127
				Glyph glyph = (Glyph)iter.next();
a62d533 128
				glyphCodeToCodePoint.put(new Integer(getGlyphCode(font, glyph.getCodePoint())), new Integer(glyph.getCodePoint()));
a62d533 129
			}
a62d533 130
a62d533 131
			List kernings = new ArrayList(256);
a62d533 132
			class KerningPair {
a62d533 133
				public int firstCodePoint, secondCodePoint, offset;
a62d533 134
			}
a62d533 135
			for (Iterator iter1 = allGlyphs.iterator(); iter1.hasNext();) {
a62d533 136
				Glyph firstGlyph = (Glyph)iter1.next();
a62d533 137
				int firstGlyphCode = getGlyphCode(font, firstGlyph.getCodePoint());
a62d533 138
				int[] values = kerning.getValues(firstGlyphCode);
a62d533 139
				if (values == null) continue;
a62d533 140
				for (int i = 0; i < values.length; i++) {
a62d533 141
					Integer secondCodePoint = (Integer)glyphCodeToCodePoint.get(new Integer(values[i] & 0xffff));
a62d533 142
					if (secondCodePoint == null) continue; // We may not be outputting the second character.
a62d533 143
					int offset = values[i] >> 16;
a62d533 144
					KerningPair pair = new KerningPair();
a62d533 145
					pair.firstCodePoint = firstGlyph.getCodePoint();
a62d533 146
					pair.secondCodePoint = secondCodePoint.intValue();
a62d533 147
					pair.offset = offset;
a62d533 148
					kernings.add(pair);
a62d533 149
				}
a62d533 150
			}
a62d533 151
			out.println("kernings count=" + kerning.getCount());
a62d533 152
			for (Iterator iter = kernings.iterator(); iter.hasNext();) {
a62d533 153
				KerningPair pair = (KerningPair)iter.next();
a62d533 154
				out.println("kerning first=" + pair.firstCodePoint + "  second=" + pair.secondCodePoint + "  amount=" + pair.offset);
a62d533 155
			}
a62d533 156
		}
a62d533 157
		out.close();
a62d533 158
a62d533 159
		int width = unicodeFont.getGlyphPageWidth();
a62d533 160
		int height = unicodeFont.getGlyphPageHeight();
a62d533 161
		IntBuffer buffer = BufferUtils.createIntBuffer(width * height);
a62d533 162
		BufferedImage pageImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
a62d533 163
		int[] row = new int[width];
a62d533 164
a62d533 165
		pageIndex = 0;
a62d533 166
		for (Iterator pageIter = unicodeFont.getGlyphPages().iterator(); pageIter.hasNext();) {
a62d533 167
			GlyphPage page = (GlyphPage)pageIter.next();
a62d533 168
			String fileName;
a62d533 169
			if (pageIndex == 0 && !pageIter.hasNext())
a62d533 170
				fileName = outputName + ".png";
a62d533 171
			else
a62d533 172
				fileName = outputName + (pageIndex + 1) + ".png";
a62d533 173
a62d533 174
			page.getTexture().bind();
a62d533 175
			buffer.clear();
a62d533 176
			GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, buffer);
a62d533 177
			WritableRaster raster = pageImage.getRaster();
a62d533 178
			for (int y = 0; y < height; y++) {
a62d533 179
				buffer.get(row);
a62d533 180
				raster.setDataElements(0, y, width, 1, row);
a62d533 181
			}
a62d533 182
			File imageOutputFile = new File(outputDir, fileName);
a62d533 183
			ImageIO.write(pageImage, "png", imageOutputFile);
a62d533 184
a62d533 185
			pageIndex++;
a62d533 186
		}
a62d533 187
	}
a62d533 188
a62d533 189
	private int getGlyphCode (Font font, int codePoint) {
a62d533 190
		char[] chars = Character.toChars(codePoint);
a62d533 191
		GlyphVector vector = font.layoutGlyphVector(GlyphPage.renderContext, chars, 0, chars.length, Font.LAYOUT_LEFT_TO_RIGHT);
a62d533 192
		return vector.getGlyphCode(0);
a62d533 193
	}
a62d533 194
a62d533 195
	private int[] getGlyphMetrics (Font font, int codePoint) {
a62d533 196
		// xOffset and xAdvance will be incorrect for unicode characters such as combining marks or non-spacing characters
a62d533 197
		// (eg Pnujabi's "\u0A1C\u0A47") that require the context of surrounding glyphs to determine spacing, but thisis the
a62d533 198
		// best we can do with the BMFont format.
a62d533 199
		char[] chars = Character.toChars(codePoint);
a62d533 200
		GlyphVector vector = font.layoutGlyphVector(GlyphPage.renderContext, chars, 0, chars.length, Font.LAYOUT_LEFT_TO_RIGHT);
a62d533 201
		GlyphMetrics metrics = vector.getGlyphMetrics(0);
a62d533 202
		int xOffset = vector.getGlyphPixelBounds(0, GlyphPage.renderContext, 0.5f, 0).x - unicodeFont.getPaddingLeft();
a62d533 203
		int xAdvance = (int)(metrics.getAdvanceX() + unicodeFont.getPaddingAdvanceX() + unicodeFont.getPaddingLeft() + unicodeFont
a62d533 204
			.getPaddingRight());
a62d533 205
		return new int[] {xOffset, xAdvance};
a62d533 206
	}
a62d533 207
}