/src/processing/core/BitmapFontFactoryProxy.java

http://mt4j.googlecode.com/ · Java · 495 lines · 219 code · 50 blank · 226 comment · 30 complexity · c4edc1f2dbb9c0b4e48c393e5ac0176f MD5 · raw file

  1. /***********************************************************************
  2. * mt4j Copyright (c) 2008 - 2010 Christopher Ruff, Fraunhofer-Gesellschaft All rights reserved.
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. ***********************************************************************/
  18. package processing.core;
  19. import java.io.FileNotFoundException;
  20. import java.util.ArrayList;
  21. import java.util.List;
  22. import org.mt4j.MTApplication;
  23. import org.mt4j.components.visibleComponents.font.BitmapFont;
  24. import org.mt4j.components.visibleComponents.font.BitmapFontCharacter;
  25. import org.mt4j.components.visibleComponents.font.IFont;
  26. import org.mt4j.components.visibleComponents.font.fontFactories.IFontFactory;
  27. import org.mt4j.util.MT4jSettings;
  28. import org.mt4j.util.MTColor;
  29. import org.mt4j.util.logging.ILogger;
  30. import org.mt4j.util.logging.MTLoggerFactory;
  31. import processing.core.PFont.Glyph;
  32. /**
  33. * A factory for creating BitmapFont objects.
  34. * @author Christopher Ruff
  35. */
  36. public class BitmapFontFactoryProxy implements IFontFactory {
  37. /** The Constant logger. */
  38. private static final ILogger logger;
  39. // = MTLoggerFactory.getLogger(BitmapFontFactoryProxy.class.getName());
  40. static{
  41. logger = MTLoggerFactory.getLogger(BitmapFontFactoryProxy.class.getName());
  42. // logger.setLevel(ILogger.ERROR);
  43. // logger.setLevel(ILogger.WARN);
  44. logger.setLevel(ILogger.DEBUG);
  45. }
  46. public static String defaultCharacters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZÁ?É?Í?Ó?abcdefghijklmnopqrstuvwxyzá?é?í?ó?<>|,;.:-_#'+*!\"§$%&/()=?´{[]}\\@";
  47. // static{
  48. // FontManager.getInstance().registerFontFactory("", new BitmapFontFactory());
  49. // }
  50. public IFont getCopy(IFont font) {
  51. if (font instanceof BitmapFont) {
  52. BitmapFont bf = (BitmapFont) font;
  53. BitmapFont copy = new BitmapFont((BitmapFontCharacter[]) bf.getCharacters(), bf.getDefaultHorizontalAdvX(), bf.getFontFamily(), bf.getFontMaxAscent(), bf.getFontMaxDescent(), bf.getUnitsPerEM(), bf.getOriginalFontSize(), bf.getFillColor(), /*bf.getStrokeColor(),*/ bf.isAntiAliased());
  54. return copy;
  55. }
  56. return null;
  57. }
  58. public IFont createFont(PApplet pa, String fontName, int fontSize, MTColor color) {
  59. return this.createFont(pa, fontName, fontSize, color, color, true);
  60. }
  61. public IFont createFont(PApplet pa, String fontName, int fontSize, MTColor color, boolean antiAliased) {
  62. return this.createFont(pa, fontName, fontSize, color, color, antiAliased);
  63. }
  64. /* (non-Javadoc)
  65. * @see org.mt4j.components.visibleComponents.font.fontFactories.IFontFactory#createFont(processing.core.PApplet, java.lang.String, int, org.mt4j.util.MTColor, org.mt4j.util.MTColor)
  66. */
  67. public IFont createFont(
  68. PApplet pa,
  69. String fontFileName,
  70. int fontSize,
  71. MTColor fillColor,
  72. MTColor strokeColor
  73. ) {
  74. return this.createFont(pa, fontFileName, fontSize, fillColor, strokeColor, true);
  75. }
  76. /* (non-Javadoc)
  77. * @see org.mt4j.components.visibleComponents.font.fontFactories.IFontFactory#createFont(processing.core.PApplet, java.lang.String, int, org.mt4j.util.MTColor, org.mt4j.util.MTColor)
  78. */
  79. public IFont createFont(
  80. PApplet pa,
  81. String fontFileName,
  82. int fontSize,
  83. MTColor fillColor,
  84. MTColor strokeColor,
  85. boolean antiAliased
  86. ) {
  87. PFont p5Font = null;
  88. try {
  89. p5Font = this.getProcessingFont(pa, fontFileName, fontSize, antiAliased);
  90. } catch (FileNotFoundException e1) {
  91. e1.printStackTrace();
  92. }
  93. List<BitmapFontCharacter> bitMapCharacters = this.createCharacters(pa, p5Font, defaultCharacters, fillColor /*, strokeColor*/);
  94. //font is null sometimes (vlw)
  95. /*
  96. Font f = p5Font.getFont();
  97. FontMetrics fm = pa.getFontMetrics(f);
  98. Map<TextAttribute, ?> atts = f.getAttributes();
  99. Set<TextAttribute> attKeys = atts.keySet();
  100. for (Iterator iterator = attKeys.iterator(); iterator.hasNext();) {
  101. TextAttribute textAttribute = (TextAttribute) iterator.next();
  102. Object value = atts.get(textAttribute);
  103. logger.debug("Key: " + textAttribute + " Value: " + value);
  104. }
  105. // FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(f);
  106. */
  107. int defaultHorizontalAdvX = (!bitMapCharacters.isEmpty())? bitMapCharacters.get(0).getHorizontalDist() : Math.round(p5Font.descent() * fontSize); //FIXME HACK!
  108. String fontFamily = p5Font.getPostScriptName();
  109. // String fontFamily = f.getFamily();
  110. //FIXME ascent() and descent() return to small values! wheres the difference??
  111. int fontMaxAscent = Math.round(p5Font.ascent()* (fontSize));
  112. fontMaxAscent +=(float)fontSize/5.5f; //FIXME HACK! because the same ttf fonts seem to have bigger ascents
  113. // int fontMaxAscent = p5Font.lazyMetrics.getAscent();
  114. int fontMaxDescent = Math.round(p5Font.descent() * fontSize);
  115. /*
  116. //TODO INFO: because in vector font this is a negative value, too
  117. Font f = p5Font.getFont();
  118. if (f != null){
  119. FontMetrics fm = pa.getFontMetrics(f);
  120. fontMaxDescent = fm.getDescent();
  121. }
  122. */
  123. fontMaxDescent *= -1; //We use negative descent values
  124. //logger.debug("Bitmapfont max descent: " + fontMaxDescent);
  125. // int fontMaxAscent = Math.round(p5Font.ascent()*fontSize);
  126. // int fontMaxDescent = Math.round(p5Font.descent()*fontSize);
  127. // int fontMaxAscent = fm.getMaxAscent();
  128. // int fontMaxDescent = fm.getMaxDescent();
  129. int unitsPerEm = 1000; //FIXME HACK!
  130. int originalFontSize = fontSize; //important for font cache
  131. PImage dummy = new PImage(1,1);
  132. // /*
  133. //Manually add a newLine character to the font
  134. BitmapFontCharacter newLine = new BitmapFontCharacter(pa, dummy, "\n", 0, 0, 0);
  135. newLine.setPickable(false);
  136. newLine.setVisible(false);
  137. newLine.setNoFill(true);
  138. newLine.setNoStroke(true);
  139. newLine.setName("newline");
  140. bitMapCharacters.add(newLine);
  141. //Manually add a SPACE character to the font
  142. // int spaceAdvancex = defaultHorizontalAdvX;
  143. // int spaceAdvancex = fm.charWidth(' ');
  144. //TODO hack, we use the dash character's width for the space width, because dont know how to get it
  145. // int spaceIndex = p5Font.index('-');
  146. // int spaceAdvancex = p5Font.width[spaceIndex];
  147. // int spaceAdvancex = p5Font.getGlyph('-').width;
  148. int spaceAdvancex = Math.round((p5Font.width('i') * (float) fontSize));
  149. // int spaceAdvancex = Math.round(pa.textWidth(' '));
  150. // int spaceAdvancex = Math.round(p5Font.width(' ') * p5Font.size);
  151. BitmapFontCharacter space = new BitmapFontCharacter(pa, dummy, " ", 0, 0, spaceAdvancex);
  152. space.setPickable(false);
  153. space.setVisible(false);
  154. space.setNoFill(true);
  155. space.setNoStroke(true);
  156. space.setName("space");
  157. bitMapCharacters.add(space);
  158. //Manually add a TAB character to the font
  159. int defaultTabWidth = spaceAdvancex*4;
  160. BitmapFontCharacter tab = new BitmapFontCharacter(pa, dummy, "\t", 0, 0, defaultTabWidth);
  161. try {
  162. int tabWidth = 4 * space.getHorizontalDist();
  163. tab.setHorizontalDist(tabWidth);
  164. } catch (Exception e) {
  165. tab.setHorizontalDist(defaultTabWidth);
  166. }
  167. tab.setPickable(false);
  168. tab.setName("tab");
  169. tab.setVisible(false);
  170. tab.setNoFill(true);
  171. tab.setNoStroke(true);
  172. bitMapCharacters.add(tab);
  173. // */
  174. //TODO bitmap font size seems different to same size vector font, we must have check descent -> textarea -> res*em*etc
  175. //TODO eureka font - numbers baseline wrong?
  176. //Create the bitmap font
  177. BitmapFontCharacter[] characters = bitMapCharacters.toArray(new BitmapFontCharacter[bitMapCharacters.size()]);
  178. BitmapFont bitmapFont = new BitmapFont(characters, defaultHorizontalAdvX, fontFamily, fontMaxAscent, fontMaxDescent, unitsPerEm, originalFontSize,
  179. fillColor,
  180. // strokeColor,
  181. antiAliased
  182. );
  183. bitmapFont.setFontFileName(fontFileName);
  184. return bitmapFont;
  185. }
  186. // /**
  187. // * Gets the processing font.
  188. // *
  189. // * @param pa the pa
  190. // * @param fontFileName the font file name
  191. // * @param fontSize the font size
  192. // * @return the processing font
  193. // */
  194. // private PFont getProcessingFont(PApplet pa, String fontFileName, int fontSize){
  195. // return this.getProcessingFont(pa, fontFileName, fontSize, true);
  196. // }
  197. /**
  198. * Gets the processing font.
  199. *
  200. * @param pa the pa
  201. * @param fontFileName the font file name
  202. * @param fontSize the font size
  203. * @param antiAliased the anti aliased
  204. * @return the processing font
  205. * @throws FileNotFoundException
  206. */
  207. private PFont getProcessingFont(PApplet pa, String fontFileName, int fontSize, boolean antiAliased) throws FileNotFoundException{
  208. PFont p5Font;
  209. //When loading the vlw font the font size and anti aliasing is already determined with the file
  210. //and our parameter isnt honored
  211. if (fontFileName.endsWith(".vlw")){
  212. p5Font = pa.loadFont(fontFileName);
  213. //If not found try to load from the "/data" directory
  214. if (p5Font == null){
  215. int lastDirFileSeparator = fontFileName.lastIndexOf(java.io.File.separator);
  216. int lastDirSeparator = fontFileName.lastIndexOf(MTApplication.separator);
  217. if (lastDirFileSeparator != -1){
  218. p5Font = pa.loadFont(fontFileName.substring(lastDirFileSeparator+1, fontFileName.length()));
  219. }else if (lastDirSeparator != -1){
  220. p5Font = pa.loadFont(fontFileName.substring(lastDirSeparator+1, fontFileName.length()));
  221. }
  222. }
  223. }
  224. else if (fontFileName.endsWith(".ttf") || fontFileName.endsWith(".otf")){
  225. p5Font = pa.createFont(fontFileName, fontSize, antiAliased);
  226. //If not found try to load from the "/data" directory
  227. if (p5Font == null){
  228. int lastDirFileSeparator = fontFileName.lastIndexOf(java.io.File.separator);
  229. int lastDirSeparator = fontFileName.lastIndexOf(MTApplication.separator);
  230. if (lastDirFileSeparator != -1){
  231. p5Font = pa.createFont(fontFileName.substring(lastDirFileSeparator+1, fontFileName.length()), fontSize, antiAliased);
  232. }else if (lastDirSeparator != -1){
  233. p5Font = pa.createFont(fontFileName.substring(lastDirSeparator+1, fontFileName.length()), fontSize, antiAliased);
  234. }else{
  235. p5Font = pa.loadFont(fontFileName);
  236. }
  237. }
  238. }
  239. else{
  240. //No file suffix -> Create font from a java/system font
  241. int lastDirFileSeparator = fontFileName.lastIndexOf(java.io.File.separator);
  242. int lastDirSeparator = fontFileName.lastIndexOf(MTApplication.separator);
  243. if (lastDirFileSeparator != -1){
  244. p5Font = pa.createFont(fontFileName.substring(lastDirFileSeparator+1, fontFileName.length()), fontSize, antiAliased); //Creats the font
  245. }
  246. else if (lastDirSeparator != -1){
  247. p5Font = pa.createFont(fontFileName.substring(lastDirSeparator+1, fontFileName.length()), fontSize, antiAliased); //Creats the font
  248. }
  249. else{
  250. p5Font = pa.loadFont(fontFileName);
  251. }
  252. }
  253. if (p5Font == null){
  254. throw new FileNotFoundException("Couldn't load the font: " + fontFileName);
  255. }
  256. return p5Font;
  257. }
  258. public List<BitmapFontCharacter> getCharacters(PApplet pa,
  259. String chars,
  260. MTColor fillColor,
  261. // MTColor strokeColor,
  262. String fontFileName,
  263. int fontSize
  264. ){
  265. return this.getCharacters(pa, chars, fillColor, /*strokeColor,*/ fontFileName, fontSize, true);
  266. }
  267. /**
  268. * Creates the specified characters.
  269. *
  270. * @param pa the pa
  271. * @param chars the chars
  272. * @param fillColor the fill color
  273. * @param strokeColor the stroke color
  274. * @param fontFileName the font file name
  275. * @param fontSize the font size
  276. * @param antiAliased the anti aliased
  277. * @return the characters
  278. */
  279. public List<BitmapFontCharacter> getCharacters(PApplet pa,
  280. String chars,
  281. MTColor fillColor,
  282. // MTColor strokeColor,
  283. String fontFileName,
  284. int fontSize,
  285. boolean antiAliased
  286. ){
  287. PFont p5Font = null;
  288. try {
  289. p5Font = this.getProcessingFont(pa, fontFileName, fontSize, antiAliased);
  290. } catch (FileNotFoundException e) {
  291. e.printStackTrace();
  292. }
  293. return createCharacters(pa, p5Font, chars, fillColor /*, strokeColor*/);
  294. }
  295. private List<BitmapFontCharacter> createCharacters(PApplet pa, PFont p5Font, String chars, MTColor fillColor /*, MTColor strokeColor*/){
  296. List<BitmapFontCharacter> bitMapCharacters = new ArrayList<BitmapFontCharacter>();
  297. for (int i = 0; i < chars.length(); i++) {
  298. char c = chars.charAt(i);
  299. // int charIndex = p5Font.index(c);
  300. Glyph glyph = p5Font.getGlyph(c);
  301. if (glyph != null){
  302. PImage charImage = glyph.image;
  303. int charWidth = glyph.width;
  304. int charHeight = glyph.height;
  305. int topExtend = glyph.topExtent;
  306. int leftExtend = glyph.leftExtent;
  307. int widthDisplacement = glyph.setWidth;
  308. //int topOffset = p5Font.descent + (-charHeight - (topExtend-charHeight)); //ORIGINAL
  309. int topOffset = (-charHeight - (topExtend-charHeight));
  310. //Copy the actual font data on the image from the upper left corner 1 pixel
  311. //into the middle of the image to avoid anti aliasing artefacts at the corners
  312. // PImage copy = new PImage(charImage.width, charImage.height, PImage.ARGB); //ORG
  313. // for (int j = 0; j < charImage.pixels.length; j++) { //ORG
  314. // int d = charImage.pixels[j];
  315. // /*
  316. // int a = d >> 24 & 0xFF;
  317. // int r = d >> 16 & 0xFF;
  318. // int g = d >> 8 & 0xFF;
  319. // int b = d & 0xFF;
  320. // logger.debug("R: " + r + " G:" + g + " B:" + " A:" + a);
  321. // */
  322. // charImage.pixels[j] = (d << 24) | 0x00FFFFFF; //ORIGINAL! //make it white
  323. //// charImage.pixels[j] = (d << 24) | pa.color(fillColor.getR(), fillColor.getG(), fillColor.getB(), 0);
  324. //// charImage.pixels[j] = (charImage.pixels[j] << 24) | 0x00FFFFFF;
  325. // //charImage.format = PConstants.ARGB;
  326. //
  327. // //Clear the copy image in the same loop
  328. // copy.pixels[j] = (copy.pixels[j] << 24) | 0x00FFFFFF; //Original! //make it white
  329. //// copy.pixels[j] = (d << 24) | 0x00FFFFFF; //Original! //make it white
  330. // }
  331. for (int j = 0; j < charImage.pixels.length; j++) { //ORG
  332. charImage.pixels[j] = (charImage.pixels[j] << 24) | 0x00FFFFFF; //ORIGINAL! //make it white
  333. }
  334. //Shift character image data down and right in the image because of aliasing artifacts at the border
  335. //we need to compensate for this when displaying the char
  336. //FIXME this creates far to big images..but because of artefacts needed..?
  337. int topShiftAmount = 4;
  338. int leftShiftAmount = 4;
  339. // PImage copy = new PImage(ToolsMath.nearestPowerOfTwo(charWidth + shiftAmount), ToolsMath.nearestPowerOfTwo(charHeight + shiftAmount), PImage.ARGB);
  340. //
  341. PImage copy = new PImage(nextPowerOfTwo(charImage.width + leftShiftAmount + 1), nextPowerOfTwo(charImage.height + topShiftAmount +1), PImage.ARGB);
  342. // PImage copy = new PImage(charImage.width + leftShiftAmount + 1, charImage.height + topShiftAmount, PImage.ARGB);
  343. copy.copy(charImage, 0, 0, charWidth, charHeight, leftShiftAmount, topShiftAmount, charWidth, charHeight);
  344. // copy.copy(charImage, 0, 0, charImage.width, charImage.height, leftShiftAmount, topShiftAmount, charImage.width, charImage.height);
  345. // copy.copy(charImage, 0, 0, charWidth, charHeight, shiftAmount, shiftAmount, charWidth, charHeight);
  346. // copy.copy(charImage, 0, 0, charImage.width, charImage.height, shiftAmount, shiftAmount, charImage.width, charImage.height);
  347. // copy.copy(charImage, 0, 0, charImage.width, charImage.height, shiftAmount, shiftAmount, charImage.width, charImage.height);
  348. // copy.copy(charImage, 0, 0, charWidth, charHeight, shiftAmount, shiftAmount, charWidth, charHeight);
  349. charImage = copy;
  350. //FIXME the topoffset is smaller than with the vector font! check that!
  351. //FIXME anti aliasing artefacts may also stem from using a perspective and not ortho camera!!
  352. //FIXME space character too wide..
  353. //Move the character to compensate for the shifting of the image
  354. topOffset -= topShiftAmount; //org shiftamount
  355. leftExtend -= leftShiftAmount;
  356. //FIXME TEST
  357. // if (c == 'i'){
  358. // copy.save(MT4jSettings.DEFAULT_IMAGES_PATH + "i.png");
  359. // }
  360. //Create bitmap font character
  361. String StringChar = Character.toString(c);
  362. BitmapFontCharacter character = new BitmapFontCharacter(pa, charImage, StringChar, leftExtend, topOffset, widthDisplacement);
  363. character.setName(StringChar);
  364. character.setFillColor(new MTColor(fillColor));
  365. if (MT4jSettings.getInstance().isOpenGlMode()){
  366. character.generateAndUseDisplayLists();
  367. }
  368. bitMapCharacters.add(character);
  369. //logger.debug("Char: " + c + " charWidth: " + charWidth + " leftExtend: " + leftExtend + " widthDisplacement: " + widthDisplacement + " imageHeight: " + charImage.height + " charHeight: " + charHeight + " topExtent: " + topExtend);
  370. }else{
  371. logger.warn("Couldnt create bitmap character : " + c + " -> not found!");
  372. }
  373. }
  374. return bitMapCharacters;
  375. }
  376. private int nextPowerOfTwo(int val) {
  377. int ret = 1;
  378. while (ret < val) {
  379. ret <<= 1;
  380. }
  381. return ret;
  382. }
  383. //
  384. // /**
  385. // * Create a .vlw font on the fly from either a font name that's
  386. // * installed on the system, or from a .ttf or .otf that's inside
  387. // * the data folder of this sketch.
  388. // * <P/>
  389. // * Many .otf fonts don't seem to be supported by Java, perhaps because
  390. // * they're CFF based?
  391. // * <P/>
  392. // * Font names are inconsistent across platforms and Java versions.
  393. // * On Mac OS X, Java 1.3 uses the font menu name of the font,
  394. // * whereas Java 1.4 uses the PostScript name of the font. Java 1.4
  395. // * on OS X will also accept the font menu name as well. On Windows,
  396. // * it appears that only the menu names are used, no matter what
  397. // * Java version is in use. Naming system unknown/untested for 1.5.
  398. // * <P/>
  399. // * Use 'null' for the charset if you want to dynamically create
  400. // * character bitmaps only as they're needed. (Version 1.0.9 and
  401. // * earlier would interpret null as all unicode characters.)
  402. // */
  403. // public PFont createFont(PApplet app, String name, float size,
  404. // boolean smooth, char charset[]) {
  405. // String lowerName = name.toLowerCase();
  406. // Font baseFont = null;
  407. //
  408. // try {
  409. // InputStream stream = null;
  410. // if (lowerName.endsWith(".otf") || lowerName.endsWith(".ttf")) {
  411. // stream = app.createInput(name);
  412. // if (stream == null) {
  413. // System.err.println("The font \"" + name + "\" " +
  414. // "is missing or inaccessible, make sure " +
  415. // "the URL is valid or that the file has been " +
  416. // "added to your sketch and is readable.");
  417. // return null;
  418. // }
  419. // baseFont = Font.createFont(Font.TRUETYPE_FONT, app.createInput(name));
  420. //
  421. // } else {
  422. // baseFont = PFont.findFont(name);
  423. // }
  424. // return new PFont(baseFont.deriveFont(size), smooth, charset,
  425. // stream != null);
  426. //
  427. // } catch (Exception e) {
  428. // System.err.println("Problem createFont(" + name + ")");
  429. // e.printStackTrace();
  430. // return null;
  431. // }
  432. // }
  433. //
  434. // private class MYPFont extends PFont{
  435. //
  436. // public void getGlyphImage(){
  437. // getGlyph('a');
  438. // }
  439. //
  440. // public class bla extends PFont.Glyph{
  441. //
  442. // }
  443. //
  444. // }
  445. }