001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Ilya S. Okomin
019: * @version $Revision$
020: */package org.apache.harmony.awt.gl.font;
021:
022: import java.awt.font.GlyphMetrics;
023:
024: import java.awt.Rectangle;
025: import java.awt.Shape;
026:
027: import java.awt.geom.GeneralPath;
028: import java.awt.geom.Rectangle2D;
029: import java.awt.image.BufferedImage;
030: import java.awt.image.DataBufferByte;
031: import java.awt.image.IndexColorModel;
032: import java.awt.image.WritableRaster;
033: import java.awt.image.Raster;
034: import java.lang.Math;
035:
036: import org.apache.harmony.awt.gl.Utils;
037: import org.apache.harmony.awt.gl.font.Glyph;
038: import org.apache.harmony.awt.gl.font.NativeFont;
039: import org.apache.harmony.awt.nativebridge.windows.Win32;
040: import org.apache.harmony.awt.nativebridge.windows.WindowsDefs;
041:
042: /**
043: *
044: * Windows implementation of the Glyph class
045: */
046: public class WinGlyph extends Glyph {
047:
048: // Win32 instance
049: private static final Win32 win32 = Win32.getInstance();
050:
051: // offset to the POINTFX array
052: private static final long TTPOLYCURVE_HEADER_OFFSET = 4;
053:
054: // offset to the POINTFX array
055: private static final long TTPOLYGONHEADER_POINTFX_OFFSET = 8;
056:
057: /**
058: * Constructor
059: */
060: public WinGlyph(long pFnt, int fntSize, char c, int glyphIndex) {
061: float metrics[];
062: int[] pxlMetrics;
063:
064: this .pFont = pFnt;
065: this .fontSize = fntSize;
066:
067: switch (c) {
068: case '\t':
069: case '\r':
070: case '\n':
071: metrics = new float[6];
072: pxlMetrics = new int[6];
073: break;
074: default:
075: metrics = NativeFont.getGlyphInfoNative(this .pFont, c,
076: fntSize);
077: pxlMetrics = NativeFont
078: .getGlyphPxlInfoNative(this .pFont, c);
079: break;
080:
081: }
082:
083: Rectangle2D.Float rect = new Rectangle2D.Float(metrics[0],
084: -metrics[1], metrics[4], metrics[5]);
085: this .glMetrics = new GlyphMetrics(
086: (float) Math.ceil(metrics[2]), rect, (byte) 0);
087: this .glCode = glyphIndex;
088: this .glChar = c;
089:
090: Rectangle rct = new Rectangle(pxlMetrics[0], -pxlMetrics[1],
091: pxlMetrics[4], pxlMetrics[5]);
092: this .glPointMetrics = new GlyphMetrics(pxlMetrics[2], rct,
093: (byte) 1);
094: }
095:
096: /**
097: * Default Glyph constructor
098: */
099: public WinGlyph(char c, int glyphIndex) {
100: float metrics[] = new float[6];
101: int[] pxlMetrics = new int[6];
102:
103: this .pFont = 0;
104: this .fontSize = 0;
105:
106: Rectangle2D.Float rect = new Rectangle2D.Float(metrics[0],
107: -metrics[1], metrics[4], metrics[5]);
108: this .glMetrics = new GlyphMetrics(
109: (float) Math.ceil(metrics[2]), rect, (byte) 0);
110:
111: this .glCode = glyphIndex;
112: this .glChar = c;
113:
114: Rectangle rct = new Rectangle(pxlMetrics[0], -pxlMetrics[1],
115: pxlMetrics[4], pxlMetrics[5]);
116: this .glPointMetrics = new GlyphMetrics(pxlMetrics[2], rct,
117: (byte) 1);
118: }
119:
120: @Override
121: public byte[] getBitmap() {
122: if (this .bitmap == null) {
123: bitmap = NativeFont.NativeInitGlyphImage(this );
124: if (bitmap != null) {
125: this .bmp_left = 0;
126: this .bmp_rows = this .getPointHeight();
127: this .bmp_top = -(int) getGlyphPointMetrics()
128: .getBounds2D().getY();
129: this .bmp_pitch = bitmap.length / this .bmp_rows;
130: this .bmp_width = this .getPointWidth();
131: }
132: }
133: return this .bitmap;
134: }
135:
136: @Override
137: public BufferedImage getImage() {
138: if ((this .getWidth() == 0) || (this .getHeight() == 0)) {
139: return null;
140: }
141:
142: byte[] pixels;
143: int alignedWidth;
144: int height;
145: if (this .image == null) {
146: pixels = NativeFont.NativeInitGlyphImage(this );
147:
148: DataBufferByte dataBuffer = new DataBufferByte(pixels,
149: pixels.length);
150: /* Work around:
151: *
152: * Because of inability to create IndexedColorModel with data, represented as DataBuffer.TYPE_INT
153: * Raster with additional width is created to cover all bits, which are extending meaningful bits
154: * to the DWORD-aligning. When we want to take an image of the glyph - we have to copy only rectangle
155: * that encloses the Glyph from the whole raster.
156: *
157: * */
158: height = (int) this .glPointMetrics.getBounds2D()
159: .getHeight();
160: alignedWidth = (pixels.length / height) << 3;
161:
162: WritableRaster wr = Raster.createPackedRaster(dataBuffer,
163: alignedWidth, height, 1, null);
164:
165: byte[] blackWhite = new byte[] { 0, (byte) 0xff };
166: IndexColorModel colorModel = new IndexColorModel(1, 2,
167: blackWhite, blackWhite, blackWhite);
168:
169: this .image = new BufferedImage(alignedWidth, height,
170: BufferedImage.TYPE_BYTE_BINARY);
171: this .image.setData(wr);
172: }
173:
174: return this .image;
175: }
176:
177: @Override
178: public Shape initOutline(char c) {
179: if ((this .getWidth() == 0) || (this .getHeight() == 0)) {
180: return new GeneralPath();
181: }
182:
183: GeneralPath gp = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
184:
185: long buffer = 0; // pointer for TTPolygonHeader structure
186: int bufSize = 0; // size of buffer
187:
188: /* getting size of buffer */
189: bufSize = NativeFont.getGlyphOutline(this .pFont, c, buffer,
190: bufSize);
191:
192: if (bufSize == 0) {
193: return gp;
194: }
195: buffer = Utils.memaccess.malloc(bufSize);
196:
197: /* getting filled TTPolygonHeader structure */
198: int size = NativeFont.getGlyphOutline(this .pFont, c, buffer,
199: bufSize);
200:
201: if (size == 0) {
202: Utils.memaccess.free(buffer);
203: return gp;
204: }
205: /* parsing TTPolygonHeader to the set of curves*/
206: Win32.TTPOLYGONHEADER polygon = win32
207: .createTTPOLYGONHEADER(buffer);
208: long ptr = polygon.lock();
209: polygon.unlock();
210: long offs = ptr;
211:
212: while (bufSize > Win32.TTPOLYGONHEADER.sizeof) {
213: int curvesize = polygon.get_cb();
214: bufSize -= curvesize;
215: if (bufSize < 0) {
216: /* Incorrect buffer structure */
217: Utils.memaccess.free(buffer);
218: return gp;
219: }
220:
221: if (polygon.get_dwType() != WindowsDefs.TT_POLYGON_TYPE) {
222: /* Polygon type isn't a TT_POLYGON_TYPE */
223: Utils.memaccess.free(buffer);
224: return gp;
225: }
226:
227: /* Set up starting point */
228: float coords[] = getPoints(offs
229: + TTPOLYGONHEADER_POINTFX_OFFSET, 1);
230: float x = coords[0];
231: float y = coords[1];
232: gp.moveTo(x, y);
233: // System.out.println("AddPoint [" + x + "," + y + "]");
234:
235: /* Obtaining first curve in a list of the polygon's curves */
236: long curveOffs = offs + curvesize;
237: offs += Win32.TTPOLYGONHEADER.sizeof;
238:
239: Win32.TTPOLYCURVE curve = win32.createTTPOLYCURVE(offs);
240:
241: int count;
242: while (offs < curveOffs) {
243:
244: offs += TTPOLYCURVE_HEADER_OFFSET;
245: count = curve.get_cpfx();
246: coords = getPoints(offs, count);
247: switch (curve.get_wType()) {
248: /* Current curve segment is Line */
249: case WindowsDefs.TT_PRIM_LINE:
250: for (int i = 0; i < count; i++) {
251: float x1 = coords[i * 2];
252: float y1 = coords[i * 2 + 1];
253: gp.lineTo(x1, y1);
254: // System.out.println("AddPoint [" + x1 + "," + y1 + "]");
255: }
256: break;
257: /* Current curve segment is a quadratic Bezier curve */
258: case WindowsDefs.TT_PRIM_QSPLINE:
259: for (int i = 0; i < count - 1; i++) {
260: float x1 = coords[i * 2];
261: float y1 = coords[i * 2 + 1];
262: float x2 = coords[(i + 1) * 2];
263: float y2 = coords[(i + 1) * 2 + 1];
264:
265: if (i == (curve.get_cpfx() - 2)) {
266: gp.quadTo(x1, y1, x2, y2);
267: // System.out.println("AddQSpline 1[" + x1 + "," + y1 + "][" + x2 + "," + y2 + "]");
268: } else {
269: gp.quadTo(x1, y1, (x1 + x2) / 2,
270: (y1 + y2) / 2);
271: // System.out.println("AddQSpline 2[" + x1 + "," + y1 + "][" + (x1 + x2)/2 + "," + (y1 + y2)/2 + "]");
272: }
273: }
274: break;
275:
276: /* Current curve segment is a cubic Bezier curve */
277: case WindowsDefs.TT_PRIM_CSPLINE:
278: for (int i = 0; i < count; i += 3) {
279: float x1 = coords[i * 2];
280: float y1 = coords[i * 2 + 1];
281: float x2 = coords[(i + 1) * 2];
282: float y2 = coords[(i + 1) * 2 + 1];
283: ;
284: float x3 = coords[(i + 2) * 2];
285: float y3 = coords[(i + 2) * 2 + 1];
286: ;
287: gp.curveTo(x1, y1, x2, y2, x3, y3);
288: // System.out.println("AddQSpline [" + x1 + "," + y1 + "][" + x2 + "," + y2 + "][" + x3 + "," + y3 + "]");
289: }
290:
291: break;
292:
293: default:
294: Utils.memaccess.free(buffer);
295: return gp;
296:
297: }
298: offs += count * Win32.POINTFX.sizeof;
299: curve = win32.createTTPOLYCURVE(offs);
300: }
301: /* Closing the polygon */
302: gp.lineTo(x, y);
303: /* processing next polygon */
304: polygon = win32.createTTPOLYGONHEADER(offs);
305: }
306:
307: Utils.memaccess.free(buffer);
308: gp.closePath();
309: return gp;
310: }
311:
312: /**
313: * Returns float array from array of POINTFX elements. Method processes
314: * specified number of elements at once.
315: *
316: * @param addr pointer to the memory block, where POINTFX elements stored
317: * @param count the total number of elements to process
318: */
319: public static native float[] getPoints(long addr, int count);
320: }
|