diff options
author | reed <reed@google.com> | 2014-11-03 14:55:40 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-11-03 14:55:40 -0800 |
commit | 615c5937807cd48aed6a0a7b6e3f2182f2a4ad8a (patch) | |
tree | 8cfb1503f4edea965f3c4fef922020eb560adaa7 /src/utils | |
parent | fb502f072f5a32ad122bfc2717fe56a3c22e482e (diff) |
move SkTextBox into utils
BUG=skia:
TBR=
Review URL: https://codereview.chromium.org/696293005
Diffstat (limited to 'src/utils')
-rw-r--r-- | src/utils/SkTextBox.cpp | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/src/utils/SkTextBox.cpp b/src/utils/SkTextBox.cpp new file mode 100644 index 0000000000..55d75a64da --- /dev/null +++ b/src/utils/SkTextBox.cpp @@ -0,0 +1,259 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTextBox.h" +#include "SkUtils.h" + +static inline int is_ws(int c) +{ + return !((c - 1) >> 5); +} + +static size_t linebreak(const char text[], const char stop[], + const SkPaint& paint, SkScalar margin, + size_t* trailing = NULL) +{ + size_t lengthBreak = paint.breakText(text, stop - text, margin); + + //Check for white space or line breakers before the lengthBreak + const char* start = text; + const char* word_start = text; + int prevWS = true; + if (trailing) { + *trailing = 0; + } + + while (text < stop) { + const char* prevText = text; + SkUnichar uni = SkUTF8_NextUnichar(&text); + int currWS = is_ws(uni); + + if (!currWS && prevWS) { + word_start = prevText; + } + prevWS = currWS; + + if (text > start + lengthBreak) { + if (currWS) { + // eat the rest of the whitespace + while (text < stop && is_ws(SkUTF8_ToUnichar(text))) { + text += SkUTF8_CountUTF8Bytes(text); + } + if (trailing) { + *trailing = text - prevText; + } + } else { + // backup until a whitespace (or 1 char) + if (word_start == start) { + if (prevText > start) { + text = prevText; + } + } else { + text = word_start; + } + } + break; + } + + if ('\n' == uni) { + size_t ret = text - start; + size_t lineBreakSize = 1; + if (text < stop) { + uni = SkUTF8_NextUnichar(&text); + if ('\r' == uni) { + ret = text - start; + ++lineBreakSize; + } + } + if (trailing) { + *trailing = lineBreakSize; + } + return ret; + } + + if ('\r' == uni) { + size_t ret = text - start; + size_t lineBreakSize = 1; + if (text < stop) { + uni = SkUTF8_NextUnichar(&text); + if ('\n' == uni) { + ret = text - start; + ++lineBreakSize; + } + } + if (trailing) { + *trailing = lineBreakSize; + } + return ret; + } + } + + return text - start; +} + +int SkTextLineBreaker::CountLines(const char text[], size_t len, const SkPaint& paint, SkScalar width) +{ + const char* stop = text + len; + int count = 0; + + if (width > 0) + { + do { + count += 1; + text += linebreak(text, stop, paint, width); + } while (text < stop); + } + return count; +} + +////////////////////////////////////////////////////////////////////////////// + +SkTextBox::SkTextBox() +{ + fBox.setEmpty(); + fSpacingMul = SK_Scalar1; + fSpacingAdd = 0; + fMode = kLineBreak_Mode; + fSpacingAlign = kStart_SpacingAlign; +} + +void SkTextBox::setMode(Mode mode) +{ + SkASSERT((unsigned)mode < kModeCount); + fMode = SkToU8(mode); +} + +void SkTextBox::setSpacingAlign(SpacingAlign align) +{ + SkASSERT((unsigned)align < kSpacingAlignCount); + fSpacingAlign = SkToU8(align); +} + +void SkTextBox::getBox(SkRect* box) const +{ + if (box) + *box = fBox; +} + +void SkTextBox::setBox(const SkRect& box) +{ + fBox = box; +} + +void SkTextBox::setBox(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) +{ + fBox.set(left, top, right, bottom); +} + +void SkTextBox::getSpacing(SkScalar* mul, SkScalar* add) const +{ + if (mul) + *mul = fSpacingMul; + if (add) + *add = fSpacingAdd; +} + +void SkTextBox::setSpacing(SkScalar mul, SkScalar add) +{ + fSpacingMul = mul; + fSpacingAdd = add; +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +void SkTextBox::draw(SkCanvas* canvas, const char text[], size_t len, const SkPaint& paint) +{ + SkASSERT(canvas && (text || len == 0)); + + SkScalar marginWidth = fBox.width(); + + if (marginWidth <= 0 || len == 0) + return; + + const char* textStop = text + len; + + SkScalar x, y, scaledSpacing, height, fontHeight; + SkPaint::FontMetrics metrics; + + switch (paint.getTextAlign()) { + case SkPaint::kLeft_Align: + x = 0; + break; + case SkPaint::kCenter_Align: + x = SkScalarHalf(marginWidth); + break; + default: + x = marginWidth; + break; + } + x += fBox.fLeft; + + fontHeight = paint.getFontMetrics(&metrics); + scaledSpacing = SkScalarMul(fontHeight, fSpacingMul) + fSpacingAdd; + height = fBox.height(); + + // compute Y position for first line + { + SkScalar textHeight = fontHeight; + + if (fMode == kLineBreak_Mode && fSpacingAlign != kStart_SpacingAlign) + { + int count = SkTextLineBreaker::CountLines(text, textStop - text, paint, marginWidth); + SkASSERT(count > 0); + textHeight += scaledSpacing * (count - 1); + } + + switch (fSpacingAlign) { + case kStart_SpacingAlign: + y = 0; + break; + case kCenter_SpacingAlign: + y = SkScalarHalf(height - textHeight); + break; + default: + SkASSERT(fSpacingAlign == kEnd_SpacingAlign); + y = height - textHeight; + break; + } + y += fBox.fTop - metrics.fAscent; + } + + for (;;) + { + size_t trailing; + len = linebreak(text, textStop, paint, marginWidth, &trailing); + if (y + metrics.fDescent + metrics.fLeading > 0) + canvas->drawText(text, len - trailing, x, y, paint); + text += len; + if (text >= textStop) + break; + y += scaledSpacing; + if (y + metrics.fAscent >= fBox.fBottom) + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkTextBox::setText(const char text[], size_t len, const SkPaint& paint) { + fText = text; + fLen = len; + fPaint = &paint; +} + +void SkTextBox::draw(SkCanvas* canvas) { + this->draw(canvas, fText, fLen, *fPaint); +} + +int SkTextBox::countLines() const { + return SkTextLineBreaker::CountLines(fText, fLen, *fPaint, fBox.width()); +} + +SkScalar SkTextBox::getTextHeight() const { + SkScalar spacing = SkScalarMul(fPaint->getTextSize(), fSpacingMul) + fSpacingAdd; + return this->countLines() * spacing; +} |