1 /*
2  * Copyright (c) 2010, 2013, 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 sun.awt.*;
29 import sun.java2d.SunGraphics2D;
30 import sun.java2d.pipe.GlyphListPipe;
31 import sun.java2d.xr.*;
32 
33 /**
34  * A delegate pipe of SG2D for drawing any text to a XRender surface
35  *
36  * @author Clemens Eisserer
37  */
38 public class XRTextRenderer extends GlyphListPipe {
39     // Workarround for a bug in libXrender.
40     // In case the number of glyphs of an ELT is a multiple of 254,
41     // a few garbage bytes are sent to the XServer causing hangs.
42     static final int MAX_ELT_GLYPH_COUNT = 253;
43 
44     XRGlyphCache glyphCache;
45     XRCompositeManager maskBuffer;
46     XRBackend backend;
47 
48     GrowableEltArray eltList;
49 
XRTextRenderer(XRCompositeManager buffer)50     public XRTextRenderer(XRCompositeManager buffer) {
51         glyphCache = new XRGlyphCache(buffer);
52         maskBuffer = buffer;
53         backend = buffer.getBackend();
54         eltList = new GrowableEltArray(64);
55     }
56 
drawGlyphList(SunGraphics2D sg2d, GlyphList gl)57     protected void drawGlyphList(SunGraphics2D sg2d, GlyphList gl) {
58         if (gl.getNumGlyphs() == 0) {
59             return;
60         }
61 
62         try {
63             SunToolkit.awtLock();
64 
65             XRSurfaceData x11sd = (XRSurfaceData) sg2d.surfaceData;
66             x11sd.validateAsDestination(null, sg2d.getCompClip());
67             x11sd.maskBuffer.validateCompositeState(sg2d.composite, sg2d.transform, sg2d.paint, sg2d);
68 
69             float advX = gl.getX();
70             float advY = gl.getY();
71             int oldPosX = 0, oldPosY = 0;
72 
73             if (gl.isSubPixPos()) {
74                 advX += 0.1666667f;
75                 advY += 0.1666667f;
76             } else {
77                 advX += 0.5f;
78                 advY += 0.5f;
79             }
80 
81             XRGlyphCacheEntry[] cachedGlyphs = glyphCache.cacheGlyphs(gl);
82             boolean containsLCDGlyphs = false;
83             int activeGlyphSet = cachedGlyphs[0].getGlyphSet();
84 
85             int eltIndex = -1;
86             gl.getBounds();
87             float[] positions = gl.getPositions();
88             for (int i = 0; i < gl.getNumGlyphs(); i++) {
89                 gl.setGlyphIndex(i);
90                 XRGlyphCacheEntry cacheEntry = cachedGlyphs[i];
91                 if (cacheEntry == null) {
92                     continue;
93                 }
94 
95                 eltList.getGlyphs().addInt(cacheEntry.getGlyphID());
96                 int glyphSet = cacheEntry.getGlyphSet();
97 
98                 containsLCDGlyphs |= (glyphSet == glyphCache.lcdGlyphSet);
99 
100                 int posX = 0, posY = 0;
101                 if (gl.usePositions()
102                         || cacheEntry.getXAdvance() != ((float) cacheEntry.getXOff())
103                         || cacheEntry.getYAdvance() != ((float) cacheEntry.getYOff())
104                         || glyphSet != activeGlyphSet
105                         || eltIndex < 0
106                         || eltList.getCharCnt(eltIndex) == MAX_ELT_GLYPH_COUNT) {
107 
108                     eltIndex = eltList.getNextIndex();
109                     eltList.setCharCnt(eltIndex, 1);
110                     activeGlyphSet = glyphSet;
111                     eltList.setGlyphSet(eltIndex, glyphSet);
112 
113                     if (gl.usePositions()) {
114                         // In this case advX only stores rounding errors
115                         float x = positions[i * 2] + advX;
116                         float y = positions[i * 2 + 1] + advY;
117                         posX = (int) Math.floor(x);
118                         posY = (int) Math.floor(y);
119                         advX -= cacheEntry.getXOff();
120                         advY -= cacheEntry.getYOff();
121                     } else {
122                         /*
123                          * Calculate next glyph's position in the case of
124                          * relative positioning. In XRender we can only position
125                          * glyphs using integer coordinates, therefor we sum all
126                          * the advances up as float, and convert them to integer
127                          * later. This way rounding-error can be corrected, and
128                          * is required to be consistent with the software loops.
129                          */
130                         posX = (int) Math.floor(advX);
131                         posY = (int) Math.floor(advY);
132 
133                         // Advance of ELT = difference between stored relative
134                         // positioning information and required float.
135                         advX += (cacheEntry.getXAdvance() - cacheEntry.getXOff());
136                         advY += (cacheEntry.getYAdvance() - cacheEntry.getYOff());
137                     }
138 
139                     // Offset of the current glyph is the difference
140                     // to the last glyph and this one
141                     eltList.setXOff(eltIndex, (posX - oldPosX));
142                     eltList.setYOff(eltIndex, (posY - oldPosY));
143 
144                     oldPosX = posX;
145                     oldPosY = posY;
146 
147                 } else {
148                     eltList.setCharCnt(eltIndex, eltList.getCharCnt(eltIndex) + 1);
149                 }
150             }
151 
152             int maskFormat = containsLCDGlyphs ? XRUtils.PictStandardARGB32 : XRUtils.PictStandardA8;
153             maskBuffer.compositeText(x11sd, (int) gl.getX(), (int) gl.getY(), 0, maskFormat, eltList);
154 
155             eltList.clear();
156         } finally {
157             SunToolkit.awtUnlock();
158         }
159     }
160 }
161