Merge "Layoutlib fixes for L [DO NOT MERGE]" into lmp-preview-dev

This commit is contained in:
Deepanshu Gupta
2014-05-28 17:18:58 +00:00
committed by Android (Google) Code Review
14 changed files with 440 additions and 605 deletions

View File

@@ -30,7 +30,6 @@ public class FontFamily {
public long mNativePtr;
public FontFamily() {
mNativePtr = nCreateFamily();
mNativePtr = nCreateFamily();
if (mNativePtr == 0) {
throw new RuntimeException();

View File

@@ -66,6 +66,9 @@ public class Typeface {
static Map<String, Typeface> sSystemFontMap;
static FontFamily[] sFallbackFonts;
static final String SYSTEM_FONTS_CONFIG = "system_fonts.xml";
static final String FALLBACK_FONTS_CONFIG = "fallback_fonts.xml";
/**
* @hide
*/
@@ -249,10 +252,16 @@ public class Typeface {
return fontFamily;
}
static {
/*
* (non-Javadoc)
*
* This should only be called once, from the static class initializer block.
*/
private static void init() {
// Load font config and initialize Minikin state
String systemConfigFilename = "/system/etc/system_fonts.xml";
String configFilename = "/system/etc/fallback_fonts.xml";
File systemFontConfigLocation = getSystemFontConfigLocation();
File systemConfigFilename = new File(systemFontConfigLocation, SYSTEM_FONTS_CONFIG);
File configFilename = new File(systemFontConfigLocation, FALLBACK_FONTS_CONFIG);
try {
// TODO: throws an exception non-Minikin builds, to fail early;
// remove when Minikin-only
@@ -301,7 +310,10 @@ public class Typeface {
} catch (XmlPullParserException e) {
Log.e(TAG, "XML parse exception for " + configFilename);
}
}
static {
init();
// Set up defaults and typefaces exposed in public API
DEFAULT = create((String) null, 0);
DEFAULT_BOLD = create((String) null, Typeface.BOLD);
@@ -315,6 +327,11 @@ public class Typeface {
create((String) null, Typeface.ITALIC),
create((String) null, Typeface.BOLD_ITALIC),
};
}
private static File getSystemFontConfigLocation() {
return new File("/system/etc/");
}
@Override

View File

@@ -97,6 +97,13 @@ public class Resources_Theme_Delegate {
return found;
}
@LayoutlibDelegate
/*package*/ static TypedArray resolveAttributes(Resources thisResources, Theme thisTheme,
int[] values, int[] attrs) {
// FIXME
return null;
}
// ---- private helper methods ----
private static boolean setupResources(Theme thisTheme) {

View File

@@ -27,4 +27,10 @@ public class TypedArray_Delegate {
// pass
return false;
}
@LayoutlibDelegate
/*package*/ static TypedArray obtain(Resources res, int len) {
// FIXME
return null;
}
}

View File

@@ -18,9 +18,11 @@ package android.graphics;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
@@ -36,12 +38,12 @@ import android.graphics.Paint_Delegate.FontInfo;
@SuppressWarnings("deprecation")
public class BidiRenderer {
/* package */ static class ScriptRun {
/*package*/ static class ScriptRun {
int start;
int limit;
boolean isRtl;
int scriptCode;
FontInfo font;
Font font;
public ScriptRun(int start, int limit, boolean isRtl) {
this.start = start;
@@ -51,9 +53,10 @@ public class BidiRenderer {
}
}
private Graphics2D mGraphics;
private Paint_Delegate mPaint;
private final Graphics2D mGraphics;
private final Paint_Delegate mPaint;
private char[] mText;
private List<Font> mFonts;
// Bounds of the text drawn so far.
private RectF mBounds;
private float mBaseline;
@@ -63,11 +66,15 @@ public class BidiRenderer {
* @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) {
/*package*/ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
assert (paint != null);
mGraphics = graphics;
mPaint = paint;
mText = text;
mFonts = new ArrayList<Font>(paint.getFonts().size());
for (FontInfo fontInfo : paint.getFonts()) {
mFonts.add(fontInfo.mFont);
}
}
/**
@@ -94,7 +101,7 @@ public class BidiRenderer {
// the script runs.
mBounds = new RectF(x, y, x, y);
mBaseline = y;
for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mPaint.getFonts())) {
for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mFonts)) {
int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT;
flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT;
renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw);
@@ -108,16 +115,15 @@ public class BidiRenderer {
* much as possible. This also implements a fallback mechanism to render characters that cannot
* be drawn using the preferred font.
*/
private void renderScript(int start, int limit, FontInfo preferredFont, int flag,
private void renderScript(int start, int limit, Font preferredFont, int flag,
float[] advances, int advancesIndex, boolean draw) {
List<FontInfo> fonts = mPaint.getFonts();
if (fonts == null || preferredFont == null) {
if (mFonts.size() == 0 || preferredFont == null) {
return;
}
while (start < limit) {
boolean foundFont = false;
int canDisplayUpTo = preferredFont.mFont.canDisplayUpTo(mText, start, limit);
int canDisplayUpTo = preferredFont.canDisplayUpTo(mText, start, limit);
if (canDisplayUpTo == -1) {
// We can draw all characters in the text.
render(start, limit, preferredFont, flag, advances, advancesIndex, draw);
@@ -133,8 +139,8 @@ public class BidiRenderer {
// The current character cannot be drawn with the preferred font. Cycle through all the
// fonts to check which one can draw it.
int charCount = Character.isHighSurrogate(mText[start]) ? 2 : 1;
for (FontInfo font : fonts) {
canDisplayUpTo = font.mFont.canDisplayUpTo(mText, start, start + charCount);
for (Font font : mFonts) {
canDisplayUpTo = font.canDisplayUpTo(mText, start, start + charCount);
if (canDisplayUpTo == -1) {
render(start, start+charCount, font, flag, advances, advancesIndex, draw);
start += charCount;
@@ -160,15 +166,19 @@ public class BidiRenderer {
* Renders the text to the right of the bounds with the given font.
* @param font The font to render the text with.
*/
private void render(int start, int limit, FontInfo font, int flag, float[] advances,
private void render(int start, int limit, Font font, int flag, float[] advances,
int advancesIndex, boolean draw) {
// 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(), mPaint.isAntiAliased(),
f.usesFractionalMetrics());
GlyphVector gv = font.mFont.layoutGlyphVector(frc, mText, start, limit, flag);
FontRenderContext frc;
if (mGraphics != null) {
frc = mGraphics.getFontRenderContext();
} else {
frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
// Metrics obtained this way don't have anti-aliasing set. So,
// we create a new FontRenderContext with anti-aliasing set.
frc = new FontRenderContext(font.getTransform(), mPaint.isAntiAliased(), frc.usesFractionalMetrics());
}
GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag);
int ng = gv.getNumGlyphs();
int[] ci = gv.getGlyphCharIndices(0, ng, null);
if (advances != null) {
@@ -206,7 +216,7 @@ public class BidiRenderer {
}
/* package */ static List<ScriptRun> getScriptRuns(char[] text, int start, int limit,
boolean isRtl, List<FontInfo> fonts) {
boolean isRtl, List<Font> fonts) {
LinkedList<ScriptRun> scriptRuns = new LinkedList<ScriptRun>();
int count = limit - start;
@@ -225,10 +235,10 @@ public class BidiRenderer {
// 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;
List<Font> fonts) {
for (Font font : fonts) {
if (font.canDisplayUpTo(text, run.start, run.limit) == -1) {
run.font = font;
return;
}
}

View File

@@ -979,7 +979,6 @@ public final class Canvas_Delegate {
final float startX, final float startY, final int flags, long paint,
long typeface) {
// TODO: use typeface.
draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
new GcSnapshot.Drawable() {
@Override
@@ -1097,7 +1096,7 @@ public final class Canvas_Delegate {
/**
* Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
* <p>Note that the drawable may actually be executed several times if there are
* layers involved (see {@link #saveLayer(RectF, int, int)}.
* layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
*/
private static void draw(long nCanvas, long nPaint, boolean compositeOnly, boolean forceSrcMode,
GcSnapshot.Drawable drawable) {
@@ -1117,7 +1116,7 @@ public final class Canvas_Delegate {
* Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
* to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
* <p>Note that the drawable may actually be executed several times if there are
* layers involved (see {@link #saveLayer(RectF, int, int)}.
* layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
*/
private static void draw(long nCanvas, GcSnapshot.Drawable drawable) {
// get the delegate from the native int.
@@ -1190,12 +1189,6 @@ public final class Canvas_Delegate {
return mSnapshot.clipRect(left, top, right, bottom, regionOp);
}
private void setBitmap(Bitmap_Delegate bitmap) {
mBitmap = bitmap;
assert mSnapshot.size() == 1;
mSnapshot.setBitmap(mBitmap);
}
private static void drawBitmap(
long nativeCanvas,
Bitmap_Delegate bitmap,

View File

@@ -0,0 +1,225 @@
/*
* Copyright (C) 2014 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 com.android.ide.common.rendering.api.LayoutLog;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import java.awt.Font;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import static android.graphics.Typeface_Delegate.SYSTEM_FONTS;
/**
* Delegate implementing the native methods of android.graphics.FontFamily
*
* Through the layoutlib_create tool, the original native methods of FontFamily have been replaced
* by calls to methods of the same name in this delegate class.
*
* This class behaves like the original native implementation, but in Java, keeping previously
* native data into its own objects and mapping them to int that are sent back and forth between
* it and the original FontFamily class.
*
* @see DelegateManager
*/
public class FontFamily_Delegate {
// FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked
// separately.
private static final String FONT_SUFFIX_BOLDITALIC = "BoldItalic.ttf";
private static final String FONT_SUFFIX_BOLD = "Bold.ttf";
private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
private static final String FONT_SUBSTRING_COMPACT = "UI";
/**
* A class associating {@link Font} with its metadata.
*/
private static final class FontInfo {
Font mFont;
/** Regular, Bold, Italic, or BoldItalic. */
int mStyle;
/**
* The variant of the Font - compact or elegant.
* @see Paint#setElegantTextHeight(boolean)
*/
boolean mIsCompact;
}
// ---- delegate manager ----
private static final DelegateManager<FontFamily_Delegate> sManager =
new DelegateManager<FontFamily_Delegate>(FontFamily_Delegate.class);
// ---- delegate helper data ----
private static String sFontLocation;
private static final List<FontFamily_Delegate> sPostInitDelegate = new
ArrayList<FontFamily_Delegate>();
// ---- delegate data ----
private List<FontInfo> mFonts = new ArrayList<FontInfo>();
// Path of fonts that haven't been created since sFontLoader hasn't been initialized.
private List<String> mPath = new ArrayList<String>();
// ---- Public Helper methods ----
public static FontFamily_Delegate getDelegate(long nativeFontFamily) {
return sManager.getDelegate(nativeFontFamily);
}
public static synchronized void setFontLocation(String fontLocation) {
sFontLocation = fontLocation;
for (FontFamily_Delegate fontFamily : sPostInitDelegate) {
fontFamily.init();
}
sPostInitDelegate.clear();
}
public Font getFont(int style, boolean isCompact) {
FontInfo plainFont = null;
FontInfo styledFont = null; // Font matching the style but not isCompact
for (FontInfo font : mFonts) {
if (font.mStyle == style) {
if (font.mIsCompact == isCompact) {
return font.mFont;
}
styledFont = font;
}
if (font.mStyle == Font.PLAIN) {
if (plainFont == null) {
plainFont = font;
continue;
}
if (font.mIsCompact == isCompact) {
// Override the previous selection of plain font since we've found a better one.
plainFont = font;
}
}
}
if (styledFont != null) {
return styledFont.mFont;
}
// No font with the mentioned style is found. Try to derive one.
if (plainFont != null && style > 0 && style < 4) {
styledFont = new FontInfo();
styledFont.mFont = plainFont.mFont.deriveFont(style);
styledFont.mStyle = style;
styledFont.mIsCompact = plainFont.mIsCompact;
// Add the font to the list of fonts so that we don't have to derive it the next time.
mFonts.add(styledFont);
return styledFont.mFont;
}
return null;
}
// ---- native methods ----
@LayoutlibDelegate
/*package*/ static long nCreateFamily() {
FontFamily_Delegate delegate = new FontFamily_Delegate();
if (sFontLocation != null) {
delegate.init();
} else {
sPostInitDelegate.add(delegate);
}
return sManager.addNewDelegate(delegate);
}
@LayoutlibDelegate
/*package*/ static void nUnrefFamily(long nativePtr) {
sManager.removeJavaReferenceFor(nativePtr);
}
@LayoutlibDelegate
/*package*/ static boolean nAddFont(long nativeFamily, String path) {
FontFamily_Delegate delegate = getDelegate(nativeFamily);
if (delegate != null) {
if (sFontLocation == null) {
delegate.mPath.add(path);
return true;
}
return delegate.addFont(path);
}
return false;
}
private void init() {
for (String path : mPath) {
addFont(path);
}
mPath = null;
}
private boolean addFont(String path) {
Font font = loadFont(path);
if (font == null) {
return false;
}
FontInfo fontInfo = new FontInfo();
fontInfo.mFont = font;
addFontMetadata(fontInfo, path);
// TODO ensure that mFonts doesn't have the font with this style already.
mFonts.add(fontInfo);
return true;
}
private static void addFontMetadata(FontInfo fontInfo, String path) {
int style = Font.PLAIN;
String fontName = path.substring(path.lastIndexOf('/'), path.length());
if (fontName.endsWith(FONT_SUFFIX_BOLDITALIC)) {
style = Font.BOLD | Font.ITALIC;
} else if (fontName.endsWith(FONT_SUFFIX_BOLD)) {
style = Font.BOLD;
} else if (fontName.endsWith(FONT_SUFFIX_ITALIC)) {
style = Font.ITALIC;
}
fontInfo.mStyle = style;
// Names of compact fonts end with UI-<style>.ttf. For example, NotoNakshUI-Regular.ttf.
// This should go away when this info is passed on by nAddFont().
int hyphenIndex = fontName.lastIndexOf('-');
fontInfo.mIsCompact = hyphenIndex > 0 &&
fontName.substring(0, hyphenIndex).endsWith(FONT_SUBSTRING_COMPACT);
}
private static Font loadFont(String path) {
if (path.startsWith(SYSTEM_FONTS) ) {
String relativePath = path.substring(SYSTEM_FONTS.length());
File f = new File(sFontLocation, relativePath);
try {
return Font.createFont(Font.TRUETYPE_FONT, f);
} catch (Exception e) {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
String.format("Unable to load font %1$s", relativePath),
null /*throwable*/, null /*data*/);
}
} else {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
"Only platform fonts located in " + SYSTEM_FONTS + "can be loaded.",
null /*throwable*/, null /*data*/);
}
return null;
}
}

View File

@@ -202,6 +202,16 @@ public final class Matrix_Delegate {
return d.isIdentity();
}
@LayoutlibDelegate
/*package*/ static boolean native_isAffine(long native_object) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
return true;
}
return (d.computeTypeMask() & kPerspective_Mask) == 0;
}
@LayoutlibDelegate
/*package*/ static boolean native_rectStaysRect(long native_object) {
Matrix_Delegate d = sManager.getDelegate(native_object);

View File

@@ -53,7 +53,7 @@ import java.util.Locale;
public class Paint_Delegate {
/**
* Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
* Class associating a {@link Font} and its {@link java.awt.FontMetrics}.
*/
/*package*/ static final class FontInfo {
Font mFont;
@@ -66,8 +66,6 @@ public class Paint_Delegate {
// ---- delegate helper data ----
private List<FontInfo> mFonts;
private final FontRenderContext mFontContext = new FontRenderContext(
new AffineTransform(), true, true);
// ---- delegate data ----
private int mFlags;
@@ -83,6 +81,7 @@ public class Paint_Delegate {
private float mTextScaleX;
private float mTextSkewX;
private int mHintingMode = Paint.HINTING_ON;
private boolean mIsCompact = true;
private Xfermode_Delegate mXfermode;
private ColorFilter_Delegate mColorFilter;
@@ -101,8 +100,7 @@ public class Paint_Delegate {
}
/**
* Returns the list of {@link Font} objects. The first item is the main font, the rest
* are fall backs for characters not present in the main font.
* Returns the list of {@link Font} objects.
*/
public List<FontInfo> getFonts() {
return mFonts;
@@ -437,12 +435,20 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static boolean isElegantTextHeight(Paint thisPaint) {
return false;
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
return delegate != null && !delegate.mIsCompact;
}
@LayoutlibDelegate
/*package*/ static void setElegantTextHeight(Paint thisPaint, boolean elegant) {
// TODO
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
if (delegate == null) {
return;
}
delegate.mIsCompact = !elegant;
}
@LayoutlibDelegate
@@ -621,7 +627,6 @@ public class Paint_Delegate {
int inc = count > 0 ? 1 : -1;
int measureIndex = 0;
float measureAcc = 0;
for (int i = index; i != index + count; i += inc, measureIndex++) {
int start, end;
if (i < index) {
@@ -640,7 +645,6 @@ public class Paint_Delegate {
measuredWidth[measureIndex] = res;
}
measureAcc += res;
if (res > maxWidth) {
// we should not return this char index, but since it's 0-based
// and we need to return a count, we simply return measureIndex;
@@ -818,7 +822,7 @@ public class Paint_Delegate {
return filter;
}
delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);;
delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);
// since none of those are supported, display a fidelity warning right away
if (delegate.mColorFilter != null && delegate.mColorFilter.isSupported() == false) {
@@ -940,52 +944,17 @@ public class Paint_Delegate {
}
@LayoutlibDelegate
/*package*/ static int native_getTextWidths(long native_object, char[] text, int index,
int count, int bidiFlags, float[] widths) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(native_object);
if (delegate == null) {
return 0;
}
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 (widths != null) {
widths[i] = adv;
}
found = true;
break;
}
}
if (found == false) {
// no advance for this char.
if (widths != null) {
widths[i] = 0.f;
}
}
}
return (int) totalAdvance;
}
return 0;
/*package*/ static int native_getTextWidths(long native_object, long native_typeface,
char[] text, int index, int count, int bidiFlags, float[] widths) {
return (int) native_getTextRunAdvances(native_object, native_typeface, text, index, count,
index, count, bidiFlags, widths, 0);
}
@LayoutlibDelegate
/*package*/ static int native_getTextWidths(long native_object, String text, int start,
int end, int bidiFlags, float[] widths) {
return native_getTextWidths(native_object, text.toCharArray(), start, end - start,
bidiFlags, widths);
/*package*/ static int native_getTextWidths(long native_object, long native_typeface,
String text, int start, int end, int bidiFlags, float[] widths) {
return native_getTextWidths(native_object, native_typeface, text.toCharArray(), start,
end - start, bidiFlags, widths);
}
@LayoutlibDelegate
@@ -997,15 +966,20 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static float native_getTextRunAdvances(long native_object,
long native_typeface /*ignored*/,
char[] text, int index, int count, int contextIndex, int contextCount,
int flags, float[] advances, int advancesIndex) {
// native_typeface is passed here since Framework's old implementation did not have the
// typeface object associated with the Paint. Since, we follow the new framework way,
// we store the typeface with the paint and use it directly.
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 || delegate.mFonts == null || delegate.mFonts.size() == 0) {
if (delegate == null) {
return 0.f;
}
boolean isRtl = isRtl(flags);
@@ -1017,7 +991,7 @@ public class Paint_Delegate {
}
@LayoutlibDelegate
/*package*/ static float native_getTextRunAdvances(long native_object,
/*package*/ static float native_getTextRunAdvances(long native_object, long native_typeface,
String text, int start, int end, int contextStart, int contextEnd,
int flags, float[] advances, int advancesIndex) {
// FIXME: support contextStart and contextEnd
@@ -1025,8 +999,8 @@ public class Paint_Delegate {
char[] buffer = TemporaryBuffer.obtain(count);
TextUtils.getChars(text, start, end, buffer, 0);
return native_getTextRunAdvances(native_object, buffer, 0, count, contextStart,
contextEnd - contextStart, flags, advances, advancesIndex);
return native_getTextRunAdvances(native_object, native_typeface, buffer, 0, count,
contextStart, contextEnd - contextStart, flags, advances, advancesIndex);
}
@LayoutlibDelegate
@@ -1076,7 +1050,7 @@ public class Paint_Delegate {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) {
if (delegate == null) {
return;
}
delegate.measureText(text, index, count, isRtl(bidiFlags)).roundOut(bounds);
@@ -1150,7 +1124,7 @@ public class Paint_Delegate {
private void updateFontObject() {
if (mTypeface != null) {
// Get the fonts from the TypeFace object.
List<Font> fonts = mTypeface.getFonts();
List<Font> fonts = mTypeface.getFonts(mIsCompact);
// create new font objects as well as FontMetrics, based on the current text size
// and skew info.

View File

@@ -19,7 +19,6 @@ package android.graphics;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.layoutlib.bridge.impl.FontLoader;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import android.content.res.AssetManager;
@@ -44,100 +43,67 @@ import java.util.List;
*/
public final class Typeface_Delegate {
private static final String SYSTEM_FONTS = "/system/fonts/";
public static final String SYSTEM_FONTS = "/system/fonts/";
// ---- delegate manager ----
private static final DelegateManager<Typeface_Delegate> sManager =
new DelegateManager<Typeface_Delegate>(Typeface_Delegate.class);
// ---- delegate helper data ----
private static final String DEFAULT_FAMILY = "sans-serif";
private static FontLoader sFontLoader;
private static final List<Typeface_Delegate> sPostInitDelegate =
new ArrayList<Typeface_Delegate>();
private static String sFontLocation;
// ---- delegate data ----
private final String mFamily;
private final long[] mFontFamilies; // the reference to FontFamily_Delegate.
private int mStyle;
private List<Font> mFonts;
private static long sDefaultTypeface;
// ---- Public Helper methods ----
public static synchronized void init(FontLoader fontLoader) {
sFontLoader = fontLoader;
for (Typeface_Delegate delegate : sPostInitDelegate) {
delegate.init();
}
sPostInitDelegate.clear();
public static synchronized void setFontLocation(String fontLocation) {
sFontLocation = fontLocation;
FontFamily_Delegate.setFontLocation(fontLocation);
}
public static Typeface_Delegate getDelegate(long nativeTypeface) {
return sManager.getDelegate(nativeTypeface);
}
public static List<Font> getFonts(Typeface typeface) {
return getFonts(typeface.native_instance);
}
public static List<Font> getFonts(long native_int) {
Typeface_Delegate delegate = sManager.getDelegate(native_int);
if (delegate == null) {
return null;
public List<Font> getFonts(boolean compact) {
List<Font> fonts = new ArrayList<Font>(mFontFamilies.length);
for (long fontFamily : mFontFamilies) {
FontFamily_Delegate ffd = FontFamily_Delegate.getDelegate(fontFamily);
if (ffd != null) {
Font font = ffd.getFont(mStyle, compact);
if (font != null) {
fonts.add(font);
}
}
}
return delegate.getFonts();
}
public List<Font> getFonts() {
return mFonts;
return fonts;
}
// ---- native methods ----
@LayoutlibDelegate
/*package*/ static synchronized long nativeCreate(String familyName, int style) {
if (familyName == null) {
familyName = DEFAULT_FAMILY;
}
if (style < 0) {
style = Typeface.NORMAL;
}
Typeface_Delegate newDelegate = new Typeface_Delegate(familyName, style);
if (sFontLoader != null) {
newDelegate.init();
} else {
// font loader has not been initialized yet, add the delegate to a list of delegates
// to init when the font loader is initialized.
// There won't be any rendering before this happens anyway.
sPostInitDelegate.add(newDelegate);
}
return sManager.addNewDelegate(newDelegate);
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
"Could not find font with family \"" + familyName + "\".",
null /*throwable*/, null /*data*/);
return 0;
}
@LayoutlibDelegate
/*package*/ static synchronized long nativeCreateFromTypeface(long native_instance, int style) {
Typeface_Delegate delegate = sManager.getDelegate(native_instance);
if (delegate == null) {
delegate = sManager.getDelegate(sDefaultTypeface);
}
if (delegate == null) {
return 0;
}
Typeface_Delegate newDelegate = new Typeface_Delegate(delegate.mFamily, style);
if (sFontLoader != null) {
newDelegate.init();
} else {
// font loader has not been initialized yet, add the delegate to a list of delegates
// to init when the font loader is initialized.
// There won't be any rendering before this happens anyway.
sPostInitDelegate.add(newDelegate);
}
return sManager.addNewDelegate(newDelegate);
return sManager.addNewDelegate(new Typeface_Delegate(delegate.mFontFamilies, style));
}
@LayoutlibDelegate
@@ -149,31 +115,15 @@ public final class Typeface_Delegate {
@LayoutlibDelegate
/*package*/ static synchronized long nativeCreateFromFile(String path) {
if (path.startsWith(SYSTEM_FONTS) ) {
String relativePath = path.substring(SYSTEM_FONTS.length());
File f = new File(sFontLoader.getOsFontsLocation(), relativePath);
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
"Typeface.createFromFile() is not supported.,", null, null);
return 0;
}
try {
Font font = Font.createFont(Font.TRUETYPE_FONT, f);
if (font != null) {
Typeface_Delegate newDelegate = new Typeface_Delegate(font);
return sManager.addNewDelegate(newDelegate);
}
} catch (Exception e) {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
String.format("Unable to load font %1$s", relativePath),
null /*throwable*/, null /*data*/);
}
} else {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
"Typeface.createFromFile() can only work with platform fonts located in " +
SYSTEM_FONTS,
null /*throwable*/, null /*data*/);
}
// return a copy of the base font
return nativeCreate(null, 0);
@LayoutlibDelegate
/*package*/ static synchronized long nativeCreateFromArray(long[] familyArray) {
Typeface_Delegate delegate = new Typeface_Delegate(familyArray, Typeface.NORMAL);
return sManager.addNewDelegate(delegate);
}
@LayoutlibDelegate
@@ -191,24 +141,21 @@ public final class Typeface_Delegate {
return delegate.mStyle;
}
@LayoutlibDelegate
/*package*/ static void nativeSetDefault(long native_instance) {
sDefaultTypeface = native_instance;
}
@LayoutlibDelegate
/*package*/ static File getSystemFontConfigLocation() {
return new File(sFontLocation);
}
// ---- Private delegate/helper methods ----
private Typeface_Delegate(String family, int style) {
mFamily = family;
private Typeface_Delegate(long[] fontFamilies, int style) {
mFontFamilies = fontFamilies;
mStyle = style;
}
private Typeface_Delegate(Font font) {
mFamily = font.getFamily();
mStyle = Typeface.NORMAL;
mFonts = sFontLoader.getFallbackFonts(mStyle);
// insert the font glyph first.
mFonts.add(0, font);
}
private void init() {
mFonts = sFontLoader.getFont(mFamily, mStyle);
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2014 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.util;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import org.kxml2.io.KXmlParser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
/**
* Delegate overriding some methods of android.util.Xml
*
* Through the layoutlib_create tool, the original methods of Xml have been replaced
* by calls to methods of the same name in this delegate class.
*
* Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
* around to map int to instance of the delegate.
*/
public class Xml_Delegate {
@LayoutlibDelegate
/*package*/ static XmlPullParser newPullParser() {
try {
KXmlParser parser = new KXmlParser();
// The prebuilt kxml2 library with the IDE doesn't support DOCECL.
// parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true);
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
return parser;
} catch (XmlPullParserException e) {
throw new AssertionError();
}
}
}

View File

@@ -26,7 +26,6 @@ import com.android.ide.common.rendering.api.RenderSession;
import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.Result.Status;
import com.android.ide.common.rendering.api.SessionParams;
import com.android.layoutlib.bridge.impl.FontLoader;
import com.android.layoutlib.bridge.impl.RenderDrawable;
import com.android.layoutlib.bridge.impl.RenderSessionImpl;
import com.android.layoutlib.bridge.util.DynamicIdMap;
@@ -61,7 +60,7 @@ import java.util.concurrent.locks.ReentrantLock;
/**
* Main entry point of the LayoutLib Bridge.
* <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call
* {@link #createScene(SceneParams)}
* {@link #createSession(SessionParams)}
*/
public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
@@ -147,8 +146,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
if (getClass() != obj.getClass()) return false;
IntArray other = (IntArray) obj;
if (!Arrays.equals(mArray, other.mArray)) return false;
return true;
return Arrays.equals(mArray, other.mArray);
}
}
@@ -251,14 +249,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
}
// load the fonts.
FontLoader fontLoader = FontLoader.create(fontLocation.getAbsolutePath());
if (fontLoader != null) {
Typeface_Delegate.init(fontLoader);
} else {
log.error(LayoutLog.TAG_BROKEN,
"Failed create FontLoader in layout lib.", null);
return false;
}
Typeface_Delegate.setFontLocation(fontLocation.getAbsolutePath());
// now parse com.android.internal.R (and only this one as android.R is a subset of
// the internal version), and put the content in the maps.

View File

@@ -1,398 +0,0 @@
/*
* Copyright (C) 2008 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 com.android.layoutlib.bridge.impl;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import android.graphics.Typeface;
import java.awt.Font;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
/**
* Provides {@link Font} object to the layout lib.
* <p/>
* The fonts are loaded from the SDK directory. Family/style mapping is done by parsing the
* fonts.xml file located alongside the ttf files.
*/
public final class FontLoader {
private static final String FONTS_SYSTEM = "system_fonts.xml";
private static final String FONTS_VENDOR = "vendor_fonts.xml";
private static final String FONTS_FALLBACK = "fallback_fonts.xml";
private static final String NODE_FAMILYSET = "familyset";
private static final String NODE_FAMILY = "family";
private static final String NODE_NAME = "name";
private static final String NODE_FILE = "file";
private static final String ATTRIBUTE_VARIANT = "variant";
private static final String ATTRIBUTE_VALUE_ELEGANT = "elegant";
private static final String FONT_SUFFIX_NONE = ".ttf";
private static final String FONT_SUFFIX_REGULAR = "-Regular.ttf";
private static final String FONT_SUFFIX_BOLD = "-Bold.ttf";
// FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked
// separately.
private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
private static final String FONT_SUFFIX_BOLDITALIC = "-BoldItalic.ttf";
// This must match the values of Typeface styles so that we can use them for indices in this
// array.
private static final int[] AWT_STYLES = new int[] {
Font.PLAIN,
Font.BOLD,
Font.ITALIC,
Font.BOLD | Font.ITALIC
};
private static int[] DERIVE_BOLD_ITALIC = new int[] {
Typeface.ITALIC, Typeface.BOLD, Typeface.NORMAL
};
private static int[] DERIVE_ITALIC = new int[] { Typeface.NORMAL };
private static int[] DERIVE_BOLD = new int[] { Typeface.NORMAL };
private static final List<FontInfo> mMainFonts = new ArrayList<FontInfo>();
private static final List<FontInfo> mFallbackFonts = new ArrayList<FontInfo>();
private final String mOsFontsLocation;
public static FontLoader create(String fontOsLocation) {
try {
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
parserFactory.setNamespaceAware(true);
// parse the system fonts
FontHandler handler = parseFontFile(parserFactory, fontOsLocation, FONTS_SYSTEM);
List<FontInfo> systemFonts = handler.getFontList();
// parse the fallback fonts
handler = parseFontFile(parserFactory, fontOsLocation, FONTS_FALLBACK);
List<FontInfo> fallbackFonts = handler.getFontList();
return new FontLoader(fontOsLocation, systemFonts, fallbackFonts);
} catch (ParserConfigurationException e) {
// return null below
} catch (SAXException e) {
// return null below
} catch (FileNotFoundException e) {
// return null below
} catch (IOException e) {
// return null below
}
return null;
}
private static FontHandler parseFontFile(SAXParserFactory parserFactory,
String fontOsLocation, String fontFileName)
throws ParserConfigurationException, SAXException, IOException, FileNotFoundException {
SAXParser parser = parserFactory.newSAXParser();
File f = new File(fontOsLocation, fontFileName);
FontHandler definitionParser = new FontHandler(
fontOsLocation + File.separator);
parser.parse(new FileInputStream(f), definitionParser);
return definitionParser;
}
private FontLoader(String fontOsLocation,
List<FontInfo> fontList, List<FontInfo> fallBackList) {
mOsFontsLocation = fontOsLocation;
mMainFonts.addAll(fontList);
mFallbackFonts.addAll(fallBackList);
}
public String getOsFontsLocation() {
return mOsFontsLocation;
}
/**
* Returns a {@link Font} object given a family name and a style value (constant in
* {@link Typeface}).
* @param family the family name
* @param style a 1-item array containing the requested style. Based on the font being read
* the actual style may be different. The array contains the actual style after
* the method returns.
* @return the font object or null if no match could be found.
*/
public synchronized List<Font> getFont(String family, int style) {
List<Font> result = new ArrayList<Font>();
if (family == null) {
return result;
}
// get the font objects from the main list based on family.
for (FontInfo info : mMainFonts) {
if (info.families.contains(family)) {
result.add(info.font[style]);
break;
}
}
// add all the fallback fonts for the given style
for (FontInfo info : mFallbackFonts) {
result.add(info.font[style]);
}
return result;
}
public synchronized List<Font> getFallbackFonts(int style) {
List<Font> result = new ArrayList<Font>();
// add all the fallback fonts
for (FontInfo info : mFallbackFonts) {
result.add(info.font[style]);
}
return result;
}
private final static class FontInfo {
final Font[] font = new Font[4]; // Matches the 4 type-face styles.
final Set<String> families;
FontInfo() {
families = new HashSet<String>();
}
}
private final static class FontHandler extends DefaultHandler {
private final String mOsFontsLocation;
private FontInfo mFontInfo = null;
private final StringBuilder mBuilder = new StringBuilder();
private List<FontInfo> mFontList = new ArrayList<FontInfo>();
private boolean isCompactFont = true;
private FontHandler(String osFontsLocation) {
super();
mOsFontsLocation = osFontsLocation;
}
public List<FontInfo> getFontList() {
return mFontList;
}
/* (non-Javadoc)
* @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
*/
@Override
public void startElement(String uri, String localName, String name, Attributes attributes)
throws SAXException {
if (NODE_FAMILYSET.equals(localName)) {
mFontList = new ArrayList<FontInfo>();
} else if (NODE_FAMILY.equals(localName)) {
if (mFontList != null) {
mFontInfo = null;
}
} else if (NODE_NAME.equals(localName)) {
if (mFontList != null && mFontInfo == null) {
mFontInfo = new FontInfo();
}
} else if (NODE_FILE.equals(localName)) {
if (mFontList != null && mFontInfo == null) {
mFontInfo = new FontInfo();
}
if (ATTRIBUTE_VALUE_ELEGANT.equals(attributes.getValue(ATTRIBUTE_VARIANT))) {
isCompactFont = false;
} else {
isCompactFont = true;
}
}
mBuilder.setLength(0);
super.startElement(uri, localName, name, attributes);
}
/* (non-Javadoc)
* @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
*/
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (isCompactFont) {
mBuilder.append(ch, start, length);
}
}
/* (non-Javadoc)
* @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
*/
@Override
public void endElement(String uri, String localName, String name) throws SAXException {
if (NODE_FAMILY.equals(localName)) {
if (mFontInfo != null) {
// if has a normal font file, add to the list
if (mFontInfo.font[Typeface.NORMAL] != null) {
mFontList.add(mFontInfo);
// create missing font styles, order is important.
if (mFontInfo.font[Typeface.BOLD_ITALIC] == null) {
computeDerivedFont(Typeface.BOLD_ITALIC, DERIVE_BOLD_ITALIC);
}
if (mFontInfo.font[Typeface.ITALIC] == null) {
computeDerivedFont(Typeface.ITALIC, DERIVE_ITALIC);
}
if (mFontInfo.font[Typeface.BOLD] == null) {
computeDerivedFont(Typeface.BOLD, DERIVE_BOLD);
}
}
mFontInfo = null;
}
} else if (NODE_NAME.equals(localName)) {
// handle a new name for an existing Font Info
if (mFontInfo != null) {
String family = trimXmlWhitespaces(mBuilder.toString());
mFontInfo.families.add(family);
}
} else if (NODE_FILE.equals(localName)) {
// handle a new file for an existing Font Info
if (isCompactFont && mFontInfo != null) {
String fileName = trimXmlWhitespaces(mBuilder.toString());
Font font = getFont(fileName);
if (font != null) {
if (fileName.endsWith(FONT_SUFFIX_REGULAR)) {
mFontInfo.font[Typeface.NORMAL] = font;
} else if (fileName.endsWith(FONT_SUFFIX_BOLD)) {
mFontInfo.font[Typeface.BOLD] = font;
} else if (fileName.endsWith(FONT_SUFFIX_BOLDITALIC)) {
mFontInfo.font[Typeface.BOLD_ITALIC] = font;
} else if (fileName.endsWith(FONT_SUFFIX_ITALIC)) {
mFontInfo.font[Typeface.ITALIC] = font;
} else if (fileName.endsWith(FONT_SUFFIX_NONE)) {
mFontInfo.font[Typeface.NORMAL] = font;
}
}
}
}
}
private Font getFont(String fileName) {
try {
File file = new File(mOsFontsLocation, fileName);
if (file.exists()) {
return Font.createFont(Font.TRUETYPE_FONT, file);
}
} catch (Exception e) {
}
return null;
}
private void computeDerivedFont( int toCompute, int[] basedOnList) {
for (int basedOn : basedOnList) {
if (mFontInfo.font[basedOn] != null) {
mFontInfo.font[toCompute] =
mFontInfo.font[basedOn].deriveFont(AWT_STYLES[toCompute]);
return;
}
}
// we really shouldn't stop there. This means we don't have a NORMAL font...
assert false;
}
private String trimXmlWhitespaces(String value) {
if (value == null) {
return null;
}
// look for carriage return and replace all whitespace around it by just 1 space.
int index;
while ((index = value.indexOf('\n')) != -1) {
// look for whitespace on each side
int left = index - 1;
while (left >= 0) {
if (Character.isWhitespace(value.charAt(left))) {
left--;
} else {
break;
}
}
int right = index + 1;
int count = value.length();
while (right < count) {
if (Character.isWhitespace(value.charAt(right))) {
right++;
} else {
break;
}
}
// remove all between left and right (non inclusive) and replace by a single space.
String leftString = null;
if (left >= 0) {
leftString = value.substring(0, left + 1);
}
String rightString = null;
if (right < count) {
rightString = value.substring(right);
}
if (leftString != null) {
value = leftString;
if (rightString != null) {
value += " " + rightString;
}
} else {
value = rightString != null ? rightString : "";
}
}
// now we un-escape the string
int length = value.length();
char[] buffer = value.toCharArray();
for (int i = 0 ; i < length ; i++) {
if (buffer[i] == '\\') {
if (buffer[i+1] == 'n') {
// replace the char with \n
buffer[i+1] = '\n';
}
// offset the rest of the buffer since we go from 2 to 1 char
System.arraycopy(buffer, i+1, buffer, i, length - i - 1);
length--;
}
}
return new String(buffer, 0, length);
}
}
}

View File

@@ -125,15 +125,19 @@ public final class CreateInfo implements ICreateInfo {
"android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
"android.content.res.Resources$Theme#obtainStyledAttributes",
"android.content.res.Resources$Theme#resolveAttribute",
"android.content.res.Resources$Theme#resolveAttributes",
"android.content.res.Resources#localeToLanguageTag",
"android.content.res.AssetManager#newTheme",
"android.content.res.AssetManager#deleteTheme",
"android.content.res.AssetManager#applyThemeStyle",
"android.content.res.TypedArray#getValueAt",
"android.content.res.TypedArray#obtain",
"android.graphics.BitmapFactory#finishDecode",
"android.graphics.Typeface#getSystemFontConfigLocation",
"android.os.Handler#sendMessageAtTime",
"android.os.HandlerThread#run",
"android.text.format.DateFormat#is24HourFormat",
"android.util.Xml#newPullParser",
"android.view.Choreographer#getRefreshRate",
"android.view.Display#updateDisplayInfoLocked",
"android.view.Display#getWindowManager",
@@ -170,6 +174,7 @@ public final class CreateInfo implements ICreateInfo {
"android.graphics.DiscretePathEffect",
"android.graphics.DrawFilter",
"android.graphics.EmbossMaskFilter",
"android.graphics.FontFamily",
"android.graphics.LayerRasterizer",
"android.graphics.LightingColorFilter",
"android.graphics.LinearGradient",