From c6ce49b0c25b0eeacd121e55567841f57e87de9a Mon Sep 17 00:00:00 2001 From: benfry Date: Fri, 19 Feb 2010 21:55:02 +0000 Subject: [PATCH] re-sync with PFont from the main code --- android/core/src/processing/core/PFont.java | 892 +++++++++++--------- 1 file changed, 484 insertions(+), 408 deletions(-) diff --git a/android/core/src/processing/core/PFont.java b/android/core/src/processing/core/PFont.java index 34a4972b2..0b883b507 100644 --- a/android/core/src/processing/core/PFont.java +++ b/android/core/src/processing/core/PFont.java @@ -3,13 +3,12 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2004-07 Ben Fry & Casey Reas + Copyright (c) 2004-10 Ben Fry & Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + modify it under the terms of version 2.01 of the GNU Lesser General + Public License as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -24,26 +23,16 @@ package processing.core; -//import java.awt.*; -//import java.awt.image.BufferedImage; -//import java.awt.image.Raster; +import java.awt.*; +import java.awt.image.*; import java.io.*; -//import java.lang.reflect.*; import java.util.Arrays; -import java.util.HashMap; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Typeface; -import android.graphics.Bitmap.Config; /** * Grayscale bitmap font class used by Processing. *

- * Awful (and by that, I mean awesome) ascii (non)art for how this works: + * Awful (and by that, I mean awesome) ASCII (non-)art for how this works: *

  *   |
  *   |                   height is the full used height of the image
