2002/03/21 01:19:33
[org.ibex.core.git] / src / org / xwt / XWF.java
diff --git a/src/org/xwt/XWF.java b/src/org/xwt/XWF.java
new file mode 100644 (file)
index 0000000..7b6f8e2
--- /dev/null
@@ -0,0 +1,222 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+import java.io.*;
+import java.util.*;
+import org.xwt.util.*;
+
+/** encapsulates a single XWF font */
+public class XWF {
+
+    /** all instances of XWF */
+    private static Hash xwtfonts = new Hash();
+
+    /** the int array comprising the XWF */
+    int[] data = null;
+
+    /** the width of the XWF's PNG */
+    int w = 0;
+
+    /** the height of the XWF's PNG */
+    int h = 0;
+
+    /** the full resource name of this font */
+    String name = null;
+
+    /** minimum descent of all glyphs */
+    int maxascent = 0;
+
+    /** maximum descent of all glyphs */
+    int maxdescent = 0;
+
+    /** hash containing all Picture instances created, keyed on an Integer object containing the argb color of the font */
+    Hash pictures = new Hash();
+
+    /** each element corresponds to a single glyph; <br>
+     *  metrics[glyphnum][0] == x position of left edge of glyph<br>
+     *  metrics[glyphnum][1] == x position of right edge of glyph<br>
+     *  metrics[glyphnum][2] == y position of top edge of glyph<br>
+     *  metrics[glyphnum][3] == y position of bottom edge of glyph<br>
+     *  metrics[glyphnum][4] == advance amount<br>
+     *  metrics[glyphnum][5] == baseline
+     */
+    int[][] metrics = null;
+
+    /** drop all cached fonts when the theme mapping changes */
+    public static void flushXWFs() { xwtfonts.clear(); }
+
+    /** retrieve an XWF instance, creating it if needed */
+    public static XWF getXWF(String resourcename) {
+        XWF ret = (XWF)xwtfonts.get(resourcename);
+        if (ret != null) return ret;
+
+        String resolved = Resources.resolve(resourcename + ".xwf", null);
+        byte[] bytes = Resources.getResource(resolved);
+        if (bytes != null) {
+            PNG png = PNG.decode(new ByteArrayInputStream(bytes), resourcename);
+            if (png != null) return new XWF(resourcename, png);
+        }
+        return null;
+    }
+
+    public int getMaxAscent() { return maxascent; }
+    public int getMaxDescent() { return maxdescent; }
+
+    /** draws <tt>text</tt> on <tt>buf</tt> in this font, with color <tt>argb</tt> */
+    public void drawString(DoubleBuffer buf, String text, int x, int y, int argb) {
+
+        Integer color = new Integer(argb | 0xFF000000);
+        Picture pg = (Picture)pictures.get(color);
+        if (pg == null) {
+            for(int i=0; i<data.length; i++)
+                data[i] = (data[i] & 0xFF000000) | (argb & 0x00FFFFFF);
+            pg = Platform.createPicture(data, w, h);
+            pictures.put(color, pg);
+        }
+
+        int left = x;
+        for(int i=0; i<text.length(); i++) {
+            int c = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~".indexOf(text.charAt(i));
+            if (c == -1 || metrics[c] == null) { left += metrics[64][4]; continue; }
+            buf.drawPicture(pg,
+                            left,
+                            y - (metrics[c][5] - metrics[c][2]),
+                            left + metrics[c][1] - metrics[c][0],
+                            y - (metrics[c][5] - metrics[c][2]) + metrics[c][3] - metrics[c][2],
+                            metrics[c][0], metrics[c][2], metrics[c][1], metrics[c][3]);
+            
+            left += metrics[c][4];
+        }
+    }
+
+    /** returns the width of <tt>text</tt> when rendered in this font */
+    public int stringWidth(String text) {
+        int ret = 0;
+        for(int i=0; i<text.length(); i++) {
+            // what a hack...
+            int c = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~".indexOf(text.charAt(i));
+            if (c == -1 || metrics[c] == null) { ret += metrics[64][4]; continue; }
+            ret += metrics[c][4];
+        }
+        return ret;
+    }
+
+    private XWF(String name, PNG png) {
+        this.name = name;
+        xwtfonts.put(name, this);
+
+        data = png.getData();
+        w = png.getWidth();
+        h = png.getHeight();
+
+        int x, y, y1;
+        boolean breakout;
+
+        int start_x, start_y, end_x, end_y, advance;
+
+        int[] rows = new int[120];
+
+        int baseline = 0;
+
+        metrics = new int[96][];
+        int numglyphs = 0;
+
+        for(y=0; y<h; y++) {
+
+            // search for the next non-empty row
+            for(breakout = false; y<h && !breakout; y++)
+                for(x = 0; x<w; x++)
+                    if ((data[x + y * w] & 0x00FFFFFF) != 0x00FFFFFF) breakout = true;
+
+            start_y = y - 2;
+
+            // search for the next empty row
+            for(breakout = false; y<h && !breakout; y++)
+                for(x = 0, breakout = true; x<w; x++)
+                    if ((data[x + y * w] & 0x00FFFFFF) != 0x00FFFFFF) breakout = false;
+
+            end_y = y;
+
+            if (start_y == end_y) continue;
+
+            for(x=0; x<w; x++) {
+
+                // search for the next column with a non-grayscale pixel in it
+                for(breakout = false; x<w && !breakout; x++)
+                    for(y1 = start_y; y1<end_y; y1++)
+                        if (Math.abs(((data[x + y1 * w] & 0x00FF0000) >> 16) - ((data[x + y1 * w] & 0x0000FF00) >> 8)) > 10 ||
+                            Math.abs(((data[x + y1 * w] & 0x0000FF00) >> 8) - ((data[x + y1 * w] & 0x000000FF))) > 10) {
+                            breakout = true;
+                            baseline = y1;
+                        }
+                
+                // search for the next column without a non-grayscale pixel in it
+                for(breakout = false; x<w && !breakout; x++)
+                    for(y1 = start_y, breakout = true; y1<end_y; y1++)
+                        if (Math.abs(((data[x + y1 * w] & 0x00FF0000) >> 16) - ((data[x + y1 * w] & 0x0000FF00) >> 8)) > 10 ||
+                            Math.abs(((data[x + y1 * w] & 0x0000FF00) >> 8) - ((data[x + y1 * w] & 0x000000FF))) > 10) {
+                            breakout = false;
+                        }
+
+                x--;
+                start_x = x;
+
+                // search for the next column with a non-grayscale pixel in it
+                for(breakout = false; x<w && !breakout; x++)
+                    for(y1 = start_y; y1<end_y; y1++)
+                        if (Math.abs(((data[x + y1 * w] & 0x00FF0000) >> 16) - ((data[x + y1 * w] & 0x0000FF00) >> 8)) > 10 ||
+                            Math.abs(((data[x + y1 * w] & 0x0000FF00) >> 8) - ((data[x + y1 * w] & 0x000000FF))) > 10) {
+                            breakout = true;
+                            baseline = y1;
+                        }
+                
+                x--;
+                advance = x - start_x;
+
+                // search for the next column without a grayscale pixel in it
+                for(breakout = false; x<w && !breakout; x++)
+                    for(y1 = start_y, breakout = true; y1<end_y; y1++)
+                        if (Math.abs(((data[x + y1 * w] & 0x00FF0000) >> 16) - ((data[x + y1 * w] & 0x0000FF00) >> 8)) > 10 ||
+                            Math.abs(((data[x + y1 * w] & 0x0000FF00) >> 8) - ((data[x + y1 * w] & 0x000000FF))) > 10) {
+                            breakout = false;
+                        }
+
+                x--;
+                end_x = x;
+
+                if (start_x == end_x) break;
+
+                metrics[numglyphs] = new int[6];
+                metrics[numglyphs][0] = start_x;
+                metrics[numglyphs][1] = end_x;
+                metrics[numglyphs][2] = start_y;
+                metrics[numglyphs][3] = end_y;
+                metrics[numglyphs][4] = advance;
+                metrics[numglyphs][5] = baseline;
+                numglyphs++;
+
+                if (numglyphs >= metrics.length) break;
+
+            }
+
+            if (numglyphs >= metrics.length) break;
+        }
+
+        for(int i=0; i<data.length; i++)
+            if (Math.abs(((data[i] & 0x00FF0000) >> 16) - ((data[i] & 0x0000FF00) >> 8)) > 10 ||
+                Math.abs(((data[i] & 0x0000FF00) >> 8) - ((data[i] & 0x000000FF))) > 10)
+                data[i] = 0x00;
+            else
+                data[i] = (0xFF - (data[i] & 0xFF)) << 24;
+
+        for(int i=33; i<=126; i++)
+            if (metrics[i - 33] != null)
+                maxascent = Math.max(maxascent, metrics[i - 33][5] - metrics[i - 33][2]);
+
+        for(int i=33; i<=126; i++)
+            if (metrics[i - 33] != null)
+                maxdescent = Math.max(maxdescent, metrics[i - 33][3] - metrics[i - 33][5]);
+       
+    }
+
+}