/** * Copyright 2013 The Loon Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package loon.action.sprite; import java.util.ArrayList; import loon.action.sprite.SpriteBatch.SpriteEffects; import loon.core.geom.RectBox; import loon.core.geom.Vector2f; import loon.core.graphics.device.LColor; import loon.core.graphics.device.LImage; import loon.core.graphics.opengl.GLLoader; import loon.core.graphics.opengl.LTexture; import loon.core.graphics.opengl.LTexture.Format; import loon.core.resource.Resources; import loon.utils.collection.ArrayByte; //通过LGame工具提取XNA字库生成的PAK文件,可以通过此类重新读取及显示 public class SpriteFont { public static SpriteFont read(String resName) { try { ArrayList<RectBox> xGlyphs = new ArrayList<RectBox>(), xCropping = new ArrayList<RectBox>(); ArrayList<Character> xChars = new ArrayList<Character>(); int xSpacingV; float xSpacingH; ArrayList<float[]> xKerning = new ArrayList<float[]>(); ArrayByte arrays = new ArrayByte(Resources.openResource(resName), ArrayByte.BIG_ENDIAN); int size = arrays.readInt(); LImage image = LImage.createImage(arrays.readByteArray(size)); int count = arrays.readInt(); while (count-- > 0) { xGlyphs.add(new RectBox(arrays.readInt(), arrays.readInt(), arrays.readInt(), arrays.readInt())); xCropping.add(new RectBox(arrays.readInt(), arrays.readInt(), arrays.readInt(), arrays.readInt())); xChars.add((char) arrays.readInt()); } xSpacingV = arrays.readInt(); xSpacingH = arrays.readFloat(); count = arrays.readInt(); while (count-- > 0) { xKerning.add(new float[] { arrays.readFloat(), arrays.readFloat(), arrays.readFloat() }); } arrays.dispose(); return new SpriteFont(new LTexture(GLLoader.getTextureData(image), Format.LINEAR), xGlyphs, xCropping, xChars, xSpacingV, xSpacingH, xKerning, 'A'); } catch (Exception e) { e.printStackTrace(); } return null; } protected LTexture texture; protected ArrayList<RectBox> glyphs; protected ArrayList<RectBox> cropping; protected ArrayList<Character> charMap; protected int lineSpacing; protected int maxCharY; protected float spacing; protected ArrayList<float[]> kerning; protected char defaultchar; public SpriteFont(LTexture tex2d, ArrayList<RectBox> gs, ArrayList<RectBox> crops, ArrayList<Character> chars, int line, float space, ArrayList<float[]> kern, char def) { this.texture = tex2d; this.glyphs = gs; this.cropping = crops; this.charMap = chars; this.lineSpacing = 0; this.spacing = 0f; this.kerning = kern; this.defaultchar = def; int max = 0; for (RectBox rect : glyphs) { if (max == 0 || rect.getHeight() > max) { max = (int) rect.getHeight(); } } this.maxCharY = max; } protected void drawChar(SpriteBatch batch, float x, float y, RectBox glyph, RectBox cropping, LColor color) { batch.draw(texture, x + cropping.x, y + cropping.y, glyph.getWidth(), glyph.getHeight(), glyph.getX(), glyph.getY(), glyph.getWidth(), glyph.getHeight(), color); } protected void drawString(SpriteBatch batch, CharSequence cs, float x, float y) { drawString(batch, cs, x, y, LColor.white); } protected void drawString(SpriteBatch batch, CharSequence cs, float x, float y, LColor color) { float xx = 0, yy = 0; for (int i = 0; i < cs.length(); i++) { char c = cs.charAt(i), c2 = i != 0 ? cs.charAt(i - 1) : 0; if (c2 != 0) { for (int j = 0; j < kerning.size(); j++) { if (kerning.get(j)[1] == c && kerning.get(j)[0] == c2) { xx += kerning.get(j)[2]; break; } } } for (int j = 0; j < charMap.size(); j++) { if (charMap.get(j) != c) { continue; } drawChar(batch, x + xx, y + yy, glyphs.get(j), cropping.get(j), color); xx += glyphs.get(j).getWidth() + spacing; } if (c == '\n') { xx = 0; yy += getLineHeight(); } } } private final Vector2f pos = new Vector2f(); protected void drawString(SpriteBatch batch, CharSequence cs, Vector2f local, LColor color, float rotation, Vector2f origin, Vector2f scale, SpriteEffects spriteEffects) { pos.set(0, 0); int flip = 1; float beginningofline = 0f; boolean flag = true; if (spriteEffects == SpriteEffects.FlipHorizontally) { beginningofline = this.measure(cs).x * scale.x; flip = -1; } if (spriteEffects == SpriteEffects.FlipVertically) { pos.y = (this.measure(cs).y - this.lineSpacing) * scale.y; } else { pos.y = 0f; } pos.x = beginningofline; for (int i = 0; i < cs.length(); i++) { char character = cs.charAt(i); switch (character) { case '\r': break; case '\n': flag = true; pos.x = beginningofline; if (spriteEffects == SpriteEffects.FlipVertically) { pos.y -= this.lineSpacing * scale.y; } else { pos.y += this.lineSpacing * scale.y; } break; default: { int indexForCharacter = this.characterIndex(character); float[] charkerning = this.kerning.get(indexForCharacter); if (flag) { charkerning[0] = Math.max(charkerning[0], 0f); } else { pos.x += (this.spacing * scale.x) * flip; } pos.x += (charkerning[0] * scale.x) * flip; RectBox rectangle = this.glyphs.get(indexForCharacter); RectBox rectangle2 = this.cropping.get(indexForCharacter); Vector2f position = pos.cpy(); position.x += rectangle2.x * scale.x; position.y += rectangle2.y * scale.y; position.addLocal(local); batch.draw(this.texture, position, rectangle, color, rotation, origin, scale, spriteEffects); flag = false; pos.x += ((charkerning[1] + charkerning[2]) * scale.x) * flip; break; } } } } public int getWidth(CharSequence cs) { ArrayList<Float> list = new ArrayList<Float>(); int y = 0; for (int i = 0; i < cs.length(); i++) { if (list.size() <= y) list.add(0f); char c = cs.charAt(i), c2 = i != 0 ? cs.charAt(i - 1) : 0; if (c2 != 0) for (int j = 0; i < kerning.size(); j++) { if (kerning.get(j)[1] == c && kerning.get(j)[0] == c2) { list.set(y, list.get(y) + kerning.get(j)[2]); break; } } for (int j = 0; j < charMap.size(); j++) { if (charMap.get(j) != c) { continue; } list.set(y, list.get(y) + glyphs.get(j).getWidth()); if (i != cs.length() - 1) list.set(y, list.get(y) + spacing); } if (c == '\n') y++; } float maxX = 0f; for (int j = 0; j < list.size(); j++) { if (maxX == 0f || list.get(j) > maxX) { maxX = list.get(j); } } return (int) (float) maxX; } public int getHeight(CharSequence cs) { return cs.toString().split("\\\\n").length * getLineHeight(); } public int getLineHeight() { return maxCharY + lineSpacing; } public int characterIndex(char character) { int lowindex = 0; int highindex = this.charMap.size() - 1; while (lowindex <= highindex) { int index = lowindex + ((highindex - lowindex) >> 1); if (this.charMap.get(index) == character) { return index; } if (this.charMap.get(index) < character) { lowindex = index + 1; } else { highindex = index - 1; } } if (this.defaultchar != '\0') { char ch = this.defaultchar; if (character != ch) { return this.characterIndex(ch); } } throw new IllegalArgumentException("Character not in Font"); } protected Vector2f measure(CharSequence cs) { if (cs.length() == 0) { return new Vector2f(); } Vector2f zero = new Vector2f(); zero.y = this.lineSpacing; float min = 0f; int count = 0; float z = 0f; boolean flag = true; for (int i = 0; i < cs.length(); i++) { if (cs.charAt(i) != '\r') { if (cs.charAt(i) == '\n') { zero.x += Math.max(z, 0f); z = 0f; min = Math.max(zero.x, min); zero = new Vector2f(); zero.y = this.lineSpacing; flag = true; count++; } else { float[] vector2 = this.kerning.get(this.characterIndex(cs .charAt(i))); if (flag) { vector2[0] = Math.max(vector2[0], 0f); } else { zero.x += this.spacing + z; } zero.x += vector2[0] + vector2[1]; z = vector2[2]; RectBox rectangle = this.cropping.get(this .characterIndex(cs.charAt(i))); zero.y = Math.max(zero.y, rectangle.height); flag = false; } } } zero.x += Math.max(z, 0f); zero.y += count * this.lineSpacing; zero.x = Math.max(zero.x, min); return zero; } public Vector2f measureString(CharSequence cs) { return measure(cs); } }