Loading tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java 0 → 100644 +215 −0 Original line number Diff line number Diff line /* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.graphics; import java.awt.Font; import java.awt.Graphics2D; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.util.LinkedList; import java.util.List; import com.ibm.icu.lang.UScript; import com.ibm.icu.lang.UScriptRun; import android.graphics.Paint_Delegate.FontInfo; /** * Render the text by breaking it into various scripts and using the right font for each script. * Can be used to measure the text without actually drawing it. */ @SuppressWarnings("deprecation") public class BidiRenderer { /* package */ static class ScriptRun { int start; int limit; boolean isRtl; int scriptCode; FontInfo font; public ScriptRun(int start, int limit, boolean isRtl) { this.start = start; this.limit = limit; this.isRtl = isRtl; this.scriptCode = UScript.INVALID_CODE; } } /* package */ Graphics2D graphics; /* package */ Paint_Delegate paint; /* package */ char[] text; /** * @param graphics May be null. * @param paint The Paint to use to get the fonts. Should not be null. * @param text Unidirectional text. Should not be null. */ /* package */ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) { assert (paint != null); this.graphics = graphics; this.paint = paint; this.text = text; } /** * Render unidirectional text. * * This method can also be used to measure the width of the text without actually drawing it. * * @param start index of the first character * @param limit index of the first character that should not be rendered. * @param isRtl is the text right-to-left * @param advances If not null, then advances for each character to be rendered are returned * here. * @param advancesIndex index into advances from where the advances need to be filled. * @param draw If true and {@link graphics} is not null, draw the rendered text on the graphics * at the given co-ordinates * @param x The x-coordinate of the left edge of where the text should be drawn on the given * graphics. * @param y The y-coordinate at which to draw the text on the given graphics. * @return The x-coordinate of the right edge of the drawn text. In other words, * x + the width of the text. */ /* package */ float renderText(int start, int limit, boolean isRtl, float advances[], int advancesIndex, boolean draw, float x, float y) { // We break the text into scripts and then select font based on it and then render each of // the script runs. for (ScriptRun run : getScriptRuns(text, start, limit, isRtl, paint.getFonts())) { int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT; flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT; x = renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw, x, y); advancesIndex += run.limit - run.start; } return x; } /** * Render a script run. Use the preferred font to render as much as possible. This also * implements a fallback mechanism to render characters that cannot be drawn using the * preferred font. * * @return x + width of the text drawn. */ private float renderScript(int start, int limit, FontInfo preferredFont, int flag, float advances[], int advancesIndex, boolean draw, float x, float y) { List<FontInfo> fonts = paint.getFonts(); if (fonts == null || preferredFont == null) { return x; } while (start < limit) { boolean foundFont = false; int canDisplayUpTo = preferredFont.mFont.canDisplayUpTo(text, start, limit); if (canDisplayUpTo == -1) { return render(start, limit, preferredFont, flag, advances, advancesIndex, draw, x, y); } else if (canDisplayUpTo > start) { // can draw something x = render(start, canDisplayUpTo, preferredFont, flag, advances, advancesIndex, draw, x, y); advancesIndex += canDisplayUpTo - start; start = canDisplayUpTo; } int charCount = Character.isHighSurrogate(text[start]) ? 2 : 1; for (FontInfo font : fonts) { canDisplayUpTo = font.mFont.canDisplayUpTo(text, start, start + charCount); if (canDisplayUpTo == -1) { x = render(start, start+charCount, font, flag, advances, advancesIndex, draw, x, y); start += charCount; advancesIndex += charCount; foundFont = true; break; } } if (!foundFont) { // No font can display this char. Use the preferred font. The char will most // probably appear as a box or a blank space. We could, probably, use some // heuristics and break the character into the base character and diacritics and // then draw it, but it's probably not worth the effort. x = render(start, start + charCount, preferredFont, flag, advances, advancesIndex, draw, x, y); start += charCount; advancesIndex += charCount; } } return x; } /** * Render the text with the given font. */ private float render(int start, int limit, FontInfo font, int flag, float advances[], int advancesIndex, boolean draw, float x, float y) { float totalAdvance = 0; // Since the metrics don't have anti-aliasing set, we create a new FontRenderContext with // the anti-aliasing set. FontRenderContext f = font.mMetrics.getFontRenderContext(); FontRenderContext frc = new FontRenderContext(f.getTransform(), paint.isAntiAliased(), f.usesFractionalMetrics()); GlyphVector gv = font.mFont.layoutGlyphVector(frc, text, start, limit, flag); int ng = gv.getNumGlyphs(); int[] ci = gv.getGlyphCharIndices(0, ng, null); for (int i = 0; i < ng; i++) { float adv = gv.getGlyphMetrics(i).getAdvanceX(); if (advances != null) { int adv_idx = advancesIndex + ci[i]; advances[adv_idx] += adv; } totalAdvance += adv; } if (draw && graphics != null) { graphics.drawGlyphVector(gv, x, y); } return x + totalAdvance; } // --- Static helper methods --- /* package */ static List<ScriptRun> getScriptRuns(char[] text, int start, int limit, boolean isRtl, List<FontInfo> fonts) { LinkedList<ScriptRun> scriptRuns = new LinkedList<ScriptRun>(); int count = limit - start; UScriptRun uScriptRun = new UScriptRun(text, start, count); while (uScriptRun.next()) { int scriptStart = uScriptRun.getScriptStart(); int scriptLimit = uScriptRun.getScriptLimit(); ScriptRun run = new ScriptRun(scriptStart, scriptLimit, isRtl); run.scriptCode = uScriptRun.getScriptCode(); setScriptFont(text, run, fonts); scriptRuns.add(run); } return scriptRuns; } // TODO: Replace this method with one which returns the font based on the scriptCode. private static void setScriptFont(char[] text, ScriptRun run, List<FontInfo> fonts) { for (FontInfo fontInfo : fonts) { if (fontInfo.mFont.canDisplayUpTo(text, run.start, run.limit) == -1) { run.font = fontInfo; return; } } run.font = fonts.get(0); } } tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +8 −81 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import com.android.layoutlib.bridge.impl.GcSnapshot; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.Bitmap.Config; import android.graphics.Paint_Delegate.FontInfo; import android.text.TextUtils; import java.awt.Color; Loading @@ -35,7 +34,6 @@ import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Arc2D; import java.awt.image.BufferedImage; import java.util.List; /** Loading Loading @@ -978,7 +976,8 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void native_drawText(int nativeCanvas, final char[] text, final int index, final int count, final float startX, final float startY, int flags, int paint) { final float startX, final float startY, final int flags, int paint) { draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, new GcSnapshot.Drawable() { @Override Loading @@ -988,10 +987,10 @@ public final class Canvas_Delegate { // Paint.TextAlign indicates how the text is positioned relative to X. // LEFT is the default and there's nothing to do. float x = startX; float y = startY; int limit = index + count; boolean isRtl = flags == Canvas.DIRECTION_RTL; if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) { // TODO: check the value of bidiFlags. float m = paintDelegate.measureText(text, index, count, 0); float m = paintDelegate.measureText(text, index, count, isRtl); if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) { x -= m / 2; } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) { Loading @@ -999,87 +998,15 @@ public final class Canvas_Delegate { } } List<FontInfo> fonts = paintDelegate.getFonts(); if (fonts.size() > 0) { FontInfo mainFont = fonts.get(0); int i = index; int lastIndex = index + count; while (i < lastIndex) { // always start with the main font. int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); if (upTo == -1) { // draw all the rest and exit. graphics.setFont(mainFont.mFont); graphics.drawChars(text, i, lastIndex - i, (int)x, (int)y); return; } else if (upTo > 0) { // draw what's possible graphics.setFont(mainFont.mFont); graphics.drawChars(text, i, upTo - i, (int)x, (int)y); // compute the width that was drawn to increase x x += mainFont.mMetrics.charsWidth(text, i, upTo - i); // move index to the first non displayed char. i = upTo; // don't call continue at this point. Since it is certain the main font // cannot display the font a index upTo (now ==i), we move on to the // fallback fonts directly. } // no char supported, attempt to read the next char(s) with the // fallback font. In this case we only test the first character // and then go back to test with the main font. // Special test for 2-char characters. boolean foundFont = false; for (int f = 1 ; f < fonts.size() ; f++) { FontInfo fontInfo = fonts.get(f); // need to check that the font can display the character. We test // differently if the char is a high surrogate. int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); if (upTo == -1) { // draw that char graphics.setFont(fontInfo.mFont); graphics.drawChars(text, i, charCount, (int)x, (int)y); // update x x += fontInfo.mMetrics.charsWidth(text, i, charCount); // update the index in the text, and move on i += charCount; foundFont = true; break; } } // in case no font can display the char, display it with the main font. // (it'll put a square probably) if (foundFont == false) { int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; graphics.setFont(mainFont.mFont); graphics.drawChars(text, i, charCount, (int)x, (int)y); // measure it to advance x x += mainFont.mMetrics.charsWidth(text, i, charCount); // and move to the next chars. i += charCount; } } } new BidiRenderer(graphics, paintDelegate, text).renderText( index, limit, isRtl, null, 0, true, x, startY); } }); } @LayoutlibDelegate /*package*/ static void native_drawText(int nativeCanvas, String text, int start, int end, float x, float y, int flags, int paint) { int start, int end, float x, float y, final int flags, int paint) { int count = end - start; char[] buffer = TemporaryBuffer.obtain(count); TextUtils.getChars(text, start, end, buffer, 0); Loading tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +30 −106 Original line number Diff line number Diff line Loading @@ -32,7 +32,6 @@ import java.awt.Stroke; import java.awt.Toolkit; import java.awt.font.FontRenderContext; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collections; import java.util.List; Loading Loading @@ -576,7 +575,7 @@ public class Paint_Delegate { return 0; } return delegate.measureText(text, index, count, bidiFlags); return delegate.measureText(text, index, count, isRtl(bidiFlags)); } @LayoutlibDelegate Loading Loading @@ -615,7 +614,7 @@ public class Paint_Delegate { } // measure from start to end float res = delegate.measureText(text, start, end - start + 1, bidiFlags); float res = delegate.measureText(text, start, end - start + 1, isRtl(bidiFlags)); if (measuredWidth != null) { measuredWidth[measureIndex] = res; Loading Loading @@ -980,51 +979,27 @@ public class Paint_Delegate { /*package*/ static float native_getTextRunAdvances(int native_object, char[] text, int index, int count, int contextIndex, int contextCount, int flags, float[] advances, int advancesIndex) { if (advances != null) for (int i = advancesIndex; i< advancesIndex+count; i++) advances[i]=0; // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); if (delegate == null) { if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) { return 0.f; } boolean isRtl = isRtl(flags); if (delegate.mFonts.size() > 0) { // FIXME: handle multi-char characters (see measureText) float totalAdvance = 0; for (int i = 0; i < count; i++) { char c = text[i + index]; boolean found = false; for (FontInfo info : delegate.mFonts) { if (info.mFont.canDisplay(c)) { float adv = info.mMetrics.charWidth(c); totalAdvance += adv; if (advances != null) { advances[i] = adv; } found = true; break; } } if (found == false) { // no advance for this char. if (advances != null) { advances[i] = 0.f; } } } return totalAdvance; } return 0; int limit = index + count; return new BidiRenderer(null, delegate, text).renderText( index, limit, isRtl, advances, advancesIndex, false, 0, 0); } @LayoutlibDelegate /*package*/ static float native_getTextRunAdvances(int native_object, String text, int start, int end, int contextStart, int contextEnd, int flags, float[] advances, int advancesIndex) { // FIXME: support contextStart, contextEnd and direction flag // FIXME: support contextStart and contextEnd int count = end - start; char[] buffer = TemporaryBuffer.obtain(count); TextUtils.getChars(text, start, end, buffer, 0); Loading Loading @@ -1080,19 +1055,12 @@ public class Paint_Delegate { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(nativePaint); if (delegate == null) { if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) { return; } // FIXME should test if the main font can display all those characters. // See MeasureText if (delegate.mFonts.size() > 0) { FontInfo mainInfo = delegate.mFonts.get(0); Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count, delegate.mFontContext); bounds.set(0, 0, (int) rect.getWidth(), (int) rect.getHeight()); } int w = (int) delegate.measureText(text, index, count, isRtl(bidiFlags)); int h= delegate.getFonts().get(0).mMetrics.getHeight(); bounds.set(0, 0, w, h); } @LayoutlibDelegate Loading Loading @@ -1176,6 +1144,7 @@ public class Paint_Delegate { info.mFont = info.mFont.deriveFont(new AffineTransform( mTextScaleX, mTextSkewX, 0, 1, 0, 0)); } // The metrics here don't have anti-aliasing set. info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont); infoList.add(info); Loading @@ -1185,64 +1154,9 @@ public class Paint_Delegate { } } /*package*/ float measureText(char[] text, int index, int count, int bidiFlags) { // TODO: find out what bidiFlags actually does. // WARNING: the logic in this method is similar to Canvas_Delegate.native_drawText // Any change to this method should be reflected there as well if (mFonts.size() > 0) { FontInfo mainFont = mFonts.get(0); int i = index; int lastIndex = index + count; float total = 0f; while (i < lastIndex) { // always start with the main font. int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); if (upTo == -1) { // shortcut to exit return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i); } else if (upTo > 0) { total += mainFont.mMetrics.charsWidth(text, i, upTo - i); i = upTo; // don't call continue at this point. Since it is certain the main font // cannot display the font a index upTo (now ==i), we move on to the // fallback fonts directly. } // no char supported, attempt to read the next char(s) with the // fallback font. In this case we only test the first character // and then go back to test with the main font. // Special test for 2-char characters. boolean foundFont = false; for (int f = 1 ; f < mFonts.size() ; f++) { FontInfo fontInfo = mFonts.get(f); // need to check that the font can display the character. We test // differently if the char is a high surrogate. int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); if (upTo == -1) { total += fontInfo.mMetrics.charsWidth(text, i, charCount); i += charCount; foundFont = true; break; } } // in case no font can display the char, measure it with the main font. if (foundFont == false) { int size = Character.isHighSurrogate(text[i]) ? 2 : 1; total += mainFont.mMetrics.charsWidth(text, i, size); i += size; } } return total; } return 0; /*package*/ float measureText(char[] text, int index, int count, boolean isRtl) { return new BidiRenderer(null, this, text).renderText( index, index + count, isRtl, null, 0, false, 0, 0); } private float getFontMetrics(FontMetrics metrics) { Loading Loading @@ -1281,4 +1195,14 @@ public class Paint_Delegate { } } private static boolean isRtl(int flag) { switch(flag) { case Paint.BIDI_RTL: case Paint.BIDI_FORCE_RTL: case Paint.BIDI_DEFAULT_RTL: return true; default: return false; } } } tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java +27 −4 Original line number Diff line number Diff line Loading @@ -16,7 +16,10 @@ package android.text; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import com.ibm.icu.text.Bidi; /** Loading @@ -29,9 +32,29 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; public class AndroidBidi_Delegate { @LayoutlibDelegate /*package*/ static int runBidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo) { // return the equivalent of Layout.DIR_LEFT_TO_RIGHT // TODO: actually figure the direction. return 0; /*package*/ static int runBidi(int dir, char[] chars, byte[] charInfo, int count, boolean haveInfo) { switch (dir) { case 0: // Layout.DIR_REQUEST_LTR case 1: // Layout.DIR_REQUEST_RTL break; // No change. case -1: dir = Bidi.LEVEL_DEFAULT_LTR; break; case -2: dir = Bidi.LEVEL_DEFAULT_RTL; break; default: // Invalid code. Log error, assume LEVEL_DEFAULT_LTR and continue. Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Invalid direction flag", null); dir = Bidi.LEVEL_DEFAULT_LTR; } Bidi bidi = new Bidi(chars, 0, null, 0, count, dir); if (charInfo != null) { for (int i = 0; i < count; ++i) charInfo[i] = bidi.getLevelAt(i); } return bidi.getParaLevel(); } } tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java +20 −2 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java 0 → 100644 +215 −0 Original line number Diff line number Diff line /* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.graphics; import java.awt.Font; import java.awt.Graphics2D; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.util.LinkedList; import java.util.List; import com.ibm.icu.lang.UScript; import com.ibm.icu.lang.UScriptRun; import android.graphics.Paint_Delegate.FontInfo; /** * Render the text by breaking it into various scripts and using the right font for each script. * Can be used to measure the text without actually drawing it. */ @SuppressWarnings("deprecation") public class BidiRenderer { /* package */ static class ScriptRun { int start; int limit; boolean isRtl; int scriptCode; FontInfo font; public ScriptRun(int start, int limit, boolean isRtl) { this.start = start; this.limit = limit; this.isRtl = isRtl; this.scriptCode = UScript.INVALID_CODE; } } /* package */ Graphics2D graphics; /* package */ Paint_Delegate paint; /* package */ char[] text; /** * @param graphics May be null. * @param paint The Paint to use to get the fonts. Should not be null. * @param text Unidirectional text. Should not be null. */ /* package */ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) { assert (paint != null); this.graphics = graphics; this.paint = paint; this.text = text; } /** * Render unidirectional text. * * This method can also be used to measure the width of the text without actually drawing it. * * @param start index of the first character * @param limit index of the first character that should not be rendered. * @param isRtl is the text right-to-left * @param advances If not null, then advances for each character to be rendered are returned * here. * @param advancesIndex index into advances from where the advances need to be filled. * @param draw If true and {@link graphics} is not null, draw the rendered text on the graphics * at the given co-ordinates * @param x The x-coordinate of the left edge of where the text should be drawn on the given * graphics. * @param y The y-coordinate at which to draw the text on the given graphics. * @return The x-coordinate of the right edge of the drawn text. In other words, * x + the width of the text. */ /* package */ float renderText(int start, int limit, boolean isRtl, float advances[], int advancesIndex, boolean draw, float x, float y) { // We break the text into scripts and then select font based on it and then render each of // the script runs. for (ScriptRun run : getScriptRuns(text, start, limit, isRtl, paint.getFonts())) { int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT; flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT; x = renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw, x, y); advancesIndex += run.limit - run.start; } return x; } /** * Render a script run. Use the preferred font to render as much as possible. This also * implements a fallback mechanism to render characters that cannot be drawn using the * preferred font. * * @return x + width of the text drawn. */ private float renderScript(int start, int limit, FontInfo preferredFont, int flag, float advances[], int advancesIndex, boolean draw, float x, float y) { List<FontInfo> fonts = paint.getFonts(); if (fonts == null || preferredFont == null) { return x; } while (start < limit) { boolean foundFont = false; int canDisplayUpTo = preferredFont.mFont.canDisplayUpTo(text, start, limit); if (canDisplayUpTo == -1) { return render(start, limit, preferredFont, flag, advances, advancesIndex, draw, x, y); } else if (canDisplayUpTo > start) { // can draw something x = render(start, canDisplayUpTo, preferredFont, flag, advances, advancesIndex, draw, x, y); advancesIndex += canDisplayUpTo - start; start = canDisplayUpTo; } int charCount = Character.isHighSurrogate(text[start]) ? 2 : 1; for (FontInfo font : fonts) { canDisplayUpTo = font.mFont.canDisplayUpTo(text, start, start + charCount); if (canDisplayUpTo == -1) { x = render(start, start+charCount, font, flag, advances, advancesIndex, draw, x, y); start += charCount; advancesIndex += charCount; foundFont = true; break; } } if (!foundFont) { // No font can display this char. Use the preferred font. The char will most // probably appear as a box or a blank space. We could, probably, use some // heuristics and break the character into the base character and diacritics and // then draw it, but it's probably not worth the effort. x = render(start, start + charCount, preferredFont, flag, advances, advancesIndex, draw, x, y); start += charCount; advancesIndex += charCount; } } return x; } /** * Render the text with the given font. */ private float render(int start, int limit, FontInfo font, int flag, float advances[], int advancesIndex, boolean draw, float x, float y) { float totalAdvance = 0; // Since the metrics don't have anti-aliasing set, we create a new FontRenderContext with // the anti-aliasing set. FontRenderContext f = font.mMetrics.getFontRenderContext(); FontRenderContext frc = new FontRenderContext(f.getTransform(), paint.isAntiAliased(), f.usesFractionalMetrics()); GlyphVector gv = font.mFont.layoutGlyphVector(frc, text, start, limit, flag); int ng = gv.getNumGlyphs(); int[] ci = gv.getGlyphCharIndices(0, ng, null); for (int i = 0; i < ng; i++) { float adv = gv.getGlyphMetrics(i).getAdvanceX(); if (advances != null) { int adv_idx = advancesIndex + ci[i]; advances[adv_idx] += adv; } totalAdvance += adv; } if (draw && graphics != null) { graphics.drawGlyphVector(gv, x, y); } return x + totalAdvance; } // --- Static helper methods --- /* package */ static List<ScriptRun> getScriptRuns(char[] text, int start, int limit, boolean isRtl, List<FontInfo> fonts) { LinkedList<ScriptRun> scriptRuns = new LinkedList<ScriptRun>(); int count = limit - start; UScriptRun uScriptRun = new UScriptRun(text, start, count); while (uScriptRun.next()) { int scriptStart = uScriptRun.getScriptStart(); int scriptLimit = uScriptRun.getScriptLimit(); ScriptRun run = new ScriptRun(scriptStart, scriptLimit, isRtl); run.scriptCode = uScriptRun.getScriptCode(); setScriptFont(text, run, fonts); scriptRuns.add(run); } return scriptRuns; } // TODO: Replace this method with one which returns the font based on the scriptCode. private static void setScriptFont(char[] text, ScriptRun run, List<FontInfo> fonts) { for (FontInfo fontInfo : fonts) { if (fontInfo.mFont.canDisplayUpTo(text, run.start, run.limit) == -1) { run.font = fontInfo; return; } } run.font = fonts.get(0); } }
tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +8 −81 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import com.android.layoutlib.bridge.impl.GcSnapshot; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.Bitmap.Config; import android.graphics.Paint_Delegate.FontInfo; import android.text.TextUtils; import java.awt.Color; Loading @@ -35,7 +34,6 @@ import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Arc2D; import java.awt.image.BufferedImage; import java.util.List; /** Loading Loading @@ -978,7 +976,8 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void native_drawText(int nativeCanvas, final char[] text, final int index, final int count, final float startX, final float startY, int flags, int paint) { final float startX, final float startY, final int flags, int paint) { draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, new GcSnapshot.Drawable() { @Override Loading @@ -988,10 +987,10 @@ public final class Canvas_Delegate { // Paint.TextAlign indicates how the text is positioned relative to X. // LEFT is the default and there's nothing to do. float x = startX; float y = startY; int limit = index + count; boolean isRtl = flags == Canvas.DIRECTION_RTL; if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) { // TODO: check the value of bidiFlags. float m = paintDelegate.measureText(text, index, count, 0); float m = paintDelegate.measureText(text, index, count, isRtl); if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) { x -= m / 2; } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) { Loading @@ -999,87 +998,15 @@ public final class Canvas_Delegate { } } List<FontInfo> fonts = paintDelegate.getFonts(); if (fonts.size() > 0) { FontInfo mainFont = fonts.get(0); int i = index; int lastIndex = index + count; while (i < lastIndex) { // always start with the main font. int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); if (upTo == -1) { // draw all the rest and exit. graphics.setFont(mainFont.mFont); graphics.drawChars(text, i, lastIndex - i, (int)x, (int)y); return; } else if (upTo > 0) { // draw what's possible graphics.setFont(mainFont.mFont); graphics.drawChars(text, i, upTo - i, (int)x, (int)y); // compute the width that was drawn to increase x x += mainFont.mMetrics.charsWidth(text, i, upTo - i); // move index to the first non displayed char. i = upTo; // don't call continue at this point. Since it is certain the main font // cannot display the font a index upTo (now ==i), we move on to the // fallback fonts directly. } // no char supported, attempt to read the next char(s) with the // fallback font. In this case we only test the first character // and then go back to test with the main font. // Special test for 2-char characters. boolean foundFont = false; for (int f = 1 ; f < fonts.size() ; f++) { FontInfo fontInfo = fonts.get(f); // need to check that the font can display the character. We test // differently if the char is a high surrogate. int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); if (upTo == -1) { // draw that char graphics.setFont(fontInfo.mFont); graphics.drawChars(text, i, charCount, (int)x, (int)y); // update x x += fontInfo.mMetrics.charsWidth(text, i, charCount); // update the index in the text, and move on i += charCount; foundFont = true; break; } } // in case no font can display the char, display it with the main font. // (it'll put a square probably) if (foundFont == false) { int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; graphics.setFont(mainFont.mFont); graphics.drawChars(text, i, charCount, (int)x, (int)y); // measure it to advance x x += mainFont.mMetrics.charsWidth(text, i, charCount); // and move to the next chars. i += charCount; } } } new BidiRenderer(graphics, paintDelegate, text).renderText( index, limit, isRtl, null, 0, true, x, startY); } }); } @LayoutlibDelegate /*package*/ static void native_drawText(int nativeCanvas, String text, int start, int end, float x, float y, int flags, int paint) { int start, int end, float x, float y, final int flags, int paint) { int count = end - start; char[] buffer = TemporaryBuffer.obtain(count); TextUtils.getChars(text, start, end, buffer, 0); Loading
tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +30 −106 Original line number Diff line number Diff line Loading @@ -32,7 +32,6 @@ import java.awt.Stroke; import java.awt.Toolkit; import java.awt.font.FontRenderContext; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collections; import java.util.List; Loading Loading @@ -576,7 +575,7 @@ public class Paint_Delegate { return 0; } return delegate.measureText(text, index, count, bidiFlags); return delegate.measureText(text, index, count, isRtl(bidiFlags)); } @LayoutlibDelegate Loading Loading @@ -615,7 +614,7 @@ public class Paint_Delegate { } // measure from start to end float res = delegate.measureText(text, start, end - start + 1, bidiFlags); float res = delegate.measureText(text, start, end - start + 1, isRtl(bidiFlags)); if (measuredWidth != null) { measuredWidth[measureIndex] = res; Loading Loading @@ -980,51 +979,27 @@ public class Paint_Delegate { /*package*/ static float native_getTextRunAdvances(int native_object, char[] text, int index, int count, int contextIndex, int contextCount, int flags, float[] advances, int advancesIndex) { if (advances != null) for (int i = advancesIndex; i< advancesIndex+count; i++) advances[i]=0; // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); if (delegate == null) { if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) { return 0.f; } boolean isRtl = isRtl(flags); if (delegate.mFonts.size() > 0) { // FIXME: handle multi-char characters (see measureText) float totalAdvance = 0; for (int i = 0; i < count; i++) { char c = text[i + index]; boolean found = false; for (FontInfo info : delegate.mFonts) { if (info.mFont.canDisplay(c)) { float adv = info.mMetrics.charWidth(c); totalAdvance += adv; if (advances != null) { advances[i] = adv; } found = true; break; } } if (found == false) { // no advance for this char. if (advances != null) { advances[i] = 0.f; } } } return totalAdvance; } return 0; int limit = index + count; return new BidiRenderer(null, delegate, text).renderText( index, limit, isRtl, advances, advancesIndex, false, 0, 0); } @LayoutlibDelegate /*package*/ static float native_getTextRunAdvances(int native_object, String text, int start, int end, int contextStart, int contextEnd, int flags, float[] advances, int advancesIndex) { // FIXME: support contextStart, contextEnd and direction flag // FIXME: support contextStart and contextEnd int count = end - start; char[] buffer = TemporaryBuffer.obtain(count); TextUtils.getChars(text, start, end, buffer, 0); Loading Loading @@ -1080,19 +1055,12 @@ public class Paint_Delegate { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(nativePaint); if (delegate == null) { if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) { return; } // FIXME should test if the main font can display all those characters. // See MeasureText if (delegate.mFonts.size() > 0) { FontInfo mainInfo = delegate.mFonts.get(0); Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count, delegate.mFontContext); bounds.set(0, 0, (int) rect.getWidth(), (int) rect.getHeight()); } int w = (int) delegate.measureText(text, index, count, isRtl(bidiFlags)); int h= delegate.getFonts().get(0).mMetrics.getHeight(); bounds.set(0, 0, w, h); } @LayoutlibDelegate Loading Loading @@ -1176,6 +1144,7 @@ public class Paint_Delegate { info.mFont = info.mFont.deriveFont(new AffineTransform( mTextScaleX, mTextSkewX, 0, 1, 0, 0)); } // The metrics here don't have anti-aliasing set. info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont); infoList.add(info); Loading @@ -1185,64 +1154,9 @@ public class Paint_Delegate { } } /*package*/ float measureText(char[] text, int index, int count, int bidiFlags) { // TODO: find out what bidiFlags actually does. // WARNING: the logic in this method is similar to Canvas_Delegate.native_drawText // Any change to this method should be reflected there as well if (mFonts.size() > 0) { FontInfo mainFont = mFonts.get(0); int i = index; int lastIndex = index + count; float total = 0f; while (i < lastIndex) { // always start with the main font. int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); if (upTo == -1) { // shortcut to exit return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i); } else if (upTo > 0) { total += mainFont.mMetrics.charsWidth(text, i, upTo - i); i = upTo; // don't call continue at this point. Since it is certain the main font // cannot display the font a index upTo (now ==i), we move on to the // fallback fonts directly. } // no char supported, attempt to read the next char(s) with the // fallback font. In this case we only test the first character // and then go back to test with the main font. // Special test for 2-char characters. boolean foundFont = false; for (int f = 1 ; f < mFonts.size() ; f++) { FontInfo fontInfo = mFonts.get(f); // need to check that the font can display the character. We test // differently if the char is a high surrogate. int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); if (upTo == -1) { total += fontInfo.mMetrics.charsWidth(text, i, charCount); i += charCount; foundFont = true; break; } } // in case no font can display the char, measure it with the main font. if (foundFont == false) { int size = Character.isHighSurrogate(text[i]) ? 2 : 1; total += mainFont.mMetrics.charsWidth(text, i, size); i += size; } } return total; } return 0; /*package*/ float measureText(char[] text, int index, int count, boolean isRtl) { return new BidiRenderer(null, this, text).renderText( index, index + count, isRtl, null, 0, false, 0, 0); } private float getFontMetrics(FontMetrics metrics) { Loading Loading @@ -1281,4 +1195,14 @@ public class Paint_Delegate { } } private static boolean isRtl(int flag) { switch(flag) { case Paint.BIDI_RTL: case Paint.BIDI_FORCE_RTL: case Paint.BIDI_DEFAULT_RTL: return true; default: return false; } } }
tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java +27 −4 Original line number Diff line number Diff line Loading @@ -16,7 +16,10 @@ package android.text; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import com.ibm.icu.text.Bidi; /** Loading @@ -29,9 +32,29 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; public class AndroidBidi_Delegate { @LayoutlibDelegate /*package*/ static int runBidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo) { // return the equivalent of Layout.DIR_LEFT_TO_RIGHT // TODO: actually figure the direction. return 0; /*package*/ static int runBidi(int dir, char[] chars, byte[] charInfo, int count, boolean haveInfo) { switch (dir) { case 0: // Layout.DIR_REQUEST_LTR case 1: // Layout.DIR_REQUEST_RTL break; // No change. case -1: dir = Bidi.LEVEL_DEFAULT_LTR; break; case -2: dir = Bidi.LEVEL_DEFAULT_RTL; break; default: // Invalid code. Log error, assume LEVEL_DEFAULT_LTR and continue. Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Invalid direction flag", null); dir = Bidi.LEVEL_DEFAULT_LTR; } Bidi bidi = new Bidi(chars, 0, null, 0, count, dir); if (charInfo != null) { for (int i = 0; i < count; ++i) charInfo[i] = bidi.getLevelAt(i); } return bidi.getParaLevel(); } }
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java +20 −2 File changed.Preview size limit exceeded, changes collapsed. Show changes