2002/09/03 18:09:03
[org.ibex.core.git] / src / org / xwt / XWF.java
1 // Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
2 package org.xwt;
3
4 import java.io.*;
5 import java.util.*;
6 import org.xwt.util.*;
7
8 /** encapsulates a single XWF font */
9 public class XWF {
10
11     /** all instances of XWF */
12     private static Hash xwtfonts = new Hash();
13
14     /** the int array comprising the XWF */
15     int[] data = null;
16
17     /** the width of the XWF's PNG */
18     int w = 0;
19
20     /** the height of the XWF's PNG */
21     int h = 0;
22
23     /** the full resource name of this font */
24     String name = null;
25
26     /** minimum descent of all glyphs */
27     int maxascent = 0;
28
29     /** maximum descent of all glyphs */
30     int maxdescent = 0;
31
32     /** hash containing all Picture instances created, keyed on an Integer object containing the argb color of the font */
33     Hash pictures = new Hash();
34
35     /** each element corresponds to a single glyph; <br>
36      *  metrics[glyphnum][0] == x position of left edge of glyph<br>
37      *  metrics[glyphnum][1] == x position of right edge of glyph<br>
38      *  metrics[glyphnum][2] == y position of top edge of glyph<br>
39      *  metrics[glyphnum][3] == y position of bottom edge of glyph<br>
40      *  metrics[glyphnum][4] == advance amount<br>
41      *  metrics[glyphnum][5] == baseline
42      */
43     int[][] metrics = null;
44
45     /** drop all cached fonts when the theme mapping changes */
46     public static void flushXWFs() { xwtfonts.clear(); }
47
48     /** retrieve an XWF instance, creating it if needed */
49     public static XWF getXWF(String resourcename) {
50         XWF ret = (XWF)xwtfonts.get(resourcename);
51         if (ret != null) return ret;
52
53         String resolved = Resources.resolve(resourcename + ".xwf", null);
54         byte[] bytes = Resources.getResource(resolved);
55         if (bytes != null) {
56             PNG png = PNG.decode(new ByteArrayInputStream(bytes), resourcename);
57             if (png != null) return new XWF(resourcename, png);
58         }
59         return null;
60     }
61
62     public int getMaxAscent() { return maxascent; }
63     public int getMaxDescent() { return maxdescent; }
64
65     /** draws <tt>text</tt> on <tt>buf</tt> in this font, with color <tt>argb</tt> */
66     public void drawString(DoubleBuffer buf, String text, int x, int y, int argb) {
67
68         Integer color = new Integer(argb | 0xFF000000);
69         Picture pg = (Picture)pictures.get(color);
70         if (pg == null) {
71             int[] data2 = new int[data.length];
72             for(int i=0; i<data.length; i++)
73                 data2[i] = (data[i] & 0xFF000000) | (argb & 0x00FFFFFF);
74             pg = Platform.createPicture(data2, w, h);
75             pictures.put(color, pg);
76         }
77
78         int left = x;
79         for(int i=0; i<text.length(); i++) {
80             int c = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~".indexOf(text.charAt(i));
81             if (c == -1 || metrics[c] == null) {
82                 left += metrics[64] != null ? metrics[64][4] : 10;
83                 continue;
84             }
85             buf.drawPicture(pg,
86                             left,
87                             y - (metrics[c][5] - metrics[c][2]),
88                             left + metrics[c][1] - metrics[c][0],
89                             y - (metrics[c][5] - metrics[c][2]) + metrics[c][3] - metrics[c][2],
90                             metrics[c][0], metrics[c][2], metrics[c][1], metrics[c][3]);
91             
92             left += metrics[c][4];
93         }
94     }
95
96     /** returns the width of <tt>text</tt> when rendered in this font */
97     public int stringWidth(String text) {
98         int ret = 0;
99         for(int i=0; i<text.length(); i++) {
100             // what a hack...
101             int c = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~".indexOf(text.charAt(i));
102             if (c == -1 || metrics[c] == null) { ret += metrics[64][4]; continue; }
103             ret += metrics[c][4];
104         }
105         return ret;
106     }
107
108     private XWF(String name, PNG png) {
109         this.name = name;
110         xwtfonts.put(name, this);
111
112         data = png.getData();
113         w = png.getWidth();
114         h = png.getHeight();
115
116         int x, y, y1;
117         boolean breakout;
118
119         int start_x, start_y, end_x, end_y, advance;
120
121         int[] rows = new int[120];
122
123         int baseline = 0;
124
125         metrics = new int[96][];
126         int numglyphs = 0;
127
128         for(y=0; y<h; y++) {
129
130             // search for the next non-empty row
131             for(breakout = false; y<h && !breakout; y++)
132                 for(x = 0; x<w; x++)
133                     if ((data[x + y * w] & 0x00FFFFFF) != 0x00FFFFFF) breakout = true;
134
135             start_y = y - 2;
136
137             // search for the next empty row
138             for(breakout = false; y<h && !breakout; y++)
139                 for(x = 0, breakout = true; x<w; x++)
140                     if ((data[x + y * w] & 0x00FFFFFF) != 0x00FFFFFF) breakout = false;
141
142             end_y = y;
143
144             if (start_y == end_y) continue;
145
146             for(x=0; x<w; x++) {
147
148                 // search for the next column with a non-grayscale pixel in it
149                 for(breakout = false; x<w && !breakout; x++)
150                     for(y1 = start_y; y1<end_y; y1++)
151                         if (Math.abs(((data[x + y1 * w] & 0x00FF0000) >> 16) - ((data[x + y1 * w] & 0x0000FF00) >> 8)) > 10 ||
152                             Math.abs(((data[x + y1 * w] & 0x0000FF00) >> 8) - ((data[x + y1 * w] & 0x000000FF))) > 10) {
153                             breakout = true;
154                             baseline = y1;
155                         }
156                 
157                 // search for the next column without a non-grayscale pixel in it
158                 for(breakout = false; x<w && !breakout; x++)
159                     for(y1 = start_y, breakout = true; y1<end_y; y1++)
160                         if (Math.abs(((data[x + y1 * w] & 0x00FF0000) >> 16) - ((data[x + y1 * w] & 0x0000FF00) >> 8)) > 10 ||
161                             Math.abs(((data[x + y1 * w] & 0x0000FF00) >> 8) - ((data[x + y1 * w] & 0x000000FF))) > 10) {
162                             breakout = false;
163                         }
164
165                 x--;
166                 start_x = x;
167
168                 // search for the next column with a non-grayscale pixel in it
169                 for(breakout = false; x<w && !breakout; x++)
170                     for(y1 = start_y; y1<end_y; y1++)
171                         if (Math.abs(((data[x + y1 * w] & 0x00FF0000) >> 16) - ((data[x + y1 * w] & 0x0000FF00) >> 8)) > 10 ||
172                             Math.abs(((data[x + y1 * w] & 0x0000FF00) >> 8) - ((data[x + y1 * w] & 0x000000FF))) > 10) {
173                             breakout = true;
174                             baseline = y1;
175                         }
176                 
177                 x--;
178                 advance = x - start_x;
179
180                 // search for the next column without a grayscale pixel in it
181                 for(breakout = false; x<w && !breakout; x++)
182                     for(y1 = start_y, breakout = true; y1<end_y; y1++)
183                         if (Math.abs(((data[x + y1 * w] & 0x00FF0000) >> 16) - ((data[x + y1 * w] & 0x0000FF00) >> 8)) > 10 ||
184                             Math.abs(((data[x + y1 * w] & 0x0000FF00) >> 8) - ((data[x + y1 * w] & 0x000000FF))) > 10) {
185                             breakout = false;
186                         }
187
188                 x--;
189                 end_x = x;
190
191                 if (start_x == end_x) break;
192
193                 metrics[numglyphs] = new int[6];
194                 metrics[numglyphs][0] = start_x;
195                 metrics[numglyphs][1] = end_x;
196                 metrics[numglyphs][2] = start_y;
197                 metrics[numglyphs][3] = end_y;
198                 metrics[numglyphs][4] = advance;
199                 metrics[numglyphs][5] = baseline;
200                 numglyphs++;
201
202                 if (numglyphs >= metrics.length) break;
203
204             }
205
206             if (numglyphs >= metrics.length) break;
207         }
208
209         for(int i=0; i<data.length; i++)
210             if (Math.abs(((data[i] & 0x00FF0000) >> 16) - ((data[i] & 0x0000FF00) >> 8)) > 10 ||
211                 Math.abs(((data[i] & 0x0000FF00) >> 8) - ((data[i] & 0x000000FF))) > 10)
212                 data[i] = 0x00;
213             else
214                 data[i] = (0xFF - (data[i] & 0xFF)) << 24;
215
216         for(int i=33; i<=126; i++)
217             if (metrics[i - 33] != null)
218                 maxascent = Math.max(maxascent, metrics[i - 33][5] - metrics[i - 33][2]);
219
220         for(int i=33; i<=126; i++)
221             if (metrics[i - 33] != null)
222                 maxdescent = Math.max(maxdescent, metrics[i - 33][3] - metrics[i - 33][5]);
223        
224     }
225
226 }