blob: d5699f1de1bbbbb4b2f33b3948735918235d8e32 [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
19import com.android.internal.util.ArrayUtils;
20
Doug Felt0c702b82010-05-14 10:55:42 -070021import android.graphics.Canvas;
Doug Felte8e45f22010-03-29 14:58:40 -070022import android.graphics.Paint;
Doug Felte8e45f22010-03-29 14:58:40 -070023import android.text.style.MetricAffectingSpan;
24import android.text.style.ReplacementSpan;
25import android.util.Log;
26
27/**
28 * @hide
29 */
30class MeasuredText {
31 /* package */ CharSequence mText;
32 /* package */ int mTextStart;
33 /* package */ float[] mWidths;
34 /* package */ char[] mChars;
35 /* package */ byte[] mLevels;
36 /* package */ int mDir;
37 /* package */ boolean mEasy;
38 /* package */ int mLen;
39 private int mPos;
Doug Felte8e45f22010-03-29 14:58:40 -070040 private TextPaint mWorkPaint;
41
42 private MeasuredText() {
43 mWorkPaint = new TextPaint();
44 }
45
46 private static MeasuredText[] cached = new MeasuredText[3];
47
48 /* package */
49 static MeasuredText obtain() {
50 MeasuredText mt;
51 synchronized (cached) {
52 for (int i = cached.length; --i >= 0;) {
53 if (cached[i] != null) {
54 mt = cached[i];
55 cached[i] = null;
56 return mt;
57 }
58 }
59 }
60 mt = new MeasuredText();
61 Log.e("MEAS", "new: " + mt);
62 return mt;
63 }
64
65 /* package */
66 static MeasuredText recycle(MeasuredText mt) {
67 mt.mText = null;
68 if (mt.mLen < 1000) {
69 synchronized(cached) {
70 for (int i = 0; i < cached.length; ++i) {
71 if (cached[i] == null) {
72 cached[i] = mt;
73 break;
74 }
75 }
76 }
77 }
78 return null;
79 }
80
81 /**
Doug Felt0c702b82010-05-14 10:55:42 -070082 * Analyzes text for bidirectional runs. Allocates working buffers.
Doug Felte8e45f22010-03-29 14:58:40 -070083 */
84 /* package */
85 void setPara(CharSequence text, int start, int end, int bidiRequest) {
86 mText = text;
87 mTextStart = start;
88
89 int len = end - start;
90 mLen = len;
91 mPos = 0;
92
93 if (mWidths == null || mWidths.length < len) {
94 mWidths = new float[ArrayUtils.idealFloatArraySize(len)];
Doug Felte8e45f22010-03-29 14:58:40 -070095 }
96 if (mChars == null || mChars.length < len) {
97 mChars = new char[ArrayUtils.idealCharArraySize(len)];
98 }
99 TextUtils.getChars(text, start, end, mChars, 0);
100
101 if (text instanceof Spanned) {
102 Spanned spanned = (Spanned) text;
103 ReplacementSpan[] spans = spanned.getSpans(start, end,
104 ReplacementSpan.class);
105
106 for (int i = 0; i < spans.length; i++) {
107 int startInPara = spanned.getSpanStart(spans[i]) - start;
108 int endInPara = spanned.getSpanEnd(spans[i]) - start;
109 for (int j = startInPara; j < endInPara; j++) {
110 mChars[j] = '\uFFFC';
111 }
112 }
113 }
114
115 if (TextUtils.doesNotNeedBidi(mChars, 0, len)) {
Doug Felt0c702b82010-05-14 10:55:42 -0700116 mDir = Layout.DIR_LEFT_TO_RIGHT;
Doug Felte8e45f22010-03-29 14:58:40 -0700117 mEasy = true;
118 } else {
119 if (mLevels == null || mLevels.length < len) {
120 mLevels = new byte[ArrayUtils.idealByteArraySize(len)];
121 }
122 mDir = AndroidBidi.bidi(bidiRequest, mChars, mLevels, len, false);
123 mEasy = false;
Doug Felte8e45f22010-03-29 14:58:40 -0700124 }
125 }
126
127 float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm) {
Doug Felte8e45f22010-03-29 14:58:40 -0700128 if (fm != null) {
129 paint.getFontMetricsInt(fm);
130 }
Doug Felt0c702b82010-05-14 10:55:42 -0700131
132 int p = mPos;
133 mPos = p + len;
134
135 if (mEasy) {
136 int flags = mDir == Layout.DIR_LEFT_TO_RIGHT
137 ? Canvas.DIRECTION_LTR : Canvas.DIRECTION_RTL;
138 return paint.getTextRunAdvances(mChars, p, len, p, len, flags, mWidths, p);
139 }
140
141 float totalAdvance = 0;
142 int level = mLevels[p];
143 for (int q = p, i = p + 1, e = p + len;; ++i) {
144 if (i == e || mLevels[i] != level) {
145 int flags = (level & 0x1) == 0 ? Canvas.DIRECTION_LTR : Canvas.DIRECTION_RTL;
146 totalAdvance +=
147 paint.getTextRunAdvances(mChars, q, i - q, q, i - q, flags, mWidths, q);
148 if (i == e) {
149 break;
150 }
151 q = i;
152 level = mLevels[i];
153 }
154 }
155 return totalAdvance;
Doug Felte8e45f22010-03-29 14:58:40 -0700156 }
157
158 float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len,
159 Paint.FontMetricsInt fm) {
160
161 TextPaint workPaint = mWorkPaint;
162 workPaint.set(paint);
163 // XXX paint should not have a baseline shift, but...
164 workPaint.baselineShift = 0;
165
166 ReplacementSpan replacement = null;
167 for (int i = 0; i < spans.length; i++) {
168 MetricAffectingSpan span = spans[i];
169 if (span instanceof ReplacementSpan) {
170 replacement = (ReplacementSpan)span;
171 } else {
172 span.updateMeasureState(workPaint);
173 }
174 }
175
176 float wid;
177 if (replacement == null) {
178 wid = addStyleRun(workPaint, len, fm);
179 } else {
180 // Use original text. Shouldn't matter.
181 wid = replacement.getSize(workPaint, mText, mTextStart + mPos,
182 mTextStart + mPos + len, fm);
183 float[] w = mWidths;
184 w[mPos] = wid;
185 for (int i = mPos + 1, e = mPos + len; i < e; i++)
186 w[i] = 0;
187 }
188
189 if (fm != null) {
190 if (workPaint.baselineShift < 0) {
191 fm.ascent += workPaint.baselineShift;
192 fm.top += workPaint.baselineShift;
193 } else {
194 fm.descent += workPaint.baselineShift;
195 fm.bottom += workPaint.baselineShift;
196 }
197 }
198
199 return wid;
200 }
201
202 int breakText(int start, int limit, boolean forwards, float width) {
203 float[] w = mWidths;
204 if (forwards) {
205 for (int i = start; i < limit; ++i) {
206 if ((width -= w[i]) < 0) {
207 return i - start;
208 }
209 }
210 } else {
211 for (int i = limit; --i >= start;) {
212 if ((width -= w[i]) < 0) {
213 return limit - i -1;
214 }
215 }
216 }
217
218 return limit - start;
219 }
220
221 float measure(int start, int limit) {
222 float width = 0;
223 float[] w = mWidths;
224 for (int i = start; i < limit; ++i) {
225 width += w[i];
226 }
227 return width;
228 }
229}