1 /*
2  * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.font;
27 
28 import java.awt.Font;
29 import java.awt.Rectangle;
30 import java.awt.geom.GeneralPath;
31 import java.awt.geom.Point2D;
32 import java.awt.geom.Rectangle2D;
33 
34 /*
35  * performance:
36  * it seems expensive that when using a composite font for
37  * every char you have to find which "slot" can display it.
38  * Just the fact that you need to check at all ..
39  * A composite glyph code ducks this by encoding the slot into the
40  * glyph code, but you still need to get from char to glyph code.
41  */
42 public final class CompositeStrike extends FontStrike {
43 
44     static final int SLOTMASK = 0xffffff;
45 
46     private CompositeFont compFont;
47     private PhysicalStrike[] strikes;
48     int numGlyphs = 0;
49 
CompositeStrike(CompositeFont font2D, FontStrikeDesc desc)50     CompositeStrike(CompositeFont font2D, FontStrikeDesc desc) {
51         this.compFont = font2D;
52         this.desc = desc;
53         this.disposer = new FontStrikeDisposer(compFont, desc);
54         if (desc.style != compFont.style) {
55             algoStyle = true;
56             if ((desc.style & Font.BOLD) == Font.BOLD &&
57                 ((compFont.style & Font.BOLD) == 0)) {
58                 boldness = 1.33f;
59             }
60             if ((desc.style & Font.ITALIC) == Font.ITALIC &&
61                 (compFont.style & Font.ITALIC) == 0) {
62                 italic = 0.7f;
63             }
64         }
65         strikes = new PhysicalStrike[compFont.numSlots];
66     }
67 
68     /* do I need this (see Strike::compositeStrikeForGlyph) */
getStrikeForGlyph(int glyphCode)69     PhysicalStrike getStrikeForGlyph(int glyphCode) {
70         return getStrikeForSlot(glyphCode >>> 24);
71     }
72 
getStrikeForSlot(int slot)73     PhysicalStrike getStrikeForSlot(int slot) {
74         if (slot >= strikes.length) {
75             slot = 0;
76         }
77         PhysicalStrike strike = strikes[slot];
78         if (strike == null) {
79             strike =
80                 (PhysicalStrike)(compFont.getSlotFont(slot).getStrike(desc));
81 
82             strikes[slot] = strike;
83         }
84         return strike;
85     }
86 
getNumGlyphs()87     public int getNumGlyphs() {
88         return compFont.getNumGlyphs();
89     }
90 
getFontMetrics()91     StrikeMetrics getFontMetrics() {
92         if (strikeMetrics == null) {
93             StrikeMetrics compMetrics = new StrikeMetrics();
94             for (int s=0; s<compFont.numMetricsSlots; s++) {
95                 compMetrics.merge(getStrikeForSlot(s).getFontMetrics());
96             }
97             strikeMetrics = compMetrics;
98         }
99         return strikeMetrics;
100     }
101 
102 
103     /* Performance tweak: Slot 0 can often return all the glyphs
104      * Note slot zero doesn't need to be masked.
105      * Could go a step further and support getting a run of glyphs.
106      * This would help many locales a little.
107      *
108      * Note that if a client constructs an invalid a composite glyph that
109      * references an invalid slot, that the behaviour is currently
110      * that this slot index falls through to CompositeFont.getSlotFont(int)
111      * which will substitute a default font, from which to obtain the
112      * strike. If its an invalid glyph code for a valid slot, then the
113      * physical font for that slot will substitute the missing glyph.
114      */
getGlyphImagePtrs(int[] glyphCodes, long[] images, int len)115     void getGlyphImagePtrs(int[] glyphCodes, long[] images, int  len) {
116         PhysicalStrike strike = getStrikeForSlot(0);
117         int numptrs = strike.getSlot0GlyphImagePtrs(glyphCodes, images, len);
118         if (numptrs == len) {
119             return;
120         }
121         for (int i=numptrs; i< len; i++) {
122             strike = getStrikeForGlyph(glyphCodes[i]);
123             images[i] = strike.getGlyphImagePtr(glyphCodes[i] & SLOTMASK);
124         }
125     }
126 
127 
getGlyphImagePtr(int glyphCode)128     long getGlyphImagePtr(int glyphCode) {
129         PhysicalStrike strike = getStrikeForGlyph(glyphCode);
130         return strike.getGlyphImagePtr(glyphCode & SLOTMASK);
131     }
132 
getGlyphImageBounds(int glyphCode, Point2D.Float pt, Rectangle result)133     void getGlyphImageBounds(int glyphCode, Point2D.Float pt, Rectangle result) {
134         PhysicalStrike strike = getStrikeForGlyph(glyphCode);
135         strike.getGlyphImageBounds(glyphCode & SLOTMASK, pt, result);
136     }
137 
getGlyphMetrics(int glyphCode)138     Point2D.Float getGlyphMetrics(int glyphCode) {
139         PhysicalStrike strike = getStrikeForGlyph(glyphCode);
140         return strike.getGlyphMetrics(glyphCode & SLOTMASK);
141     }
142 
getCharMetrics(char ch)143     Point2D.Float getCharMetrics(char ch) {
144         return getGlyphMetrics(compFont.getMapper().charToGlyph(ch));
145     }
146 
getGlyphAdvance(int glyphCode)147     float getGlyphAdvance(int glyphCode) {
148         PhysicalStrike strike = getStrikeForGlyph(glyphCode);
149         return strike.getGlyphAdvance(glyphCode & SLOTMASK);
150     }
151 
152     /* REMIND where to cache?
153      * The glyph advance is already cached by physical strikes and that's a lot
154      * of the work.
155      * Also FontDesignMetrics maintains a latin char advance cache, so don't
156      * cache advances here as apps tend to hold onto metrics objects when
157      * performance is sensitive to it. Revisit this assumption later.
158      */
getCodePointAdvance(int cp)159     float getCodePointAdvance(int cp) {
160         return getGlyphAdvance(compFont.getMapper().charToGlyph(cp));
161     }
162 
getGlyphOutlineBounds(int glyphCode)163     Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
164         PhysicalStrike strike = getStrikeForGlyph(glyphCode);
165         return strike.getGlyphOutlineBounds(glyphCode & SLOTMASK);
166     }
167 
getGlyphOutline(int glyphCode, float x, float y)168     GeneralPath getGlyphOutline(int glyphCode, float x, float y) {
169 
170         PhysicalStrike strike = getStrikeForGlyph(glyphCode);
171         GeneralPath path = strike.getGlyphOutline(glyphCode & SLOTMASK, x, y);
172         if (path == null) {
173             return new GeneralPath();
174         } else {
175             return path;
176         }
177     }
178 
179     /* The physical font slot for each glyph is encoded in the glyph ID
180      * To be as efficient as possible we find a run of glyphs from the
181      * same slot and create a temporary array of these glyphs decoded
182      * to the slot. The slot font is then queried for the GeneralPath
183      * for that run of glyphs. GeneralPaths from each run are appended
184      * to create the shape for the whole glyph array.
185      */
getGlyphVectorOutline(int[] glyphs, float x, float y)186     GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
187         GeneralPath path = null;
188         GeneralPath gp;
189         int glyphIndex = 0;
190         int[] tmpGlyphs;
191 
192         while (glyphIndex < glyphs.length) {
193             int start = glyphIndex;
194             int slot = glyphs[glyphIndex] >>> 24;
195             while (glyphIndex < glyphs.length &&
196                    (glyphs[glyphIndex+1] >>> 24) == slot) {
197                 glyphIndex++;
198             }
199             int tmpLen = glyphIndex-start+1;
200             tmpGlyphs = new int[tmpLen];
201             for (int i=0;i<tmpLen;i++) {
202                 tmpGlyphs[i] = glyphs[i] & SLOTMASK;
203             }
204             gp = getStrikeForSlot(slot).getGlyphVectorOutline(tmpGlyphs, x, y);
205             if (path == null) {
206                 path = gp;
207             } else if (gp != null) {
208                 path.append(gp, false);
209             }
210         }
211         if (path == null) {
212             return new GeneralPath();
213         } else {
214             return path;
215         }
216     }
217 }
218