blob: 0c881a4da6b360e23e5648de71bda77ba00a7b6a [file] [log] [blame]
Doug Felte8e45f22010-03-29 14:58:40 -07001/*
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
17package android.text;
18
Doug Felt0c702b82010-05-14 10:55:42 -070019import android.graphics.Canvas;
Doug Felte8e45f22010-03-29 14:58:40 -070020import android.graphics.Paint;
Doug Felte8e45f22010-03-29 14:58:40 -070021import android.text.style.MetricAffectingSpan;
22import android.text.style.ReplacementSpan;
23import android.util.Log;
24
Gilles Debunne9a3a8842011-05-27 09:33:46 -070025import com.android.internal.util.ArrayUtils;
26
Doug Felte8e45f22010-03-29 14:58:40 -070027/**
28 * @hide
29 */
30class MeasuredText {
Kenny Rootfbc86302011-01-31 13:54:58 -080031 private static final boolean localLOGV = false;
Romain Guye5ea4402011-08-01 14:01:37 -070032 CharSequence mText;
33 int mTextStart;
34 float[] mWidths;
35 char[] mChars;
36 byte[] mLevels;
37 int mDir;
38 boolean mEasy;
39 int mLen;
40
Doug Felte8e45f22010-03-29 14:58:40 -070041 private int mPos;
Doug Felte8e45f22010-03-29 14:58:40 -070042 private TextPaint mWorkPaint;
43
44 private MeasuredText() {
45 mWorkPaint = new TextPaint();
46 }
47
Romain Guye5ea4402011-08-01 14:01:37 -070048 private static final Object[] sLock = new Object[0];
49 private static MeasuredText[] sCached = new MeasuredText[3];
Doug Felte8e45f22010-03-29 14:58:40 -070050
Doug Felte8e45f22010-03-29 14:58:40 -070051 static MeasuredText obtain() {
52 MeasuredText mt;
Romain Guye5ea4402011-08-01 14:01:37 -070053 synchronized (sLock) {
54 for (int i = sCached.length; --i >= 0;) {
55 if (sCached[i] != null) {
56 mt = sCached[i];
57 sCached[i] = null;
Doug Felte8e45f22010-03-29 14:58:40 -070058 return mt;
59 }
60 }
61 }
62 mt = new MeasuredText();
Kenny Rootfbc86302011-01-31 13:54:58 -080063 if (localLOGV) {
64 Log.v("MEAS", "new: " + mt);
65 }
Doug Felte8e45f22010-03-29 14:58:40 -070066 return mt;
67 }
68
Doug Felte8e45f22010-03-29 14:58:40 -070069 static MeasuredText recycle(MeasuredText mt) {
70 mt.mText = null;
71 if (mt.mLen < 1000) {
Romain Guye5ea4402011-08-01 14:01:37 -070072 synchronized(sLock) {
73 for (int i = 0; i < sCached.length; ++i) {
74 if (sCached[i] == null) {
75 sCached[i] = mt;
76 mt.mText = null;
Doug Felte8e45f22010-03-29 14:58:40 -070077 break;
78 }
79 }
80 }
81 }
82 return null;
83 }
84
Gilles Debunnecd943a72012-06-07 17:54:47 -070085 void setPos(int pos) {
Raph Levien1341ab62012-07-09 16:57:35 -070086 mPos = pos - mTextStart;
Gilles Debunnecd943a72012-06-07 17:54:47 -070087 }
88
Doug Felte8e45f22010-03-29 14:58:40 -070089 /**
Doug Felt0c702b82010-05-14 10:55:42 -070090 * Analyzes text for bidirectional runs. Allocates working buffers.
Doug Felte8e45f22010-03-29 14:58:40 -070091 */
Doug Feltcb3791202011-07-07 11:57:48 -070092 void setPara(CharSequence text, int start, int end, TextDirectionHeuristic textDir) {
Doug Felte8e45f22010-03-29 14:58:40 -070093 mText = text;
94 mTextStart = start;
95
96 int len = end - start;
97 mLen = len;
98 mPos = 0;
99
100 if (mWidths == null || mWidths.length < len) {
101 mWidths = new float[ArrayUtils.idealFloatArraySize(len)];
Doug Felte8e45f22010-03-29 14:58:40 -0700102 }
103 if (mChars == null || mChars.length < len) {
104 mChars = new char[ArrayUtils.idealCharArraySize(len)];
105 }
106 TextUtils.getChars(text, start, end, mChars, 0);
107
108 if (text instanceof Spanned) {
109 Spanned spanned = (Spanned) text;
110 ReplacementSpan[] spans = spanned.getSpans(start, end,
111 ReplacementSpan.class);
112
113 for (int i = 0; i < spans.length; i++) {
114 int startInPara = spanned.getSpanStart(spans[i]) - start;
115 int endInPara = spanned.getSpanEnd(spans[i]) - start;
Gilles Debunneba3634f2012-01-23 16:36:33 -0800116 // The span interval may be larger and must be restricted to [start, end[
117 if (startInPara < 0) startInPara = 0;
118 if (endInPara > len) endInPara = len;
Doug Felte8e45f22010-03-29 14:58:40 -0700119 for (int j = startInPara; j < endInPara; j++) {
Gilles Debunnecd943a72012-06-07 17:54:47 -0700120 mChars[j] = '\uFFFC'; // object replacement character
Doug Felte8e45f22010-03-29 14:58:40 -0700121 }
122 }
123 }
124
Doug Feltcb3791202011-07-07 11:57:48 -0700125 if ((textDir == TextDirectionHeuristics.LTR ||
126 textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR ||
127 textDir == TextDirectionHeuristics.ANYRTL_LTR) &&
128 TextUtils.doesNotNeedBidi(mChars, 0, len)) {
Doug Felt0c702b82010-05-14 10:55:42 -0700129 mDir = Layout.DIR_LEFT_TO_RIGHT;
Doug Felte8e45f22010-03-29 14:58:40 -0700130 mEasy = true;
131 } else {
132 if (mLevels == null || mLevels.length < len) {
133 mLevels = new byte[ArrayUtils.idealByteArraySize(len)];
134 }
Doug Feltcb3791202011-07-07 11:57:48 -0700135 int bidiRequest;
136 if (textDir == TextDirectionHeuristics.LTR) {
137 bidiRequest = Layout.DIR_REQUEST_LTR;
138 } else if (textDir == TextDirectionHeuristics.RTL) {
139 bidiRequest = Layout.DIR_REQUEST_RTL;
140 } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) {
141 bidiRequest = Layout.DIR_REQUEST_DEFAULT_LTR;
142 } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
143 bidiRequest = Layout.DIR_REQUEST_DEFAULT_RTL;
144 } else {
145 boolean isRtl = textDir.isRtl(mChars, 0, len);
146 bidiRequest = isRtl ? Layout.DIR_REQUEST_RTL : Layout.DIR_REQUEST_LTR;
147 }
Doug Felte8e45f22010-03-29 14:58:40 -0700148 mDir = AndroidBidi.bidi(bidiRequest, mChars, mLevels, len, false);
149 mEasy = false;
Doug Felte8e45f22010-03-29 14:58:40 -0700150 }
151 }
152
153 float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm) {
Doug Felte8e45f22010-03-29 14:58:40 -0700154 if (fm != null) {
155 paint.getFontMetricsInt(fm);
156 }
Doug Felt0c702b82010-05-14 10:55:42 -0700157
158 int p = mPos;
159 mPos = p + len;
160
161 if (mEasy) {
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800162 return paint.getTextRunAdvances(mChars, p, len, p, len, mWidths, p);
Doug Felt0c702b82010-05-14 10:55:42 -0700163 }
164
165 float totalAdvance = 0;
166 int level = mLevels[p];
167 for (int q = p, i = p + 1, e = p + len;; ++i) {
168 if (i == e || mLevels[i] != level) {
Doug Felt0c702b82010-05-14 10:55:42 -0700169 totalAdvance +=
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800170 paint.getTextRunAdvances(mChars, q, i - q, q, i - q, mWidths, q);
Doug Felt0c702b82010-05-14 10:55:42 -0700171 if (i == e) {
172 break;
173 }
174 q = i;
175 level = mLevels[i];
176 }
177 }
178 return totalAdvance;
Doug Felte8e45f22010-03-29 14:58:40 -0700179 }
180
181 float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len,
182 Paint.FontMetricsInt fm) {
183
184 TextPaint workPaint = mWorkPaint;
185 workPaint.set(paint);
186 // XXX paint should not have a baseline shift, but...
187 workPaint.baselineShift = 0;
188
189 ReplacementSpan replacement = null;
190 for (int i = 0; i < spans.length; i++) {
191 MetricAffectingSpan span = spans[i];
192 if (span instanceof ReplacementSpan) {
193 replacement = (ReplacementSpan)span;
194 } else {
195 span.updateMeasureState(workPaint);
196 }
197 }
198
199 float wid;
200 if (replacement == null) {
201 wid = addStyleRun(workPaint, len, fm);
202 } else {
203 // Use original text. Shouldn't matter.
204 wid = replacement.getSize(workPaint, mText, mTextStart + mPos,
205 mTextStart + mPos + len, fm);
206 float[] w = mWidths;
207 w[mPos] = wid;
208 for (int i = mPos + 1, e = mPos + len; i < e; i++)
209 w[i] = 0;
Gilles Debunne9a3a8842011-05-27 09:33:46 -0700210 mPos += len;
Doug Felte8e45f22010-03-29 14:58:40 -0700211 }
212
213 if (fm != null) {
214 if (workPaint.baselineShift < 0) {
215 fm.ascent += workPaint.baselineShift;
216 fm.top += workPaint.baselineShift;
217 } else {
218 fm.descent += workPaint.baselineShift;
219 fm.bottom += workPaint.baselineShift;
220 }
221 }
222
223 return wid;
224 }
225
Gilles Debunnec70e7a02012-02-23 18:05:55 -0800226 int breakText(int limit, boolean forwards, float width) {
Doug Felte8e45f22010-03-29 14:58:40 -0700227 float[] w = mWidths;
228 if (forwards) {
Gilles Debunnec70e7a02012-02-23 18:05:55 -0800229 int i = 0;
230 while (i < limit) {
231 width -= w[i];
232 if (width < 0.0f) break;
233 i++;
Doug Felte8e45f22010-03-29 14:58:40 -0700234 }
Gilles Debunnec70e7a02012-02-23 18:05:55 -0800235 while (i > 0 && mChars[i - 1] == ' ') i--;
236 return i;
Doug Felte8e45f22010-03-29 14:58:40 -0700237 } else {
Gilles Debunnec70e7a02012-02-23 18:05:55 -0800238 int i = limit - 1;
239 while (i >= 0) {
240 width -= w[i];
241 if (width < 0.0f) break;
242 i--;
Doug Felte8e45f22010-03-29 14:58:40 -0700243 }
Gilles Debunnec70e7a02012-02-23 18:05:55 -0800244 while (i < limit - 1 && mChars[i + 1] == ' ') i++;
245 return limit - i - 1;
Doug Felte8e45f22010-03-29 14:58:40 -0700246 }
Doug Felte8e45f22010-03-29 14:58:40 -0700247 }
248
249 float measure(int start, int limit) {
250 float width = 0;
251 float[] w = mWidths;
252 for (int i = start; i < limit; ++i) {
253 width += w[i];
254 }
255 return width;
256 }
Kenny Rootfbc86302011-01-31 13:54:58 -0800257}