Reduce the amount of allocations on Paint_Delegate

When the text properties of the Paint object are updated, or when .set()
is called, updateFontObject will be called. This method is currently at the
top of the methods for number of allocations.
This CL reduces the number of calls to updateFontObject by a 90%,
reducing the number of Font allocations.

Change-Id: I3e232f9e6e230da8ecdd19921faadf2d3f4ff763
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
index 857e6d0..c7b24bc 100644
--- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -178,7 +178,9 @@
         desiredStyle.mIsItalic = isItalic;
         FontInfo bestFont = null;
         int bestMatch = Integer.MAX_VALUE;
-        for (FontInfo font : mFonts) {
+        //noinspection ForLoopReplaceableByForEach (avoid iterator instantiation)
+        for (int i = 0, n = mFonts.size(); i < n; i++) {
+            FontInfo font = mFonts.get(i);
             int match = computeMatch(font, desiredStyle);
             if (match < bestMatch) {
                 bestMatch = match;
@@ -415,7 +417,9 @@
         boolean isItalic = fontInfo.mIsItalic;
         // The list is usually just two fonts big. So iterating over all isn't as bad as it looks.
         // It's biggest for roboto where the size is 12.
-        for (FontInfo font : mFonts) {
+        //noinspection ForLoopReplaceableByForEach (avoid iterator instantiation)
+        for (int i = 0, n = mFonts.size(); i < n; i++) {
+            FontInfo font = mFonts.get(i);
             if (font.mWeight == weight && font.mIsItalic == isItalic) {
                 return false;
             }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index 65b65ec..a545283e 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -480,8 +480,10 @@
             return;
         }
 
-        delegate.mTextSize = textSize;
-        delegate.updateFontObject();
+        if (delegate.mTextSize != textSize) {
+            delegate.mTextSize = textSize;
+            delegate.updateFontObject();
+        }
     }
 
     @LayoutlibDelegate
@@ -503,8 +505,10 @@
             return;
         }
 
-        delegate.mTextScaleX = scaleX;
-        delegate.updateFontObject();
+        if (delegate.mTextScaleX != scaleX) {
+            delegate.mTextScaleX = scaleX;
+            delegate.updateFontObject();
+        }
     }
 
     @LayoutlibDelegate
@@ -526,8 +530,10 @@
             return;
         }
 
-        delegate.mTextSkewX = skewX;
-        delegate.updateFontObject();
+        if (delegate.mTextSkewX != skewX) {
+            delegate.mTextSkewX = skewX;
+            delegate.updateFontObject();
+        }
     }
 
     @LayoutlibDelegate
@@ -897,9 +903,12 @@
             return 0;
         }
 
-        delegate.mTypeface = Typeface_Delegate.getDelegate(typeface);
-        delegate.mNativeTypeface = typeface;
-        delegate.updateFontObject();
+        Typeface_Delegate typefaceDelegate = Typeface_Delegate.getDelegate(typeface);
+        if (delegate.mTypeface != typefaceDelegate || delegate.mNativeTypeface != typeface) {
+            delegate.mTypeface = Typeface_Delegate.getDelegate(typeface);
+            delegate.mNativeTypeface = typeface;
+            delegate.updateFontObject();
+        }
         return typeface;
     }
 
@@ -1214,13 +1223,31 @@
         mCap = paint.mCap;
         mJoin = paint.mJoin;
         mTextAlign = paint.mTextAlign;
-        mTypeface = paint.mTypeface;
-        mNativeTypeface = paint.mNativeTypeface;
+
+        boolean needsFontUpdate = false;
+        if (mTypeface != paint.mTypeface || mNativeTypeface != paint.mNativeTypeface) {
+            mTypeface = paint.mTypeface;
+            mNativeTypeface = paint.mNativeTypeface;
+            needsFontUpdate = true;
+        }
+
+        if (mTextSize != paint.mTextSize) {
+            mTextSize = paint.mTextSize;
+            needsFontUpdate = true;
+        }
+
+        if (mTextScaleX != paint.mTextScaleX) {
+            mTextScaleX = paint.mTextScaleX;
+            needsFontUpdate = true;
+        }
+
+        if (mTextSkewX != paint.mTextSkewX) {
+            mTextSkewX = paint.mTextSkewX;
+            needsFontUpdate = true;
+        }
+
         mStrokeWidth = paint.mStrokeWidth;
         mStrokeMiter = paint.mStrokeMiter;
-        mTextSize = paint.mTextSize;
-        mTextScaleX = paint.mTextScaleX;
-        mTextSkewX = paint.mTextSkewX;
         mXfermode = paint.mXfermode;
         mColorFilter = paint.mColorFilter;
         mShader = paint.mShader;
@@ -1228,7 +1255,10 @@
         mMaskFilter = paint.mMaskFilter;
         mRasterizer = paint.mRasterizer;
         mHintingMode = paint.mHintingMode;
-        updateFontObject();
+
+        if (needsFontUpdate) {
+            updateFontObject();
+        }
     }
 
     private void reset() {
@@ -1264,10 +1294,18 @@
             // Get the fonts from the TypeFace object.
             List<Font> fonts = mTypeface.getFonts(mFontVariant);
 
+            if (fonts.isEmpty()) {
+                mFonts = Collections.emptyList();
+                return;
+            }
+
             // create new font objects as well as FontMetrics, based on the current text size
             // and skew info.
-            ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
-            for (Font font : fonts) {
+            int nFonts = fonts.size();
+            ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(nFonts);
+            //noinspection ForLoopReplaceableByForEach (avoid iterator instantiation)
+            for (int i = 0; i < nFonts; i++) {
+                Font font = fonts.get(i);
                 if (font == null) {
                     // If the font is null, add null to infoList. When rendering the text, if this
                     // null is reached, a warning will be logged.