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