1 // Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
8 /** encapsulates a single XWF font */
11 /** all instances of XWF */
12 private static Hash xwtfonts = new Hash();
14 /** the int array comprising the XWF */
17 /** the width of the XWF's PNG */
20 /** the height of the XWF's PNG */
23 /** the full resource name of this font */
26 /** minimum descent of all glyphs */
29 /** maximum descent of all glyphs */
32 /** hash containing all Picture instances created, keyed on an Integer object containing the argb color of the font */
33 Hash pictures = new Hash();
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
43 int[][] metrics = null;
45 /** drop all cached fonts when the theme mapping changes */
46 public static void flushXWFs() { xwtfonts.clear(); }
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;
53 String resolved = Resources.resolve(resourcename + ".xwf", null);
54 byte[] bytes = Resources.getResource(resolved);
56 PNG png = PNG.decode(new ByteArrayInputStream(bytes), resourcename);
57 if (png != null) return new XWF(resourcename, png);
62 public int getMaxAscent() { return maxascent; }
63 public int getMaxDescent() { return maxdescent; }
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) {
68 Integer color = new Integer(argb | 0xFF000000);
69 Picture pg = (Picture)pictures.get(color);
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);
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;
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]);
92 left += metrics[c][4];
96 /** returns the width of <tt>text</tt> when rendered in this font */
97 public int stringWidth(String text) {
99 for(int i=0; i<text.length(); i++) {
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];
108 private XWF(String name, PNG png) {
110 xwtfonts.put(name, this);
112 data = png.getData();
119 int start_x, start_y, end_x, end_y, advance;
121 int[] rows = new int[120];
125 metrics = new int[96][];
130 // search for the next non-empty row
131 for(breakout = false; y<h && !breakout; y++)
133 if ((data[x + y * w] & 0x00FFFFFF) != 0x00FFFFFF) breakout = true;
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;
144 if (start_y == end_y) continue;
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) {
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) {
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) {
178 advance = x - start_x;
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) {
191 if (start_x == end_x) break;
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;
202 if (numglyphs >= metrics.length) break;
206 if (numglyphs >= metrics.length) break;
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)
214 data[i] = (0xFF - (data[i] & 0xFF)) << 24;
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]);
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]);