951 lines · 38.7 KB · Java
RawDownload
                    segIntGlyphImages[segIndex][subIndex] = (int)glyphPtr;                    return glyphPtr;                } else {                    StrikeCache.freeIntPointer((int)glyphPtr);                    return segIntGlyphImages[segIndex][subIndex] & INTMASK;                }             case LONGARRAY:                if (longGlyphImages[glyphCode] == 0L) {                    longGlyphImages[glyphCode] = glyphPtr;                    return glyphPtr;                } else {                    StrikeCache.freeLongPointer(glyphPtr);                    return longGlyphImages[glyphCode];                }            case SEGLONGARRAY:                segIndex = glyphCode >> SEGSHIFT;                subIndex = glyphCode % SEGSIZE;                if (segLongGlyphImages[segIndex] == null) {                    segLongGlyphImages[segIndex] = new long[SEGSIZE];                }                if (segLongGlyphImages[segIndex][subIndex] == 0L) {                    segLongGlyphImages[segIndex][subIndex] = glyphPtr;                    return glyphPtr;                } else {                    StrikeCache.freeLongPointer(glyphPtr);                    return segLongGlyphImages[segIndex][subIndex];                }        }         /* Reach here only when the cache is not initialised which is only         * for the first glyph to be initialised in the strike.         * Initialise it and recurse. Note that we are already synchronized.         */        initGlyphCache();        return setCachedGlyphPtr(glyphCode, glyphPtr);    }     /* Called only from synchronized code or constructor */    private synchronized void initGlyphCache() {         int numGlyphs = mapper.getNumGlyphs();        int tmpFormat = UNINITIALISED;        if (segmentedCache) {            int numSegments = (numGlyphs + SEGSIZE-1)/SEGSIZE;            if (longAddresses) {                tmpFormat = SEGLONGARRAY;                segLongGlyphImages = new long[numSegments][];                this.disposer.segLongGlyphImages = segLongGlyphImages;             } else {                 tmpFormat = SEGINTARRAY;                 segIntGlyphImages = new int[numSegments][];                 this.disposer.segIntGlyphImages = segIntGlyphImages;             }        } else {            if (longAddresses) {                tmpFormat = LONGARRAY;                longGlyphImages = new long[numGlyphs];                this.disposer.longGlyphImages = longGlyphImages;            } else {                tmpFormat = INTARRAY;                intGlyphImages = new int[numGlyphs];                this.disposer.intGlyphImages = intGlyphImages;            }        }        glyphCacheFormat = tmpFormat;    }     float getGlyphAdvance(int glyphCode) {        return getGlyphAdvance(glyphCode, true);    }     /* Metrics info is always retrieved. If the GlyphInfo address is non-zero     * then metrics info there is valid and can just be copied.     * This is in user space coordinates unless getUserAdv == false.     * Device space advance should not be propagated out of this class.     */    private float getGlyphAdvance(int glyphCode, boolean getUserAdv) {        float advance;         if (glyphCode >= INVISIBLE_GLYPHS) {            return 0f;        }         /* Notes on the (getUserAdv == false) case.         *         * Setting getUserAdv == false is internal to this class.         * If there's no graphics transform we can let         * getGlyphAdvance take its course, and potentially caching in         * advances arrays, except for signalling that         * getUserAdv == false means there is no need to create an image.         * It is possible that code already calculated the user advance,         * and it is desirable to take advantage of that work.         * But, if there's a transform and we want device advance, we         * can't use any values cached in the advances arrays - unless         * first re-transform them into device space using 'desc.devTx'.         * invertDevTx is null if the graphics transform is identity,         * a translate, or non-invertible. The latter case should         * not ever occur in the getUserAdv == false path.         * In other words its either null, or the inversion of a         * simple uniform scale. If its null, we can populate and         * use the advance caches as normal.         *         * If we don't find a cached value, obtain the device advance and         * return it. This will get stashed on the image by the caller and any         * subsequent metrics calls will be able to use it as is the case         * whenever an image is what is initially requested.         *         * Don't query if there's a value cached on the image, since this         * getUserAdv==false code path is entered solely when none exists.         */        if (horizontalAdvances != null) {            advance = horizontalAdvances[glyphCode];            if (advance != Float.MAX_VALUE) {                if (!getUserAdv && invertDevTx != null) {                    Point2D.Float metrics = new Point2D.Float(advance, 0f);                    desc.devTx.deltaTransform(metrics, metrics);                    return metrics.x;                } else {                    return advance;                }            }        } else if (segmentedCache && segHorizontalAdvances != null) {            int segIndex = glyphCode >> SEGSHIFT;            float[] subArray = segHorizontalAdvances[segIndex];            if (subArray != null) {                advance = subArray[glyphCode % SEGSIZE];                if (advance != Float.MAX_VALUE) {                    if (!getUserAdv && invertDevTx != null) {                        Point2D.Float metrics = new Point2D.Float(advance, 0f);                        desc.devTx.deltaTransform(metrics, metrics);                        return metrics.x;                    } else {                        return advance;                    }                }            }        }         if (!getUserAdv && invertDevTx != null) {            Point2D.Float metrics = new Point2D.Float();            fileFont.getGlyphMetrics(pScalerContext, glyphCode, metrics);            return metrics.x;        }         if (invertDevTx != null || !getUserAdv) {            /* If there is a device transform need x & y advance to             * transform back into user space.             */            advance = getGlyphMetrics(glyphCode, getUserAdv).x;        } else {            long glyphPtr;            if (getImageWithAdvance) {                /* A heuristic optimisation says that for most cases its                 * worthwhile retrieving the image at the same time as the                 * advance. So here we get the image data even if its not                 * already cached.                 */                glyphPtr = getGlyphImagePtr(glyphCode);            } else {                glyphPtr = getCachedGlyphPtr(glyphCode);            }            if (glyphPtr != 0L) {                advance = StrikeCache.unsafe.getFloat                    (glyphPtr + StrikeCache.xAdvanceOffset);             } else {                advance = fileFont.getGlyphAdvance(pScalerContext, glyphCode);            }        }         if (horizontalAdvances != null) {            horizontalAdvances[glyphCode] = advance;        } else if (segmentedCache && segHorizontalAdvances != null) {            int segIndex = glyphCode >> SEGSHIFT;            int subIndex = glyphCode % SEGSIZE;            if (segHorizontalAdvances[segIndex] == null) {                segHorizontalAdvances[segIndex] = new float[SEGSIZE];                for (int i=0; i<SEGSIZE; i++) {                     segHorizontalAdvances[segIndex][i] = Float.MAX_VALUE;                }            }            segHorizontalAdvances[segIndex][subIndex] = advance;        }        return advance;    }     float getCodePointAdvance(int cp) {        return getGlyphAdvance(mapper.charToGlyph(cp));    }     /**     * Result and pt are both in device space.     */    void getGlyphImageBounds(int glyphCode, Point2D.Float pt,                             Rectangle result) {         long ptr = getGlyphImagePtr(glyphCode);        float topLeftX, topLeftY;         /* With our current design NULL ptr is not possible           but if we eventually allow scalers to return NULL pointers           this check might be actually useful. */        if (ptr == 0L) {            result.x = (int) Math.floor(pt.x);            result.y = (int) Math.floor(pt.y);            result.width = result.height = 0;            return;        }         topLeftX = StrikeCache.unsafe.getFloat(ptr+StrikeCache.topLeftXOffset);        topLeftY = StrikeCache.unsafe.getFloat(ptr+StrikeCache.topLeftYOffset);         result.x = (int)Math.floor(pt.x + topLeftX);        result.y = (int)Math.floor(pt.y + topLeftY);        result.width =            StrikeCache.unsafe.getShort(ptr+StrikeCache.widthOffset)  &0x0ffff;        result.height =            StrikeCache.unsafe.getShort(ptr+StrikeCache.heightOffset) &0x0ffff;         /* HRGB LCD text may have padding that is empty. This is almost always         * going to be when topLeftX is -2 or less.         * Try to return a tighter bounding box in that case.         * If the first three bytes of every row are all zero, then         * add 1 to "x" and reduce "width" by 1.         */        if ((desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HRGB ||             desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HBGR)            && topLeftX <= -2.0f) {            int minx = getGlyphImageMinX(ptr, (int)result.x);            if (minx > result.x) {                result.x += 1;                result.width -=1;            }        }    }     private int getGlyphImageMinX(long ptr, int origMinX) {         int width = StrikeCache.unsafe.getChar(ptr+StrikeCache.widthOffset);        int height = StrikeCache.unsafe.getChar(ptr+StrikeCache.heightOffset);        int rowBytes =            StrikeCache.unsafe.getChar(ptr+StrikeCache.rowBytesOffset);         if (rowBytes == width) {            return origMinX;        }         long pixelData;        if (StrikeCache.nativeAddressSize == 4) {            pixelData = 0xffffffff &                StrikeCache.unsafe.getInt(ptr + StrikeCache.pixelDataOffset);        } else {            pixelData =                StrikeCache.unsafe.getLong(ptr + StrikeCache.pixelDataOffset);        }        if (pixelData == 0L) {            return origMinX;        }         for (int y=0;y<height;y++) {            for (int x=0;x<3;x++) {                if (StrikeCache.unsafe.getByte(pixelData+y*rowBytes+x) != 0) {                    return origMinX;                }            }        }        return origMinX+1;    }     /* These 3 metrics methods below should be implemented to return     * values in user space.     */    StrikeMetrics getFontMetrics() {        if (strikeMetrics == null) {            strikeMetrics =                fileFont.getFontMetrics(pScalerContext);            if (invertDevTx != null) {                strikeMetrics.convertToUserSpace(invertDevTx);            }        }        return strikeMetrics;    }     Point2D.Float getGlyphMetrics(int glyphCode) {        return getGlyphMetrics(glyphCode, true);    }     private Point2D.Float getGlyphMetrics(int glyphCode, boolean getImage) {        Point2D.Float metrics = new Point2D.Float();         // !!! or do we force sgv user glyphs?        if (glyphCode >= INVISIBLE_GLYPHS) {            return metrics;        }        long glyphPtr;        if (getImageWithAdvance && getImage) {            /* A heuristic optimisation says that for most cases its             * worthwhile retrieving the image at the same time as the             * metrics. So here we get the image data even if its not             * already cached.             */            glyphPtr = getGlyphImagePtr(glyphCode);        } else {             glyphPtr = getCachedGlyphPtr(glyphCode);        }        if (glyphPtr != 0L) {            metrics = new Point2D.Float();            metrics.x = StrikeCache.unsafe.getFloat                (glyphPtr + StrikeCache.xAdvanceOffset);            metrics.y = StrikeCache.unsafe.getFloat                (glyphPtr + StrikeCache.yAdvanceOffset);            /* advance is currently in device space, need to convert back             * into user space.             * This must not include the translation component. */            if (invertDevTx != null) {                invertDevTx.deltaTransform(metrics, metrics);            }        } else {            /* We sometimes cache these metrics as they are expensive to             * generate for large glyphs.             * We never reach this path if we obtain images with advances.             * But if we do not obtain images with advances its possible that             * we first obtain this information, then the image, and never             * will access this value again.             */            Integer key = Integer.valueOf(glyphCode);            Point2D.Float value = null;            ConcurrentHashMap<Integer, Point2D.Float> glyphMetricsMap = null;            if (glyphMetricsMapRef != null) {                glyphMetricsMap = glyphMetricsMapRef.get();            }            if (glyphMetricsMap != null) {                value = glyphMetricsMap.get(key);                if (value != null) {                    metrics.x = value.x;                    metrics.y = value.y;                    /* already in user space */                    return metrics;                }            }            if (value == null) {                fileFont.getGlyphMetrics(pScalerContext, glyphCode, metrics);                /* advance is currently in device space, need to convert back                 * into user space.                 */                if (invertDevTx != null) {                    invertDevTx.deltaTransform(metrics, metrics);                }                value = new Point2D.Float(metrics.x, metrics.y);                /* We aren't synchronizing here so it is possible to                 * overwrite the map with another one but this is harmless.                 */                if (glyphMetricsMap == null) {                    glyphMetricsMap =                        new ConcurrentHashMap<Integer, Point2D.Float>();                    glyphMetricsMapRef =                        new SoftReference<ConcurrentHashMap<Integer,                        Point2D.Float>>(glyphMetricsMap);                }                glyphMetricsMap.put(key, value);            }        }        return metrics;    }     Point2D.Float getCharMetrics(char ch) {        return getGlyphMetrics(mapper.charToGlyph(ch));    }     /* The caller of this can be trusted to return a copy of this     * return value rectangle to public API. In fact frequently it     * can't use use this return value directly anyway.     * This returns bounds in device space. Currently the only     * caller is SGV and it converts back to user space.     * We could change things so that this code does the conversion so     * that all coords coming out of the font system are converted back     * into user space even if they were measured in device space.     * The same applies to the other methods that return outlines (below)     * But it may make particular sense for this method that caches its     * results.     * There'd be plenty of exceptions, to this too, eg getGlyphPoint needs     * device coords as its called from native layout and getGlyphImageBounds     * is used by GlyphVector.getGlyphPixelBounds which is specified to     * return device coordinates, the image pointers aren't really used     * up in Java code either.     */    Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {         if (boundsMap == null) {            boundsMap = new ConcurrentHashMap<Integer, Rectangle2D.Float>();        }         Integer key = Integer.valueOf(glyphCode);        Rectangle2D.Float bounds = boundsMap.get(key);         if (bounds == null) {            bounds = fileFont.getGlyphOutlineBounds(pScalerContext, glyphCode);            boundsMap.put(key, bounds);        }        return bounds;    }     public Rectangle2D getOutlineBounds(int glyphCode) {        return fileFont.getGlyphOutlineBounds(pScalerContext, glyphCode);    }     private        WeakReference<ConcurrentHashMap<Integer,GeneralPath>> outlineMapRef;     GeneralPath getGlyphOutline(int glyphCode, float x, float y) {         GeneralPath gp = null;        ConcurrentHashMap<Integer, GeneralPath> outlineMap = null;         if (outlineMapRef != null) {            outlineMap = outlineMapRef.get();            if (outlineMap != null) {                gp = (GeneralPath)outlineMap.get(glyphCode);            }        }         if (gp == null) {            gp = fileFont.getGlyphOutline(pScalerContext, glyphCode, 0, 0);            if (outlineMap == null) {                outlineMap = new ConcurrentHashMap<Integer, GeneralPath>();                outlineMapRef =                   new WeakReference                       <ConcurrentHashMap<Integer,GeneralPath>>(outlineMap);            }            outlineMap.put(glyphCode, gp);        }        gp = (GeneralPath)gp.clone(); // mutable!        if (x != 0f || y != 0f) {            gp.transform(AffineTransform.getTranslateInstance(x, y));        }        return gp;    }     GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {        return fileFont.getGlyphVectorOutline(pScalerContext,                                              glyphs, glyphs.length, x, y);    }     protected void adjustPoint(Point2D.Float pt) {        if (invertDevTx != null) {            invertDevTx.deltaTransform(pt, pt);        }    }}

Lines 501–951 of 951. Previous lines