use of android.text.style.LeadingMarginSpan.LeadingMarginSpan2 in project android_frameworks_base by AOSPA.
the class Layout method drawText.
/**
* @hide
*/
public void drawText(Canvas canvas, int firstLine, int lastLine) {
int previousLineBottom = getLineTop(firstLine);
int previousLineEnd = getLineStart(firstLine);
ParagraphStyle[] spans = NO_PARA_SPANS;
int spanEnd = 0;
TextPaint paint = mPaint;
CharSequence buf = mText;
Alignment paraAlign = mAlignment;
TabStops tabStops = null;
boolean tabStopsIsInitialized = false;
TextLine tl = TextLine.obtain();
// The baseline is the top of the following line minus the current line's descent.
for (int lineNum = firstLine; lineNum <= lastLine; lineNum++) {
int start = previousLineEnd;
previousLineEnd = getLineStart(lineNum + 1);
int end = getLineVisibleEnd(lineNum, start, previousLineEnd);
int ltop = previousLineBottom;
int lbottom = getLineTop(lineNum + 1);
previousLineBottom = lbottom;
int lbaseline = lbottom - getLineDescent(lineNum);
int dir = getParagraphDirection(lineNum);
int left = 0;
int right = mWidth;
if (mSpannedText) {
Spanned sp = (Spanned) buf;
int textLength = buf.length();
boolean isFirstParaLine = (start == 0 || buf.charAt(start - 1) == '\n');
// our problem.
if (start >= spanEnd && (lineNum == firstLine || isFirstParaLine)) {
spanEnd = sp.nextSpanTransition(start, textLength, ParagraphStyle.class);
spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class);
paraAlign = mAlignment;
for (int n = spans.length - 1; n >= 0; n--) {
if (spans[n] instanceof AlignmentSpan) {
paraAlign = ((AlignmentSpan) spans[n]).getAlignment();
break;
}
}
tabStopsIsInitialized = false;
}
// Draw all leading margin spans. Adjust left or right according
// to the paragraph direction of the line.
final int length = spans.length;
boolean useFirstLineMargin = isFirstParaLine;
for (int n = 0; n < length; n++) {
if (spans[n] instanceof LeadingMarginSpan2) {
int count = ((LeadingMarginSpan2) spans[n]).getLeadingMarginLineCount();
int startLine = getLineForOffset(sp.getSpanStart(spans[n]));
// the count that is greatest
if (lineNum < startLine + count) {
useFirstLineMargin = true;
break;
}
}
}
for (int n = 0; n < length; n++) {
if (spans[n] instanceof LeadingMarginSpan) {
LeadingMarginSpan margin = (LeadingMarginSpan) spans[n];
if (dir == DIR_RIGHT_TO_LEFT) {
margin.drawLeadingMargin(canvas, paint, right, dir, ltop, lbaseline, lbottom, buf, start, end, isFirstParaLine, this);
right -= margin.getLeadingMargin(useFirstLineMargin);
} else {
margin.drawLeadingMargin(canvas, paint, left, dir, ltop, lbaseline, lbottom, buf, start, end, isFirstParaLine, this);
left += margin.getLeadingMargin(useFirstLineMargin);
}
}
}
}
boolean hasTab = getLineContainsTab(lineNum);
// Can't tell if we have tabs for sure, currently
if (hasTab && !tabStopsIsInitialized) {
if (tabStops == null) {
tabStops = new TabStops(TAB_INCREMENT, spans);
} else {
tabStops.reset(TAB_INCREMENT, spans);
}
tabStopsIsInitialized = true;
}
// Determine whether the line aligns to normal, opposite, or center.
Alignment align = paraAlign;
if (align == Alignment.ALIGN_LEFT) {
align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE;
} else if (align == Alignment.ALIGN_RIGHT) {
align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL;
}
int x;
if (align == Alignment.ALIGN_NORMAL) {
if (dir == DIR_LEFT_TO_RIGHT) {
x = left + getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
} else {
x = right + getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
}
} else {
int max = (int) getLineExtent(lineNum, tabStops, false);
if (align == Alignment.ALIGN_OPPOSITE) {
if (dir == DIR_LEFT_TO_RIGHT) {
x = right - max + getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
} else {
x = left - max + getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
}
} else {
// Alignment.ALIGN_CENTER
max = max & ~1;
x = ((right + left - max) >> 1) + getIndentAdjust(lineNum, Alignment.ALIGN_CENTER);
}
}
paint.setHyphenEdit(getHyphen(lineNum));
Directions directions = getLineDirections(lineNum);
if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTab) {
// XXX: assumes there's nothing additional to be done
canvas.drawText(buf, start, end, x, lbaseline, paint);
} else {
tl.set(paint, buf, start, end, dir, directions, hasTab, tabStops);
tl.draw(canvas, x, ltop, lbaseline, lbottom);
}
paint.setHyphenEdit(0);
}
TextLine.recycle(tl);
}
use of android.text.style.LeadingMarginSpan.LeadingMarginSpan2 in project android_frameworks_base by ResurrectionRemix.
the class Layout method getParagraphLeadingMargin.
/**
* Returns the effective leading margin (unsigned) for this line,
* taking into account LeadingMarginSpan and LeadingMarginSpan2.
* @param line the line index
* @return the leading margin of this line
*/
private int getParagraphLeadingMargin(int line) {
if (!mSpannedText) {
return 0;
}
Spanned spanned = (Spanned) mText;
int lineStart = getLineStart(line);
int lineEnd = getLineEnd(line);
int spanEnd = spanned.nextSpanTransition(lineStart, lineEnd, LeadingMarginSpan.class);
LeadingMarginSpan[] spans = getParagraphSpans(spanned, lineStart, spanEnd, LeadingMarginSpan.class);
if (spans.length == 0) {
// no leading margin span;
return 0;
}
int margin = 0;
boolean isFirstParaLine = lineStart == 0 || spanned.charAt(lineStart - 1) == '\n';
boolean useFirstLineMargin = isFirstParaLine;
for (int i = 0; i < spans.length; i++) {
if (spans[i] instanceof LeadingMarginSpan2) {
int spStart = spanned.getSpanStart(spans[i]);
int spanLine = getLineForOffset(spStart);
int count = ((LeadingMarginSpan2) spans[i]).getLeadingMarginLineCount();
// if there is more than one LeadingMarginSpan2, use the count that is greatest
useFirstLineMargin |= line < spanLine + count;
}
}
for (int i = 0; i < spans.length; i++) {
LeadingMarginSpan span = spans[i];
margin += span.getLeadingMargin(useFirstLineMargin);
}
return margin;
}
use of android.text.style.LeadingMarginSpan.LeadingMarginSpan2 in project android_frameworks_base by DirtyUnicorns.
the class Layout method getParagraphLeadingMargin.
/**
* Returns the effective leading margin (unsigned) for this line,
* taking into account LeadingMarginSpan and LeadingMarginSpan2.
* @param line the line index
* @return the leading margin of this line
*/
private int getParagraphLeadingMargin(int line) {
if (!mSpannedText) {
return 0;
}
Spanned spanned = (Spanned) mText;
int lineStart = getLineStart(line);
int lineEnd = getLineEnd(line);
int spanEnd = spanned.nextSpanTransition(lineStart, lineEnd, LeadingMarginSpan.class);
LeadingMarginSpan[] spans = getParagraphSpans(spanned, lineStart, spanEnd, LeadingMarginSpan.class);
if (spans.length == 0) {
// no leading margin span;
return 0;
}
int margin = 0;
boolean isFirstParaLine = lineStart == 0 || spanned.charAt(lineStart - 1) == '\n';
boolean useFirstLineMargin = isFirstParaLine;
for (int i = 0; i < spans.length; i++) {
if (spans[i] instanceof LeadingMarginSpan2) {
int spStart = spanned.getSpanStart(spans[i]);
int spanLine = getLineForOffset(spStart);
int count = ((LeadingMarginSpan2) spans[i]).getLeadingMarginLineCount();
// if there is more than one LeadingMarginSpan2, use the count that is greatest
useFirstLineMargin |= line < spanLine + count;
}
}
for (int i = 0; i < spans.length; i++) {
LeadingMarginSpan span = spans[i];
margin += span.getLeadingMargin(useFirstLineMargin);
}
return margin;
}
use of android.text.style.LeadingMarginSpan.LeadingMarginSpan2 in project android_frameworks_base by DirtyUnicorns.
the class StaticLayout method generate.
/* package */
void generate(Builder b, boolean includepad, boolean trackpad) {
CharSequence source = b.mText;
int bufStart = b.mStart;
int bufEnd = b.mEnd;
TextPaint paint = b.mPaint;
int outerWidth = b.mWidth;
TextDirectionHeuristic textDir = b.mTextDir;
float spacingmult = b.mSpacingMult;
float spacingadd = b.mSpacingAdd;
float ellipsizedWidth = b.mEllipsizedWidth;
TextUtils.TruncateAt ellipsize = b.mEllipsize;
// TODO: move to builder to avoid allocation costs
LineBreaks lineBreaks = new LineBreaks();
// store span end locations
int[] spanEndCache = new int[4];
// store fontMetrics per span range
// must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range)
int[] fmCache = new int[4 * 4];
// TODO: also respect LocaleSpan within the text
b.setLocale(paint.getTextLocale());
mLineCount = 0;
int v = 0;
boolean needMultiply = (spacingmult != 1 || spacingadd != 0);
Paint.FontMetricsInt fm = b.mFontMetricsInt;
int[] chooseHtv = null;
MeasuredText measured = b.mMeasuredText;
Spanned spanned = null;
if (source instanceof Spanned)
spanned = (Spanned) source;
int paraEnd;
for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd);
if (paraEnd < 0)
paraEnd = bufEnd;
else
paraEnd++;
int firstWidthLineCount = 1;
int firstWidth = outerWidth;
int restWidth = outerWidth;
LineHeightSpan[] chooseHt = null;
if (spanned != null) {
LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd, LeadingMarginSpan.class);
for (int i = 0; i < sp.length; i++) {
LeadingMarginSpan lms = sp[i];
firstWidth -= sp[i].getLeadingMargin(true);
restWidth -= sp[i].getLeadingMargin(false);
// leading margin spans, not just this particular one
if (lms instanceof LeadingMarginSpan2) {
LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
firstWidthLineCount = Math.max(firstWidthLineCount, lms2.getLeadingMarginLineCount());
}
}
chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
if (chooseHt.length == 0) {
// So that out() would not assume it has any contents
chooseHt = null;
} else {
if (chooseHtv == null || chooseHtv.length < chooseHt.length) {
chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
}
for (int i = 0; i < chooseHt.length; i++) {
int o = spanned.getSpanStart(chooseHt[i]);
if (o < paraStart) {
// starts in this layout, before the
// current paragraph
chooseHtv[i] = getLineTop(getLineForOffset(o));
} else {
// starts in this paragraph
chooseHtv[i] = v;
}
}
}
}
measured.setPara(source, paraStart, paraEnd, textDir, b);
char[] chs = measured.mChars;
float[] widths = measured.mWidths;
byte[] chdirs = measured.mLevels;
int dir = measured.mDir;
boolean easy = measured.mEasy;
// tab stop locations
int[] variableTabStops = null;
if (spanned != null) {
TabStopSpan[] spans = getParagraphSpans(spanned, paraStart, paraEnd, TabStopSpan.class);
if (spans.length > 0) {
int[] stops = new int[spans.length];
for (int i = 0; i < spans.length; i++) {
stops[i] = spans[i].getTabStop();
}
Arrays.sort(stops, 0, stops.length);
variableTabStops = stops;
}
}
nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart, firstWidth, firstWidthLineCount, restWidth, variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency);
if (mLeftIndents != null || mRightIndents != null) {
// TODO(raph) performance: it would be better to do this once per layout rather
// than once per paragraph, but that would require a change to the native
// interface.
int leftLen = mLeftIndents == null ? 0 : mLeftIndents.length;
int rightLen = mRightIndents == null ? 0 : mRightIndents.length;
int indentsLen = Math.max(1, Math.max(leftLen, rightLen) - mLineCount);
int[] indents = new int[indentsLen];
for (int i = 0; i < indentsLen; i++) {
int leftMargin = mLeftIndents == null ? 0 : mLeftIndents[Math.min(i + mLineCount, leftLen - 1)];
int rightMargin = mRightIndents == null ? 0 : mRightIndents[Math.min(i + mLineCount, rightLen - 1)];
indents[i] = leftMargin + rightMargin;
}
nSetIndents(b.mNativePtr, indents);
}
// measurement has to be done before performing line breaking
// but we don't want to recompute fontmetrics or span ranges the
// second time, so we cache those and then use those stored values
int fmCacheCount = 0;
int spanEndCacheCount = 0;
for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
if (fmCacheCount * 4 >= fmCache.length) {
int[] grow = new int[fmCacheCount * 4 * 2];
System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4);
fmCache = grow;
}
if (spanEndCacheCount >= spanEndCache.length) {
int[] grow = new int[spanEndCacheCount * 2];
System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount);
spanEndCache = grow;
}
if (spanned == null) {
spanEnd = paraEnd;
int spanLen = spanEnd - spanStart;
measured.addStyleRun(paint, spanLen, fm);
} else {
spanEnd = spanned.nextSpanTransition(spanStart, paraEnd, MetricAffectingSpan.class);
int spanLen = spanEnd - spanStart;
MetricAffectingSpan[] spans = spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
spans = TextUtils.removeEmptySpans(spans, spanned, MetricAffectingSpan.class);
measured.addStyleRun(paint, spans, spanLen, fm);
}
// the order of storage here (top, bottom, ascent, descent) has to match the code below
// where these values are retrieved
fmCache[fmCacheCount * 4 + 0] = fm.top;
fmCache[fmCacheCount * 4 + 1] = fm.bottom;
fmCache[fmCacheCount * 4 + 2] = fm.ascent;
fmCache[fmCacheCount * 4 + 3] = fm.descent;
fmCacheCount++;
spanEndCache[spanEndCacheCount] = spanEnd;
spanEndCacheCount++;
}
nGetWidths(b.mNativePtr, widths);
int breakCount = nComputeLineBreaks(b.mNativePtr, lineBreaks, lineBreaks.breaks, lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length);
int[] breaks = lineBreaks.breaks;
float[] lineWidths = lineBreaks.widths;
int[] flags = lineBreaks.flags;
final int remainingLineCount = mMaximumVisibleLineCount - mLineCount;
final boolean ellipsisMayBeApplied = ellipsize != null && (ellipsize == TextUtils.TruncateAt.END || (mMaximumVisibleLineCount == 1 && ellipsize != TextUtils.TruncateAt.MARQUEE));
if (remainingLineCount > 0 && remainingLineCount < breakCount && ellipsisMayBeApplied) {
// Calculate width and flag.
float width = 0;
int flag = 0;
for (int i = remainingLineCount - 1; i < breakCount; i++) {
if (i == breakCount - 1) {
width += lineWidths[i];
} else {
for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
width += widths[j];
}
}
flag |= flags[i] & TAB_MASK;
}
// Treat the last line and overflowed lines as a single line.
breaks[remainingLineCount - 1] = breaks[breakCount - 1];
lineWidths[remainingLineCount - 1] = width;
flags[remainingLineCount - 1] = flag;
breakCount = remainingLineCount;
}
// here is the offset of the starting character of the line we are currently measuring
int here = paraStart;
int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0;
int fmCacheIndex = 0;
int spanEndCacheIndex = 0;
int breakIndex = 0;
for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
// retrieve end of span
spanEnd = spanEndCache[spanEndCacheIndex++];
// retrieve cached metrics, order matches above
fm.top = fmCache[fmCacheIndex * 4 + 0];
fm.bottom = fmCache[fmCacheIndex * 4 + 1];
fm.ascent = fmCache[fmCacheIndex * 4 + 2];
fm.descent = fmCache[fmCacheIndex * 4 + 3];
fmCacheIndex++;
if (fm.top < fmTop) {
fmTop = fm.top;
}
if (fm.ascent < fmAscent) {
fmAscent = fm.ascent;
}
if (fm.descent > fmDescent) {
fmDescent = fm.descent;
}
if (fm.bottom > fmBottom) {
fmBottom = fm.bottom;
}
// skip breaks ending before current span range
while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
breakIndex++;
}
while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) {
int endPos = paraStart + breaks[breakIndex];
boolean moreChars = (endPos < bufEnd);
v = out(source, here, endPos, fmAscent, fmDescent, fmTop, fmBottom, v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, flags[breakIndex], needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad, chs, widths, paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex], paint, moreChars);
if (endPos < spanEnd) {
// preserve metrics for current span
fmTop = fm.top;
fmBottom = fm.bottom;
fmAscent = fm.ascent;
fmDescent = fm.descent;
} else {
fmTop = fmBottom = fmAscent = fmDescent = 0;
}
here = endPos;
breakIndex++;
if (mLineCount >= mMaximumVisibleLineCount) {
return;
}
}
}
if (paraEnd == bufEnd)
break;
}
if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) && mLineCount < mMaximumVisibleLineCount) {
// Log.e("text", "output last " + bufEnd);
measured.setPara(source, bufEnd, bufEnd, textDir, b);
paint.getFontMetricsInt(fm);
v = out(source, bufEnd, bufEnd, fm.ascent, fm.descent, fm.top, fm.bottom, v, spacingmult, spacingadd, null, null, fm, 0, needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd, includepad, trackpad, null, null, bufStart, ellipsize, ellipsizedWidth, 0, paint, false);
}
}
use of android.text.style.LeadingMarginSpan.LeadingMarginSpan2 in project android_frameworks_base by crdroidandroid.
the class StaticLayout method generate.
/* package */
void generate(Builder b, boolean includepad, boolean trackpad) {
CharSequence source = b.mText;
int bufStart = b.mStart;
int bufEnd = b.mEnd;
TextPaint paint = b.mPaint;
int outerWidth = b.mWidth;
TextDirectionHeuristic textDir = b.mTextDir;
float spacingmult = b.mSpacingMult;
float spacingadd = b.mSpacingAdd;
float ellipsizedWidth = b.mEllipsizedWidth;
TextUtils.TruncateAt ellipsize = b.mEllipsize;
// TODO: move to builder to avoid allocation costs
LineBreaks lineBreaks = new LineBreaks();
// store span end locations
int[] spanEndCache = new int[4];
// store fontMetrics per span range
// must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range)
int[] fmCache = new int[4 * 4];
// TODO: also respect LocaleSpan within the text
b.setLocale(paint.getTextLocale());
mLineCount = 0;
int v = 0;
boolean needMultiply = (spacingmult != 1 || spacingadd != 0);
Paint.FontMetricsInt fm = b.mFontMetricsInt;
int[] chooseHtv = null;
MeasuredText measured = b.mMeasuredText;
Spanned spanned = null;
if (source instanceof Spanned)
spanned = (Spanned) source;
int paraEnd;
for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd);
if (paraEnd < 0)
paraEnd = bufEnd;
else
paraEnd++;
int firstWidthLineCount = 1;
int firstWidth = outerWidth;
int restWidth = outerWidth;
LineHeightSpan[] chooseHt = null;
if (spanned != null) {
LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd, LeadingMarginSpan.class);
for (int i = 0; i < sp.length; i++) {
LeadingMarginSpan lms = sp[i];
firstWidth -= sp[i].getLeadingMargin(true);
restWidth -= sp[i].getLeadingMargin(false);
// leading margin spans, not just this particular one
if (lms instanceof LeadingMarginSpan2) {
LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
firstWidthLineCount = Math.max(firstWidthLineCount, lms2.getLeadingMarginLineCount());
}
}
chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
if (chooseHt.length == 0) {
// So that out() would not assume it has any contents
chooseHt = null;
} else {
if (chooseHtv == null || chooseHtv.length < chooseHt.length) {
chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
}
for (int i = 0; i < chooseHt.length; i++) {
int o = spanned.getSpanStart(chooseHt[i]);
if (o < paraStart) {
// starts in this layout, before the
// current paragraph
chooseHtv[i] = getLineTop(getLineForOffset(o));
} else {
// starts in this paragraph
chooseHtv[i] = v;
}
}
}
}
measured.setPara(source, paraStart, paraEnd, textDir, b);
char[] chs = measured.mChars;
float[] widths = measured.mWidths;
byte[] chdirs = measured.mLevels;
int dir = measured.mDir;
boolean easy = measured.mEasy;
// tab stop locations
int[] variableTabStops = null;
if (spanned != null) {
TabStopSpan[] spans = getParagraphSpans(spanned, paraStart, paraEnd, TabStopSpan.class);
if (spans.length > 0) {
int[] stops = new int[spans.length];
for (int i = 0; i < spans.length; i++) {
stops[i] = spans[i].getTabStop();
}
Arrays.sort(stops, 0, stops.length);
variableTabStops = stops;
}
}
nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart, firstWidth, firstWidthLineCount, restWidth, variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency);
if (mLeftIndents != null || mRightIndents != null) {
// TODO(raph) performance: it would be better to do this once per layout rather
// than once per paragraph, but that would require a change to the native
// interface.
int leftLen = mLeftIndents == null ? 0 : mLeftIndents.length;
int rightLen = mRightIndents == null ? 0 : mRightIndents.length;
int indentsLen = Math.max(1, Math.max(leftLen, rightLen) - mLineCount);
int[] indents = new int[indentsLen];
for (int i = 0; i < indentsLen; i++) {
int leftMargin = mLeftIndents == null ? 0 : mLeftIndents[Math.min(i + mLineCount, leftLen - 1)];
int rightMargin = mRightIndents == null ? 0 : mRightIndents[Math.min(i + mLineCount, rightLen - 1)];
indents[i] = leftMargin + rightMargin;
}
nSetIndents(b.mNativePtr, indents);
}
// measurement has to be done before performing line breaking
// but we don't want to recompute fontmetrics or span ranges the
// second time, so we cache those and then use those stored values
int fmCacheCount = 0;
int spanEndCacheCount = 0;
for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
if (fmCacheCount * 4 >= fmCache.length) {
int[] grow = new int[fmCacheCount * 4 * 2];
System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4);
fmCache = grow;
}
if (spanEndCacheCount >= spanEndCache.length) {
int[] grow = new int[spanEndCacheCount * 2];
System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount);
spanEndCache = grow;
}
if (spanned == null) {
spanEnd = paraEnd;
int spanLen = spanEnd - spanStart;
measured.addStyleRun(paint, spanLen, fm);
} else {
spanEnd = spanned.nextSpanTransition(spanStart, paraEnd, MetricAffectingSpan.class);
int spanLen = spanEnd - spanStart;
MetricAffectingSpan[] spans = spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
spans = TextUtils.removeEmptySpans(spans, spanned, MetricAffectingSpan.class);
measured.addStyleRun(paint, spans, spanLen, fm);
}
// the order of storage here (top, bottom, ascent, descent) has to match the code below
// where these values are retrieved
fmCache[fmCacheCount * 4 + 0] = fm.top;
fmCache[fmCacheCount * 4 + 1] = fm.bottom;
fmCache[fmCacheCount * 4 + 2] = fm.ascent;
fmCache[fmCacheCount * 4 + 3] = fm.descent;
fmCacheCount++;
spanEndCache[spanEndCacheCount] = spanEnd;
spanEndCacheCount++;
}
nGetWidths(b.mNativePtr, widths);
int breakCount = nComputeLineBreaks(b.mNativePtr, lineBreaks, lineBreaks.breaks, lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length);
int[] breaks = lineBreaks.breaks;
float[] lineWidths = lineBreaks.widths;
int[] flags = lineBreaks.flags;
final int remainingLineCount = mMaximumVisibleLineCount - mLineCount;
final boolean ellipsisMayBeApplied = ellipsize != null && (ellipsize == TextUtils.TruncateAt.END || (mMaximumVisibleLineCount == 1 && ellipsize != TextUtils.TruncateAt.MARQUEE));
if (remainingLineCount > 0 && remainingLineCount < breakCount && ellipsisMayBeApplied) {
// Calculate width and flag.
float width = 0;
int flag = 0;
for (int i = remainingLineCount - 1; i < breakCount; i++) {
if (i == breakCount - 1) {
width += lineWidths[i];
} else {
for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
width += widths[j];
}
}
flag |= flags[i] & TAB_MASK;
}
// Treat the last line and overflowed lines as a single line.
breaks[remainingLineCount - 1] = breaks[breakCount - 1];
lineWidths[remainingLineCount - 1] = width;
flags[remainingLineCount - 1] = flag;
breakCount = remainingLineCount;
}
// here is the offset of the starting character of the line we are currently measuring
int here = paraStart;
int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0;
int fmCacheIndex = 0;
int spanEndCacheIndex = 0;
int breakIndex = 0;
for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
// retrieve end of span
spanEnd = spanEndCache[spanEndCacheIndex++];
// retrieve cached metrics, order matches above
fm.top = fmCache[fmCacheIndex * 4 + 0];
fm.bottom = fmCache[fmCacheIndex * 4 + 1];
fm.ascent = fmCache[fmCacheIndex * 4 + 2];
fm.descent = fmCache[fmCacheIndex * 4 + 3];
fmCacheIndex++;
if (fm.top < fmTop) {
fmTop = fm.top;
}
if (fm.ascent < fmAscent) {
fmAscent = fm.ascent;
}
if (fm.descent > fmDescent) {
fmDescent = fm.descent;
}
if (fm.bottom > fmBottom) {
fmBottom = fm.bottom;
}
// skip breaks ending before current span range
while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
breakIndex++;
}
while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) {
int endPos = paraStart + breaks[breakIndex];
boolean moreChars = (endPos < bufEnd);
v = out(source, here, endPos, fmAscent, fmDescent, fmTop, fmBottom, v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, flags[breakIndex], needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad, chs, widths, paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex], paint, moreChars);
if (endPos < spanEnd) {
// preserve metrics for current span
fmTop = fm.top;
fmBottom = fm.bottom;
fmAscent = fm.ascent;
fmDescent = fm.descent;
} else {
fmTop = fmBottom = fmAscent = fmDescent = 0;
}
here = endPos;
breakIndex++;
if (mLineCount >= mMaximumVisibleLineCount) {
return;
}
}
}
if (paraEnd == bufEnd)
break;
}
if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) && mLineCount < mMaximumVisibleLineCount) {
// Log.e("text", "output last " + bufEnd);
measured.setPara(source, bufEnd, bufEnd, textDir, b);
paint.getFontMetricsInt(fm);
v = out(source, bufEnd, bufEnd, fm.ascent, fm.descent, fm.top, fm.bottom, v, spacingmult, spacingadd, null, null, fm, 0, needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd, includepad, trackpad, null, null, bufStart, ellipsize, ellipsizedWidth, 0, paint, false);
}
}
Aggregations