@@ -64,80 +53,201 @@ import android.graphics.Bitmap.Config;
  */
 public class PFont implements PConstants {
 
-  public int charCount;
-  public PImage images[];
+  /** Number of character glyphs in this font. */
+  protected int glyphCount;
+  
+  /** 
+   * Actual glyph data. The length of this array won't necessarily be the 
+   * same size as glyphCount, in cases where lazy font loading is in use.
+   */
+  protected Glyph[] glyphs;
+
+  /**
+   * Name of the font as seen by Java when it was created.
+   * If the font is available, the native version will be used.
+   */
+  protected String name;
+
+  /** 
+   * Postscript name of the font that this bitmap was created from.
+   */
+  protected String psname;
+
+  /** 
+   * The original size of the font when it was first created 
+   */
+  protected int size;
+
+  /** true if smoothing was enabled for this font, used for native impl */
+  protected boolean smooth;
+
+  /** 
+   * The ascent of the font. If the 'd' character is present in this PFont, 
+   * this value is replaced with its pixel height, because the values returned
+   * by FontMetrics.getAscent() seem to be terrible. 
+   */
+  protected int ascent;
+  
+  /** 
+   * The descent of the font. If the 'p' character is present in this PFont, 
+   * this value is replaced with its lowest pixel height, because the values 
+   * returned by FontMetrics.getDescent() are gross. 
+   */
+  protected int descent;
+
+  /**
+   * A more efficient array lookup for straight ASCII characters. For Unicode
+   * characters, a QuickSort-style search is used. 
+   */
+  protected int[] ascii;
+
+  /**
+   * True if this font is set to load dynamically. This is the default when 
+   * createFont() method is called without a character set. Bitmap versions of 
+   * characters are only created when prompted by an index() call.
+   */
+  protected boolean lazy;
 
   /**
    * Native Java version of the font. If possible, this allows the
    * PGraphics subclass to just use Java's font rendering stuff
    * in situations where that's faster.
    */
-  protected Typeface font;
-  //protected Font font;
+  protected Font font;
+
+  /** 
+   * True if we've already tried to find the native AWT version of this font.
+   */
   protected boolean fontSearched;
 
   /**
-   * Name of the font as seen by Java when it was created.
-   * If the font is available, the native version will be used.
+   * Array of the native system fonts. Used to lookup native fonts by their 
+   * PostScript name. This is a workaround for a several year old Apple Java
+   * bug that they can't be bothered to fix. 
    */
-  public String name;
+  static protected Font[] fonts;
 
-  /**
-   * Postscript name of the font that this bitmap was created from.
-   */
-  public String psname;
 
-  /** "natural" size of the font (most often 48) */
-  public int size;
-
-  /** true if smoothing was enabled for this font, used for native impl */
-  public boolean smooth;
-
-  /** next power of 2 over the max image size (usually 64) */
-  public int mbox2;
-
-  /** floating point width (convenience) */
-  protected float fwidth;
-
-  /** floating point width (convenience) */
-  protected float fheight;
-
-  /** texture width, same as mbox2, but reserved for future use */
-  public int twidth;
-
-  /** texture height, same as mbox2, but reserved for future use */
-  public int theight;
-
-  public int value[];  // char code
-  public int height[]; // height of the bitmap data
-  public int width[];  // width of bitmap data
-  public int setWidth[];  // width displaced by the char
-  public int topExtent[];  // offset for the top
-  public int leftExtent[];  // offset for the left
-
-  public int ascent;
-  public int descent;
-
-  protected int ascii[];  // quick lookup for the ascii chars
-
-  // shared by the text() functions to avoid incessant allocation of memory
-  //protected char textBuffer[] = new char[8 * 1024];
-  //protected char widthBuffer[] = new char[8 * 1024];
-  
-  static protected Typeface[] fonts;
+  // objects to handle creation of font characters only as they're needed
+  BufferedImage lazyImage;
+  Graphics2D lazyGraphics;
+  FontMetrics lazyMetrics;
+  int[] lazySamples;  
 
 
   public PFont() { }  // for subclasses
 
 
+  /**
+   * Create a new Processing font from a native font, but don't create all the
+   * characters at once, instead wait until they're used to include them.
+   * @param font
+   * @param smooth
+   */
+  public PFont(Font font, boolean smooth) {
+    this(font, smooth, null);
+  }
+  
+  
+  /**
+   * Create a new image-based font on the fly. If charset is set to null, 
+   * the characters will only be created as bitmaps when they're drawn.
+   *
+   * @param font the font object to create from
+   * @param charset array of all unicode chars that should be included
+   * @param smooth true to enable smoothing/anti-aliasing
+   */
+  public PFont(Font font, boolean smooth, char charset[]) {
+    // save this so that we can use the native version
+    this.font = font;
+    this.smooth = smooth;
+
+    name = font.getName();
+    psname = font.getPSName();
+    size = font.getSize();
+
+    // no, i'm not interested in getting off the couch
+    lazy = true;
+    // not sure what else to do here
+    //mbox2 = 0; 
+
+    int initialCount = 10;
+    glyphs = new Glyph[initialCount];
+
+    ascii = new int[128];
+    Arrays.fill(ascii, -1);
+
+    int mbox3 = size * 3;
+
+    lazyImage = new BufferedImage(mbox3, mbox3, BufferedImage.TYPE_INT_RGB);
+    lazyGraphics = (Graphics2D) lazyImage.getGraphics();
+    lazyGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                                  smooth ?
+                                  RenderingHints.VALUE_ANTIALIAS_ON :
+                                  RenderingHints.VALUE_ANTIALIAS_OFF);
+    // adding this for post-1.0.9
+    lazyGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
+                                  smooth ?
+                                  RenderingHints.VALUE_TEXT_ANTIALIAS_ON :
+                                  RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
+
+    lazyGraphics.setFont(font);
+    lazyMetrics = lazyGraphics.getFontMetrics();
+    lazySamples = new int[mbox3 * mbox3];
+
+    ascent = lazyMetrics.getAscent();
+    descent = lazyMetrics.getDescent();
+
+    if (charset != null) {
+      // charset needs to be sorted to make index lookup run more quickly
+      // http://dev.processing.org/bugs/show_bug.cgi?id=494
+      Arrays.sort(charset);
+
+      glyphs = new Glyph[charset.length];
+
+      glyphCount = 0;
+      for (char c : charset) {
+        if (font.canDisplay(c)) {
+          glyphs[glyphCount++] = new Glyph(c);
+        }
+      }
+
+      // shorten the array if necessary
+      if (glyphCount != charset.length) {
+        glyphs = (Glyph[]) PApplet.subset(glyphs, 0, glyphCount);
+      }
+
+      // foreign font, so just make ascent the max topExtent
+      // for > 1.0.9, not doing this anymore. 
+      // instead using getAscent() and getDescent() values for these cases.
+//      if ((ascent == 0) && (descent == 0)) {
+//        //for (int i = 0; i < charCount; i++) {
+//        for (Glyph glyph : glyphs) {
+//          char cc = (char) glyph.value;
+//          //char cc = (char) glyphs[i].value;
+//          if (Character.isWhitespace(cc) ||
+//              (cc == '\u00A0') || (cc == '\u2007') || (cc == '\u202F')) {
+//            continue;
+//          }
+//          if (glyph.topExtent > ascent) {
+//            ascent = glyph.topExtent;
+//          }
+//          int d = -glyph.topExtent + glyph.height;
+//          if (d > descent) {
+//            descent = d;
+//          }
+//        }
+//      }
+    }
+  }
+
+
   public PFont(InputStream input) throws IOException {
     DataInputStream is = new DataInputStream(input);
 
     // number of character images stored in this font
-    charCount = is.readInt();
+    glyphCount = is.readInt();
 
-    // bit count is ignored since this is always 8
-    //int numBits = is.readInt();
     // used to be the bitCount, but now used for version number.
     // version 8 is any font before 69, so 9 is anything from 83+
     // 9 was buggy so gonna increment to 10.
@@ -146,62 +256,28 @@ public class PFont implements PConstants {
     // this was formerly ignored, now it's the actual font size
     //mbox = is.readInt();
     size = is.readInt();
+
     // this was formerly mboxY, the one that was used
     // this will make new fonts downward compatible
-    //mbox2 = is.readInt();
-    mbox2 = is.readInt();
-
-    fwidth = size; //mbox;
-    fheight = size; //mbox;
-
-    // size for image ("texture") is next power of 2
-    // over the font size. for most vlw fonts, the size is 48
-    // so the next power of 2 is 64.
-    // double-check to make sure that mbox2 is a power of 2
-    // there was a bug in the old font generator that broke this
-    //mbox2 = (int) Math.pow(2, Math.ceil(Math.log(mbox2) / Math.log(2)));
-    mbox2 = (int) Math.pow(2, Math.ceil(Math.log(mbox2) / Math.log(2)));
-    // size for the texture is stored in the font
-    twidth = theight = mbox2; //mbox2;
+    is.readInt();  // ignore the other mbox attribute
 
     ascent  = is.readInt();  // formerly baseHt (zero/ignored)
     descent = is.readInt();  // formerly ignored struct padding
 
     // allocate enough space for the character info
-    value       = new int[charCount];
-    height      = new int[charCount];
-    width       = new int[charCount];
-    setWidth    = new int[charCount];
-    topExtent   = new int[charCount];
-    leftExtent  = new int[charCount];
+    glyphs = new Glyph[glyphCount];
 
     ascii = new int[128];
-    for (int i = 0; i < 128; i++) ascii[i] = -1;
+    Arrays.fill(ascii, -1);
 
     // read the information about the individual characters
-    for (int i = 0; i < charCount; i++) {
-      value[i]      = is.readInt();
-      height[i]     = is.readInt();
-      width[i]      = is.readInt();
-      setWidth[i]   = is.readInt();
-      topExtent[i]  = is.readInt();
-      leftExtent[i] = is.readInt();
-
-      // pointer in the c version, ignored
-      is.readInt();
-
+    for (int i = 0; i < glyphCount; i++) {
+      Glyph glyph = new Glyph(is);
       // cache locations of the ascii charset
-      if (value[i] < 128) ascii[value[i]] = i;
-
-      // the values for getAscent() and getDescent() from FontMetrics
-      // seem to be way too large.. perhaps they're the max?
-      // as such, use a more traditional marker for ascent/descent
-      if (value[i] == 'd') {
-        if (ascent == 0) ascent = topExtent[i];
-      }
-      if (value[i] == 'p') {
-        if (descent == 0) descent = -topExtent[i] + height[i];
+      if (glyph.value < 128) {
+        ascii[glyph.value] = i;
       }
+      glyphs[i] = glyph;
     }
 
     // not a roman font, so throw an error and ask to re-build.
@@ -211,30 +287,8 @@ public class PFont implements PConstants {
                                  "re-create this font.");
     }
 
-    images = new PImage[charCount];
-    for (int i = 0; i < charCount; i++) {
-//      images[i] = new PImage(twidth, theight, ALPHA);
-      images[i] = new PImage(twidth, theight, ARGB);
-      int bitmapSize = height[i] * width[i];
-
-      byte temp[] = new byte[bitmapSize];
-      is.readFully(temp);
-
-      // convert the bitmap to an alpha channel
-      int w = width[i];
-      int h = height[i];
-      for (int y = 0; y < h; y++) {
-        for (int x = 0; x < w; x++) {
-          int valu = temp[y*w + x] & 0xff;
-          //images[i].pixels[y * twidth + x] = valu;
-          images[i].pixels[y * twidth + x] = (valu << 24) | 0xFFFFFF;
-//          images[i].pixels[y * twidth + x] = (valu << 24);
-
-          //System.out.print((images[i].pixels[y*64+x] > 128) ? "*" : ".");
-        }
-        //System.out.println();
-      }
-      //System.out.println();
+    for (Glyph glyph : glyphs) {
+      glyph.readBitmap(is);
     }
 
     if (version >= 10) {  // includes the font name at the end of the file
@@ -247,10 +301,104 @@ public class PFont implements PConstants {
   }
 
 
+  /**
+   * Write this PFont to an OutputStream.
+   * 

+ * This is used by the Create Font tool, or whatever anyone else dreams + * up for messing with fonts themselves. + *

+ * It is assumed that the calling class will handle closing + * the stream when finished. + */ + public void save(OutputStream output) throws IOException { + DataOutputStream os = new DataOutputStream(output); + + os.writeInt(glyphCount); + + if ((name == null) || (psname == null)) { + name = ""; + psname = ""; + } + + os.writeInt(11); // formerly numBits, now used for version number + os.writeInt(size); // formerly mboxX (was 64, now 48) + os.writeInt(0); // formerly mboxY, now ignored + os.writeInt(ascent); // formerly baseHt (was ignored) + os.writeInt(descent); // formerly struct padding for c version + + for (int i = 0; i < glyphCount; i++) { + glyphs[i].writeHeader(os); + } + + for (int i = 0; i < glyphCount; i++) { + glyphs[i].writeBitmap(os); + } + + // version 11 + os.writeUTF(name); + os.writeUTF(psname); + os.writeBoolean(smooth); + + os.flush(); + } + + + /** + * Create a new glyph, and add the character to the current font. + * @param c character to create an image for. + */ + protected void addGlyph(char c) { + Glyph glyph = new Glyph(c); + + if (glyphCount == glyphs.length) { + glyphs = (Glyph[]) PApplet.expand(glyphs); + } + if (glyphCount == 0) { + glyphs[glyphCount] = glyph; + if (glyph.value < 128) { + ascii[glyph.value] = 0; + } + + } else if (glyphs[glyphCount-1].value < glyph.value) { + glyphs[glyphCount] = glyph; + if (glyph.value < 128) { + ascii[glyph.value] = glyphCount; + } + + } else { + for (int i = 0; i < glyphCount; i++) { + if (glyphs[i].value > c) { + for (int j = glyphCount; j > i; --j) { + glyphs[j] = glyphs[j-1]; + if (glyphs[j].value < 128) { + ascii[glyphs[j].value] = j; + } + } + glyphs[i] = glyph; + // cache locations of the ascii charset + if (c < 128) ascii[c] = i; + break; + } + } + } + glyphCount++; + } + + + public String getName() { + return name; + } + + + public String getPostScriptName() { + return psname; + } + + /** * Set the native complement of this font. */ - public void setFont(Typeface font) { + public void setFont(Font font) { this.font = font; } @@ -258,7 +406,7 @@ public class PFont implements PConstants { /** * Return the native java.awt.Font associated with this PFont (if any). */ - public Typeface getFont() { + public Font getFont() { // if (font == null && !fontSearched) { // font = findFont(); // } @@ -270,10 +418,8 @@ public class PFont implements PConstants { * Attempt to find the native version of this font. * (Public so that it can be used by OpenGL or other renderers.) */ - public Typeface findFont() { + public Font findFont() { if (font == null) { - throw new RuntimeException("findFont() not available with Android."); - /* if (!fontSearched) { // this font may or may not be installed font = new Font(name, Font.PLAIN, size); @@ -291,83 +437,55 @@ public class PFont implements PConstants { } fontSearched = true; } - */ } return font; } - /** - * Write this PFont to an OutputStream. - *

- * This is used by the Create Font tool, or whatever anyone else dreams - * up for messing with fonts themselves. - *

- * It is assumed that the calling class will handle closing - * the stream when finished. - */ - public void save(OutputStream output) throws IOException { - DataOutputStream os = new DataOutputStream(output); - - os.writeInt(charCount); - - if ((name == null) || (psname == null)) { - name = ""; - psname = ""; - } - // formerly numBits, now used for version number - //os.writeInt((name != null) ? 11 : 8); - os.writeInt(11); - - os.writeInt(size); // formerly mboxX (was 64, now 48) - os.writeInt(mbox2); // formerly mboxY (was 64, still 64) - os.writeInt(ascent); // formerly baseHt (was ignored) - os.writeInt(descent); // formerly struct padding for c version - - for (int i = 0; i < charCount; i++) { - os.writeInt(value[i]); - os.writeInt(height[i]); - os.writeInt(width[i]); - os.writeInt(setWidth[i]); - os.writeInt(topExtent[i]); - os.writeInt(leftExtent[i]); - os.writeInt(0); // padding - } - - for (int i = 0; i < charCount; i++) { - for (int y = 0; y < height[i]; y++) { - for (int x = 0; x < width[i]; x++) { - os.write(images[i].pixels[y * mbox2 + x] & 0xff); - } - } - } - - //if (name != null) { // version 11 - os.writeUTF(name); - os.writeUTF(psname); - os.writeBoolean(smooth); - //} - - os.flush(); + public Glyph getGlyph(char c) { + int index = index(c); + return (index == -1) ? null : glyphs[index]; } /** - * Get index for the char (convert from unicode to bagel charset). + * Get index for the character. * @return index into arrays or -1 if not found */ - public int index(char c) { + protected int index(char c) { + if (lazy) { + int index = indexActual(c); + if (index != -1) { + return index; + } + if (font.canDisplay(c)) { + // create the glyph + addGlyph(c); + // now where did i put that? + return indexActual(c); + + } else { + return -1; + } + + } else { + return indexActual(c); + } + } + + + protected int indexActual(char c) { // degenerate case, but the find function will have trouble // if there are somehow zero chars in the lookup //if (value.length == 0) return -1; - if (charCount == 0) return -1; + if (glyphCount == 0) return -1; // quicker lookup for the ascii fellers if (c < 128) return ascii[c]; // some other unicode char, hunt it out //return index_hunt(c, 0, value.length-1); - return indexHunt(c, 0, charCount-1); + return indexHunt(c, 0, glyphCount-1); } @@ -375,14 +493,14 @@ public class PFont implements PConstants { int pivot = (start + stop) / 2; // if this is the char, then return it - if (c == value[pivot]) return pivot; + if (c == glyphs[pivot].value) return pivot; // char doesn't exist, otherwise would have been the pivot //if (start == stop) return -1; if (start >= stop) return -1; // if it's in the lower half, continue searching that - if (c < value[pivot]) return indexHunt(c, start, pivot-1); + if (c < glyphs[pivot].value) return indexHunt(c, start, pivot-1); // if it's in the upper half, continue there return indexHunt(c, pivot+1, stop); @@ -403,7 +521,7 @@ public class PFont implements PConstants { * The value is based on a font of size 1. */ public float ascent() { - return ((float)ascent / fheight); + return ((float) ascent / (float) size); } @@ -412,7 +530,7 @@ public class PFont implements PConstants { * The value is based on a font size of 1. */ public float descent() { - return ((float)descent / fheight); + return ((float) descent / (float) size); } @@ -425,7 +543,7 @@ public class PFont implements PConstants { int cc = index(c); if (cc == -1) return 0; - return ((float)setWidth[cc] / fwidth); + return ((float) glyphs[cc].setWidth / (float) size); } @@ -478,103 +596,171 @@ public class PFont implements PConstants { *

* Not that I expect that to happen. */ - static public char[] DEFAULT_CHARSET; + static public char[] CHARSET; static { - DEFAULT_CHARSET = new char[126-33+1 + EXTRA_CHARS.length]; + CHARSET = new char[126-33+1 + EXTRA_CHARS.length]; int index = 0; for (int i = 33; i <= 126; i++) { - DEFAULT_CHARSET[index++] = (char)i; + CHARSET[index++] = (char)i; } for (int i = 0; i < EXTRA_CHARS.length; i++) { - DEFAULT_CHARSET[index++] = EXTRA_CHARS[i]; + CHARSET[index++] = EXTRA_CHARS[i]; } }; /** - * Create a new image-based font on the fly. - * - * @param font the font object to create from - * @param charset array of all unicode chars that should be included - * @param smooth true to enable smoothing/anti-aliasing + * Get a list of the fonts installed on the system that can be used + * by Java. Not all fonts can be used in Java, in fact it's mostly + * only TrueType fonts. OpenType fonts with CFF data such as Adobe's + * OpenType fonts seem to have trouble (even though they're sort of + * TrueType fonts as well, or may have a .ttf extension). Regular + * PostScript fonts seem to work OK, however. + *

+ * Not recommended for use in applets, but this is implemented + * in PFont because the Java methods to access this information + * have changed between 1.1 and 1.4, and the 1.4 method is + * typical of the sort of undergraduate-level over-abstraction + * that the seems to have made its way into the Java API after 1.1. */ - public PFont(Typeface font, int size, boolean smooth, char charset[]) { - // save this so that we can use the native version - this.font = font; - this.smooth = smooth; + static public String[] list() { + loadFonts(); + String list[] = new String[fonts.length]; + for (int i = 0; i < list.length; i++) { + list[i] = fonts[i].getName(); + } + return list; + } + -// name = font.getName(); -// psname = font.getPSName(); + static public void loadFonts() { + if (fonts == null) { + GraphicsEnvironment ge = + GraphicsEnvironment.getLocalGraphicsEnvironment(); + fonts = ge.getAllFonts(); + } + } - // fix regression from sorting (bug #564) - if (charset != null) { - // charset needs to be sorted to make index lookup run more quickly - // http://dev.processing.org/bugs/show_bug.cgi?id=494 - Arrays.sort(charset); + + /** + * Starting with Java 1.5, Apple broke the ability to specify most fonts. + * This has been filed as bug #4769141 at bugreporter.apple.com. More info at + * Bug 407. + */ + static public Font findFont(String name) { + loadFonts(); + if (PApplet.platform == PConstants.MACOSX) { + for (int i = 0; i < fonts.length; i++) { + if (name.equals(fonts[i].getName())) { + return fonts[i]; + } + } + } + return new Font(name, Font.PLAIN, 1); + } + + + ////////////////////////////////////////////////////////////// + + + /** + * A single character, and its visage. + */ + public class Glyph { + PImage image; + int value; + int height; + int width; + int setWidth; + int topExtent; + int leftExtent; + + + protected Glyph() { + // used when reading from a stream or for subclasses + } + + + protected Glyph(DataInputStream is) throws IOException { + readHeader(is); + } + + + protected void readHeader(DataInputStream is) throws IOException { + value = is.readInt(); + height = is.readInt(); + width = is.readInt(); + setWidth = is.readInt(); + topExtent = is.readInt(); + leftExtent = is.readInt(); + + // pointer from a struct in the c version, ignored + is.readInt(); + + // the values for getAscent() and getDescent() from FontMetrics + // seem to be way too large.. perhaps they're the max? + // as such, use a more traditional marker for ascent/descent + if (value == 'd') { + if (ascent == 0) ascent = topExtent; + } + if (value == 'p') { + if (descent == 0) descent = -topExtent + height; + } } - // the count gets reset later based on how many of - // the chars are actually found inside the font. - this.charCount = (charset == null) ? 65536 : charset.length; - this.size = size; - - fwidth = fheight = size; - - PImage bitmaps[] = new PImage[charCount]; - - // allocate enough space for the character info - value = new int[charCount]; - height = new int[charCount]; - width = new int[charCount]; - setWidth = new int[charCount]; - topExtent = new int[charCount]; - leftExtent = new int[charCount]; - - ascii = new int[128]; - for (int i = 0; i < 128; i++) ascii[i] = -1; - - int mbox3 = size * 3; - -// BufferedImage playground = -// new BufferedImage(mbox3, mbox3, BufferedImage.TYPE_INT_RGB); - Bitmap playground = Bitmap.createBitmap(mbox3, mbox3, Config.ARGB_8888); -// Graphics2D g = (Graphics2D) playground.getGraphics(); - Canvas canvas = new Canvas(playground); -// g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, -// smooth ? -// RenderingHints.VALUE_ANTIALIAS_ON : -// RenderingHints.VALUE_ANTIALIAS_OFF); - Paint paint = new Paint(); - paint.setAntiAlias(smooth); - -// g.setFont(font); -// FontMetrics metrics = g.getFontMetrics(); - paint.setTypeface(font); - paint.setTextSize(size); - int samples[] = new int[mbox3 * mbox3]; + protected void writeHeader(DataOutputStream os) throws IOException { + os.writeInt(value); + os.writeInt(height); + os.writeInt(width); + os.writeInt(setWidth); + os.writeInt(topExtent); + os.writeInt(leftExtent); + os.writeInt(0); // padding + } + + + protected void readBitmap(DataInputStream is) throws IOException { + image = new PImage(width, height, ALPHA); + int bitmapSize = width * height; - int maxWidthHeight = 0; - int index = 0; - for (int i = 0; i < charCount; i++) { - char c = (charset == null) ? (char)i : charset[i]; + byte[] temp = new byte[bitmapSize]; + is.readFully(temp); -// if (!font.canDisplay(c)) { // skip chars not in the font -// continue; -// } + // convert the bitmap to an alpha channel + int w = width; + int h = height; + int[] pixels = image.pixels; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + pixels[y * width + x] = temp[y*w + x] & 0xff; +// System.out.print((image.pixels[y*64+x] > 128) ? "*" : "."); + } +// System.out.println(); + } +// System.out.println(); + } + + + protected void writeBitmap(DataOutputStream os) throws IOException { + int[] pixels = image.pixels; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + os.write(pixels[y * width + x] & 0xff); + } + } + } -// g.setColor(Color.white); -// g.fillRect(0, 0, mbox3, mbox3); - canvas.drawColor(Color.WHITE); -// g.setColor(Color.black); - paint.setColor(Color.BLACK); -// g.drawString(String.valueOf(c), size, size * 2); - canvas.drawText(String.valueOf(c), size, size * 2, paint); - // grabs copy of the current data.. so no updates (do each time) -// Raster raster = playground.getData(); -// raster.getSamples(0, 0, mbox3, mbox3, 0, samples); - playground.getPixels(samples, 0, mbox3, 0, 0, mbox3, mbox3); + protected Glyph(char c) { + int mbox3 = size * 3; + lazyGraphics.setColor(Color.white); + lazyGraphics.fillRect(0, 0, mbox3, mbox3); + lazyGraphics.setColor(Color.black); + lazyGraphics.drawString(String.valueOf(c), size, size * 2); + + WritableRaster raster = lazyImage.getRaster(); + raster.getDataElements(0, 0, mbox3, mbox3, lazySamples); int minX = 1000, maxX = 0; int minY = 1000, maxY = 0; @@ -582,11 +768,7 @@ public class PFont implements PConstants { for (int y = 0; y < mbox3; y++) { for (int x = 0; x < mbox3; x++) { - //int sample = raster.getSample(x, y, 0); // maybe? - int sample = samples[y * mbox3 + x] & 0xff; - // or int samples[] = raster.getPixel(x, y, null); - - //if (sample == 0) { // or just not white? hmm + int sample = lazySamples[y * mbox3 + x] & 0xff; if (sample != 255) { if (x < minX) minX = x; if (y < minY) minY = y; @@ -604,141 +786,35 @@ public class PFont implements PConstants { // maybe better to set one to -1 so nothing is added? } - value[index] = c; - height[index] = (maxY - minY) + 1; - width[index] = (maxX - minX) + 1; -// setWidth[index] = metrics.charWidth(c); - setWidth[index] = (int) paint.measureText(String.valueOf(c)); - //System.out.println((char)c + " " + setWidth[index]); - - // cache locations of the ascii charset - //if (value[i] < 128) ascii[value[i]] = i; - if (c < 128) ascii[c] = index; + value = c; + height = (maxY - minY) + 1; + width = (maxX - minX) + 1; + setWidth = lazyMetrics.charWidth(c); // offset from vertical location of baseline // of where the char was drawn (size*2) - topExtent[index] = size*2 - minY; + topExtent = size*2 - minY; // offset from left of where coord was drawn - leftExtent[index] = minX - size; - - if (c == 'd') { - ascent = topExtent[index]; - } - if (c == 'p') { - descent = -topExtent[index] + height[index]; - } - - if (width[index] > maxWidthHeight) maxWidthHeight = width[index]; - if (height[index] > maxWidthHeight) maxWidthHeight = height[index]; - - bitmaps[index] = new PImage(width[index], height[index], ALPHA); + leftExtent = minX - size; + image = new PImage(width, height, ALPHA); + int[] pixels = image.pixels; for (int y = minY; y <= maxY; y++) { for (int x = minX; x <= maxX; x++) { - int val = 255 - (samples[y * mbox3 + x] & 0xff); - int pindex = (y - minY) * width[index] + (x - minX); - bitmaps[index].pixels[pindex] = val; + int val = 255 - (lazySamples[y * mbox3 + x] & 0xff); + int pindex = (y - minY) * width + (x - minX); + pixels[pindex] = val; } } - index++; - } - charCount = index; - // foreign font, so just make ascent the max topExtent - if ((ascent == 0) && (descent == 0)) { - for (int i = 0; i < charCount; i++) { - char cc = (char) value[i]; - if (Character.isWhitespace(cc) || - (cc == '\u00A0') || (cc == '\u2007') || (cc == '\u202F')) { - continue; - } - if (topExtent[i] > ascent) { - ascent = topExtent[i]; - } - int d = -topExtent[i] + height[i]; - if (d > descent) { - descent = d; - } + // replace the ascent/descent values with something.. err, decent. + if (value == 'd') { + if (ascent == 0) ascent = topExtent; + } + if (value == 'p') { + if (descent == 0) descent = -topExtent + height; } } - // size for image/texture is next power of 2 over largest char - mbox2 = (int) - Math.pow(2, Math.ceil(Math.log(maxWidthHeight) / Math.log(2))); - twidth = theight = mbox2; - - // shove the smaller PImage data into textures of next-power-of-2 size, - // so that this font can be used immediately by p5. - images = new PImage[charCount]; - for (int i = 0; i < charCount; i++) { - images[i] = new PImage(mbox2, mbox2, ALPHA); - for (int y = 0; y < height[i]; y++) { - System.arraycopy(bitmaps[i].pixels, y*width[i], - images[i].pixels, y*mbox2, - width[i]); - } - bitmaps[i] = null; - } - } - - - static HashMap fontMap; - static String[] fontList; - - - /** - * Get a list of the built-in fonts. - */ - static public String[] list() { - loadFonts(); - return fontList; - } - - - static public void loadFonts() { - if (fontMap == null) { - fontMap = new HashMap(); - - fontMap.put("Serif", - Typeface.create(Typeface.SERIF, Typeface.NORMAL)); - fontMap.put("Serif-Bold", - Typeface.create(Typeface.SERIF, Typeface.BOLD)); - fontMap.put("Serif-Italic", - Typeface.create(Typeface.SERIF, Typeface.ITALIC)); - fontMap.put("Serif-BoldItalic", - Typeface.create(Typeface.SERIF, Typeface.BOLD_ITALIC)); - - fontMap.put("SansSerif", - Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL)); - fontMap.put("SansSerif-Bold", - Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD)); - fontMap.put("SansSerif-Italic", - Typeface.create(Typeface.SANS_SERIF, Typeface.ITALIC)); - fontMap.put("SansSerif-BoldItalic", - Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD_ITALIC)); - - fontMap.put("Monospaced", - Typeface.create(Typeface.MONOSPACE, Typeface.NORMAL)); - fontMap.put("Monospaced-Bold", - Typeface.create(Typeface.MONOSPACE, Typeface.BOLD)); - fontMap.put("Monospaced-Italic", - Typeface.create(Typeface.MONOSPACE, Typeface.ITALIC)); - fontMap.put("Monospaced-BoldItalic", - Typeface.create(Typeface.MONOSPACE, Typeface.BOLD_ITALIC)); - - fontList = new String[fontMap.size()]; - fontMap.keySet().toArray(fontList); - } - } - - - /** - * Starting with Java 1.5, Apple broke the ability to specify most fonts. - * This has been filed as bug #4769141 at bugreporter.apple.com. More info at - * Bug 407. - */ - static public Typeface findFont(String name) { - loadFonts(); - return fontMap.get(name); } }