1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.text; 18 19 import android.graphics.Canvas; 20 import android.graphics.Paint; 21 import android.text.style.MetricAffectingSpan; 22 import android.text.style.ReplacementSpan; 23 import android.util.Log; 24 25 import com.android.internal.util.ArrayUtils; 26 27 /** 28 * @hide 29 */ 30 class MeasuredText { 31 private static final boolean localLOGV = false; 32 CharSequence mText; 33 int mTextStart; 34 float[] mWidths; 35 char[] mChars; 36 byte[] mLevels; 37 int mDir; 38 boolean mEasy; 39 int mLen; 40 41 private int mPos; 42 private TextPaint mWorkPaint; 43 MeasuredText()44 private MeasuredText() { 45 mWorkPaint = new TextPaint(); 46 } 47 48 private static final Object[] sLock = new Object[0]; 49 private static MeasuredText[] sCached = new MeasuredText[3]; 50 obtain()51 static MeasuredText obtain() { 52 MeasuredText mt; 53 synchronized (sLock) { 54 for (int i = sCached.length; --i >= 0;) { 55 if (sCached[i] != null) { 56 mt = sCached[i]; 57 sCached[i] = null; 58 return mt; 59 } 60 } 61 } 62 mt = new MeasuredText(); 63 if (localLOGV) { 64 Log.v("MEAS", "new: " + mt); 65 } 66 return mt; 67 } 68 recycle(MeasuredText mt)69 static MeasuredText recycle(MeasuredText mt) { 70 mt.mText = null; 71 if (mt.mLen < 1000) { 72 synchronized(sLock) { 73 for (int i = 0; i < sCached.length; ++i) { 74 if (sCached[i] == null) { 75 sCached[i] = mt; 76 mt.mText = null; 77 break; 78 } 79 } 80 } 81 } 82 return null; 83 } 84 85 /** 86 * Analyzes text for bidirectional runs. Allocates working buffers. 87 */ setPara(CharSequence text, int start, int end, TextDirectionHeuristic textDir)88 void setPara(CharSequence text, int start, int end, TextDirectionHeuristic textDir) { 89 mText = text; 90 mTextStart = start; 91 92 int len = end - start; 93 mLen = len; 94 mPos = 0; 95 96 if (mWidths == null || mWidths.length < len) { 97 mWidths = new float[ArrayUtils.idealFloatArraySize(len)]; 98 } 99 if (mChars == null || mChars.length < len) { 100 mChars = new char[ArrayUtils.idealCharArraySize(len)]; 101 } 102 TextUtils.getChars(text, start, end, mChars, 0); 103 104 if (text instanceof Spanned) { 105 Spanned spanned = (Spanned) text; 106 ReplacementSpan[] spans = spanned.getSpans(start, end, 107 ReplacementSpan.class); 108 109 for (int i = 0; i < spans.length; i++) { 110 int startInPara = spanned.getSpanStart(spans[i]) - start; 111 int endInPara = spanned.getSpanEnd(spans[i]) - start; 112 for (int j = startInPara; j < endInPara; j++) { 113 mChars[j] = '\uFFFC'; 114 } 115 } 116 } 117 118 if ((textDir == TextDirectionHeuristics.LTR || 119 textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR || 120 textDir == TextDirectionHeuristics.ANYRTL_LTR) && 121 TextUtils.doesNotNeedBidi(mChars, 0, len)) { 122 mDir = Layout.DIR_LEFT_TO_RIGHT; 123 mEasy = true; 124 } else { 125 if (mLevels == null || mLevels.length < len) { 126 mLevels = new byte[ArrayUtils.idealByteArraySize(len)]; 127 } 128 int bidiRequest; 129 if (textDir == TextDirectionHeuristics.LTR) { 130 bidiRequest = Layout.DIR_REQUEST_LTR; 131 } else if (textDir == TextDirectionHeuristics.RTL) { 132 bidiRequest = Layout.DIR_REQUEST_RTL; 133 } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) { 134 bidiRequest = Layout.DIR_REQUEST_DEFAULT_LTR; 135 } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) { 136 bidiRequest = Layout.DIR_REQUEST_DEFAULT_RTL; 137 } else { 138 boolean isRtl = textDir.isRtl(mChars, 0, len); 139 bidiRequest = isRtl ? Layout.DIR_REQUEST_RTL : Layout.DIR_REQUEST_LTR; 140 } 141 mDir = AndroidBidi.bidi(bidiRequest, mChars, mLevels, len, false); 142 mEasy = false; 143 } 144 } 145 addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm)146 float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm) { 147 if (fm != null) { 148 paint.getFontMetricsInt(fm); 149 } 150 151 int p = mPos; 152 mPos = p + len; 153 154 if (mEasy) { 155 int flags = mDir == Layout.DIR_LEFT_TO_RIGHT 156 ? Canvas.DIRECTION_LTR : Canvas.DIRECTION_RTL; 157 return paint.getTextRunAdvances(mChars, p, len, p, len, flags, mWidths, p); 158 } 159 160 float totalAdvance = 0; 161 int level = mLevels[p]; 162 for (int q = p, i = p + 1, e = p + len;; ++i) { 163 if (i == e || mLevels[i] != level) { 164 int flags = (level & 0x1) == 0 ? Canvas.DIRECTION_LTR : Canvas.DIRECTION_RTL; 165 totalAdvance += 166 paint.getTextRunAdvances(mChars, q, i - q, q, i - q, flags, mWidths, q); 167 if (i == e) { 168 break; 169 } 170 q = i; 171 level = mLevels[i]; 172 } 173 } 174 return totalAdvance; 175 } 176 addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len, Paint.FontMetricsInt fm)177 float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len, 178 Paint.FontMetricsInt fm) { 179 180 TextPaint workPaint = mWorkPaint; 181 workPaint.set(paint); 182 // XXX paint should not have a baseline shift, but... 183 workPaint.baselineShift = 0; 184 185 ReplacementSpan replacement = null; 186 for (int i = 0; i < spans.length; i++) { 187 MetricAffectingSpan span = spans[i]; 188 if (span instanceof ReplacementSpan) { 189 replacement = (ReplacementSpan)span; 190 } else { 191 span.updateMeasureState(workPaint); 192 } 193 } 194 195 float wid; 196 if (replacement == null) { 197 wid = addStyleRun(workPaint, len, fm); 198 } else { 199 // Use original text. Shouldn't matter. 200 wid = replacement.getSize(workPaint, mText, mTextStart + mPos, 201 mTextStart + mPos + len, fm); 202 float[] w = mWidths; 203 w[mPos] = wid; 204 for (int i = mPos + 1, e = mPos + len; i < e; i++) 205 w[i] = 0; 206 mPos += len; 207 } 208 209 if (fm != null) { 210 if (workPaint.baselineShift < 0) { 211 fm.ascent += workPaint.baselineShift; 212 fm.top += workPaint.baselineShift; 213 } else { 214 fm.descent += workPaint.baselineShift; 215 fm.bottom += workPaint.baselineShift; 216 } 217 } 218 219 return wid; 220 } 221 breakText(int start, int limit, boolean forwards, float width)222 int breakText(int start, int limit, boolean forwards, float width) { 223 float[] w = mWidths; 224 if (forwards) { 225 for (int i = start; i < limit; ++i) { 226 if ((width -= w[i]) < 0) { 227 return i - start; 228 } 229 } 230 } else { 231 for (int i = limit; --i >= start;) { 232 if ((width -= w[i]) < 0) { 233 return limit - i -1; 234 } 235 } 236 } 237 238 return limit - start; 239 } 240 measure(int start, int limit)241 float measure(int start, int limit) { 242 float width = 0; 243 float[] w = mWidths; 244 for (int i = start; i < limit; ++i) { 245 width += w[i]; 246 } 247 return width; 248 } 249 } 250