/** * Copyright 2008 - 2015 The Loon Game Engine 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. * * @project loon * @author cping * @email:javachenpeng@yahoo.com * @version 0.5 */ package loon.opengl; import loon.LRelease; import loon.LSystem; import loon.canvas.LColor; import loon.font.LFont; import loon.utils.ObjectMap; import loon.utils.StringUtils; import loon.utils.ObjectMap.Entries; import loon.utils.ObjectMap.Entry; import loon.utils.TArray; public final class LSTRDictionary { private final int CACHE_SIZE = LSystem.DEFAULT_MAX_CACHE_SIZE * 2; private static LSTRDictionary instance; public final static LSTRDictionary make() { return new LSTRDictionary(); } public final static LSTRDictionary get() { if (instance != null) { return instance; } synchronized (LSTRDictionary.class) { if (instance == null) { instance = make(); } return instance; } } public void setAsyn(boolean asyn) { this.tmp_asyn = asyn; } public boolean isAsyn() { return this.tmp_asyn; } public boolean asyn() { return this.tmp_asyn; } private boolean tmp_asyn = true; private final ObjectMap<String, LFont> cacheList = new ObjectMap<String, LFont>(20); private final ObjectMap<String, Dict> fontList = new ObjectMap<String, Dict>(20); private final ObjectMap<LFont, Dict> englishFontList = new ObjectMap<LFont, Dict>(20); // 每次渲染图像到纹理时,同时追加一些常用非中文标记上去,以避免LSTRFont反复重构纹理 private final static String ADDED = " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:.^,!?@#$%^&*(){}[]<>\"'\\/+-~~▼【】:,。…?!"; private final static char[] checkMessage = ADDED.toCharArray(); public final static char split = '$'; private Dict _lastDict; private String _lastMessage; private static StringBuffer _lazyKey; public static class Dict implements LRelease { TArray<Character> dicts; LSTRFont font; public static Dict newDict() { return new Dict(); } public Dict() { dicts = new TArray<Character>(512); } public LSTRFont getSTR() { return font; } public boolean include(String mes) { final char[] chars = mes.toCharArray(); int size = chars.length; for (int i = 0; i < size; i++) { char flag = chars[i]; if (!dicts.contains(flag)) { return false; } } return true; } public boolean isClose() { return font.isClose(); } @Override public void close() { if (font != null) { font.close(); font = null; } if (dicts != null) { dicts.clear(); dicts = null; } } } public void clearEnglishLazy() { synchronized (englishFontList) { for (Dict d : englishFontList.values()) { if (d != null) { d.close(); d = null; } } englishFontList.clear(); } } public void clearStringLazy() { synchronized (cacheList) { if (cacheList != null) { cacheList.clear(); } } synchronized (fontList) { for (Dict d : fontList.values()) { if (d != null) { d.close(); d = null; } } fontList.clear(); } } public final boolean checkMessage(String key, String message) { final char[] chars = message.toCharArray(); final char[] list = key.toCharArray(); int size = chars.length; int limit = list.length; int idx = 0; for (int j = 0; j < limit; j++) { char name = list[j]; for (int i = 0; i < size; i++) { char flag = chars[i]; if (flag == name) { idx++; } if (idx >= limit) { return true; } } if (idx >= limit) { return true; } } return false; } private boolean checkEnglishString(String mes) { int len = mes.length(); int count = 0; for (int n = 0; n < len; n++) { for (int i = 0, j = checkMessage.length; i < j; i++) { if (count >= len) { return true; } if (checkMessage[i] == mes.charAt(n)) { count++; } } } return count == len; } private StringBuffer tmpBuffer = null; public final Dict bind(final LFont font, final TArray<CharSequence> chars) { CharSequence[] buffers = new CharSequence[chars.size]; for (int i = 0, size = buffers.length; i < size; i++) { buffers[i] = chars.get(i); } return bind(font, StringUtils.unificationCharSequence(buffers)); } public final Dict bind(final LFont font, final String[] messages) { return bind(font, StringUtils.unificationStrings(messages)); } public final Dict bind(final LFont font, final String mes) { if (StringUtils.isEmpty(mes)) { return new Dict(); } if (mes.equals(_lastMessage) && _lastDict != null && !_lastDict.isClose()) { return _lastDict; } _lastMessage = mes; if (checkEnglishString(mes)) { Dict pDict = englishFontList.get(font); if (pDict != null && pDict.isClose()) { englishFontList.remove(font); pDict = null; } if (pDict == null) { pDict = Dict.newDict(); pDict.font = new LSTRFont(font, ADDED, tmp_asyn); englishFontList.put(font, pDict); } return (_lastDict = pDict); } final String message = StringUtils.unificationStrings(mes + ADDED); if (cacheList.size > CACHE_SIZE) { clearStringLazy(); } synchronized (fontList) { LFont cFont = cacheList.get(message); if (cFont == null) { for (Entries<String, LFont> it = cacheList.iterator(); it.hasNext();) { Entry<String, LFont> obj = it.next(); String key = obj.key; if (checkMessage(key, message)) { cFont = obj.value; break; } } } String fontFlag = font.getFontName() + "_" + font.getStyle() + "_" + font.getSize(); Dict pDict = fontList.get(fontFlag); if (pDict != null && pDict.isClose()) { fontList.remove(fontFlag); pDict = null; } // 判定当前font与字体和已存在的文字图片纹理,是否和缓存的font适配 if ((cFont == null || pDict == null || (pDict != null && !pDict.include(mes)))) { if (pDict == null) { pDict = Dict.newDict(); fontList.put(fontFlag, pDict); } synchronized (pDict) { cacheList.put(message, font); TArray<Character> charas = pDict.dicts; int oldSize = charas.size; char[] chars = message.toCharArray(); int size = chars.length; for (int i = 0; i < size; i++) { char flag = chars[i]; if (!charas.contains(flag)) { charas.add(flag); } } int newSize = charas.size; // 如果旧有大小,不等于新的纹理字符大小,重新扩展LSTRFont纹理字符 if (oldSize != newSize) { if (pDict.font != null) { pDict.font.close(); pDict.font = null; } if (tmpBuffer == null) { tmpBuffer = new StringBuffer(newSize); } else { tmpBuffer.delete(0, tmpBuffer.length()); } for (int i = 0; i < newSize; i++) { tmpBuffer.append(charas.get(i)); } // 个别浏览器纹理同步会卡出国,只能异步…… pDict.font = new LSTRFont(font, tmpBuffer.toString(), tmp_asyn); } } } return (_lastDict = pDict); } } public final void drawString(LFont font, String message, float x, float y, float angle, LColor c) { Dict pDict = bind(font, message); if (pDict.font != null) { synchronized (pDict.font) { pDict.font.drawString(message, x, y, angle, c); } } } public final void drawString(LFont font, String message, float x, float y, float sx, float sy, float ax, float ay, float angle, LColor c) { Dict pDict = bind(font, message); if (pDict.font != null) { synchronized (pDict.font) { pDict.font.drawString(message, x, y, sx, sy, ax, ay, angle, c); } } } public final void drawString(GLEx gl, LFont font, String message, float x, float y, float angle, LColor c) { Dict pDict = bind(font, message); if (pDict.font != null) { synchronized (pDict.font) { pDict.font.drawString(gl, message, x, y, angle, c); } } } public final void drawString(GLEx gl, LFont font, String message, float x, float y, float sx, float sy, float angle, LColor c) { Dict pDict = bind(font, message); if (pDict.font != null) { synchronized (pDict.font) { pDict.font.drawString(gl, message, x, y, sx, sy, angle, c); } } } public final void drawString(GLEx gl, LFont font, String message, float x, float y, float sx, float sy, float ax, float ay, float angle, LColor c) { Dict pDict = bind(font, message); if (pDict.font != null) { synchronized (pDict.font) { pDict.font.drawString(gl, x, y, sx, sy, ax, ay, angle, message, c); } } } /** * 生成特定字符串的缓存用ID * * @param font * @param text * @return */ public final static String makeStringLazyKey(final LFont font, final String text) { int hashCode = 0; hashCode = LSystem.unite(hashCode, font.getSize()); hashCode = LSystem.unite(hashCode, font.getStyle()); hashCode = LSystem.unite(hashCode, font.getAscent()); hashCode = LSystem.unite(hashCode, font.getLeading()); hashCode = LSystem.unite(hashCode, font.getDescent()); if (_lazyKey == null) { _lazyKey = new StringBuffer(); _lazyKey.append(font.getFontName().toLowerCase()); _lazyKey.append(hashCode); _lazyKey.append(split); _lazyKey.append(text); } else { _lazyKey.delete(0, _lazyKey.length()); _lazyKey.append(font.getFontName().toLowerCase()); _lazyKey.append(hashCode); _lazyKey.append(split); _lazyKey.append(text); } return _lazyKey.toString(); } public final LSTRFont STRFont(LFont font) { if (fontList != null) { for (Dict d : fontList.values()) { if (d != null && d.font != null && d.font.getFont().equals(font)) { return d.font; } } } return null; } public final static String getAddedString() { return ADDED; } public final void dispose() { cacheList.clear(); clearStringLazy(); clearEnglishLazy(); } }