3c02da8b062b00966d2766e6ee1214f5b6c314e4
[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) { left += metrics[64][4]; continue; }
82             buf.drawPicture(pg,
83                             left,
84                             y - (metrics[c][5] - metrics[c][2]),
85                             left + metrics[c][1] - metrics[c][0],
86                             y - (metrics[c][5] - metrics[c][2]) + metrics[c][3] - metrics[c][2],
87                             metrics[c][0], metrics[c][2], metrics[c][1], metrics[c][3]);
88             
89             left += metrics[c][4];
90         }
91     }
92
93     /** returns the width of <tt>text</tt> when rendered in this font */
94     public int stringWidth(String text) {
95         int ret = 0;
96         for(int i=0; i<text.length(); i++) {
97             // what a hack...
98             int c = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~".indexOf(text.charAt(i));
99             if (c == -1 || metrics[c] == null) { ret += metrics[64][4]; continue; }
100             ret += metrics[c][4];
101         }
102         return ret;
103     }
104
105     private XWF(String name, PNG png) {
106         this.name = name;
107         xwtfonts.put(name, this);
108
109         data = png.getData();
110         w = png.getWidth();
111         h = png.getHeight();
112
113         int x, y, y1;
114         boolean breakout;
115
116         int start_x, start_y, end_x, end_y, advance;
117
118         int[] rows = new int[120];
119
120         int baseline = 0;
121
122         metrics = new int[96][];
123         int numglyphs = 0;
124
125         for(y=0; y<h; y++) {
126
127             // search for the next non-empty row
128             for(breakout = false; y<h && !breakout; y++)
129                 for(x = 0; x<w; x++)
130                     if ((data[x + y * w] & 0x00FFFFFF) != 0x00FFFFFF) breakout = true;
131
132             start_y = y - 2;
133
134             // search for the next empty row
135             for(breakout = false; y<h && !breakout; y++)
136                 for(x = 0, breakout = true; x<w; x++)
137                     if ((data[x + y * w] & 0x00FFFFFF) != 0x00FFFFFF) breakout = false;
138
139             end_y = y;
140
141             if (start_y == end_y) continue;
142
143             for(x=0; x<w; x++) {
144
145                 // search for the next column with a non-grayscale pixel in it
146                 for(breakout = false; x<w && !breakout; x++)
147                     for(y1 = start_y; y1<end_y; y1++)
148                         if (Math.abs(((data[x + y1 * w] & 0x00FF0000) >> 16) - ((data[x + y1 * w] & 0x0000FF00) >> 8)) > 10 ||
149                             Math.abs(((data[x + y1 * w] & 0x0000FF00) >> 8) - ((data[x + y1 * w] & 0x000000FF))) > 10) {
150                             breakout = true;
151                             baseline = y1;
152                         }
153                 
154                 // search for the next column without a non-grayscale pixel in it
155                 for(breakout = false; x<w && !breakout; x++)
156                     for(y1 = start_y, breakout = true; y1<end_y; y1++)
157                         if (Math.abs(((data[x + y1 * w] & 0x00FF0000) >> 16) - ((data[x + y1 * w] & 0x0000FF00) >> 8)) > 10 ||
158                             Math.abs(((data[x + y1 * w] & 0x0000FF00) >> 8) - ((data[x + y1 * w] & 0x000000FF))) > 10) {
159                             breakout = false;
160                         }
161
162                 x--;
163                 start_x = x;
164
165                 // search for the next column with a non-grayscale pixel in it
166                 for(breakout = false; x<w && !breakout; x++)
167                     for(y1 = start_y; y1<end_y; y1++)
168                         if (Math.abs(((data[x + y1 * w] & 0x00FF0000) >> 16) - ((data[x + y1 * w] & 0x0000FF00) >> 8)) > 10 ||
169                             Math.abs(((data[x + y1 * w] & 0x0000FF00) >> 8) - ((data[x + y1 * w] & 0x000000FF))) > 10) {
170                             breakout = true;
171                             baseline = y1;
172                         }
173                 
174                 x--;
175                 advance = x - start_x;
176
177                 // search for the next column without a grayscale pixel in it
178                 for(breakout = false; x<w && !breakout; x++)
179                     for(y1 = start_y, breakout = true; y1<end_y; y1++)
180                         if (Math.abs(((data[x + y1 * w] & 0x00FF0000) >> 16) - ((data[x + y1 * w] & 0x0000FF00) >> 8)) > 10 ||
181                             Math.abs(((data[x + y1 * w] & 0x0000FF00) >> 8) - ((data[x + y1 * w] & 0x000000FF))) > 10) {
182                             breakout = false;
183                         }
184
185                 x--;
186                 end_x = x;
187
188                 if (start_x == end_x) break;
189
190                 metrics[numglyphs] = new int[6];
191                 metrics[numglyphs][0] = start_x;
192                 metrics[numglyphs][1] = end_x;
193                 metrics[numglyphs][2] = start_y;
194                 metrics[numglyphs][3] = end_y;
195                 metrics[numglyphs][4] = advance;
196                 metrics[numglyphs][5] = baseline;
197                 numglyphs++;
198
199                 if (numglyphs >= metrics.length) break;
200
201             }
202
203             if (numglyphs >= metrics.length) break;
204         }
205
206         for(int i=0; i<data.length; i++)
207             if (Math.abs(((data[i] & 0x00FF0000) >> 16) - ((data[i] & 0x0000FF00) >> 8)) > 10 ||
208                 Math.abs(((data[i] & 0x0000FF00) >> 8) - ((data[i] & 0x000000FF))) > 10)
209                 data[i] = 0x00;
210             else
211                 data[i] = (0xFF - (data[i] & 0xFF)) << 24;
212
213         for(int i=33; i<=126; i++)
214             if (metrics[i - 33] != null)
215                 maxascent = Math.max(maxascent, metrics[i - 33][5] - metrics[i - 33][2]);
216
217         for(int i=33; i<=126; i++)
218             if (metrics[i - 33] != null)
219                 maxdescent = Math.max(maxdescent, metrics[i - 33][3] - metrics[i - 33][5]);
220        
221     }
222
223 }