Home | History | Annotate | Download | only in views
      1 /*
      2  * Copyright 2006 The Android Open Source Project
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkTextBox.h"
      9 #include "SkUtils.h"
     10 
     11 static inline int is_ws(int c)
     12 {
     13     return !((c - 1) >> 5);
     14 }
     15 
     16 static size_t linebreak(const char text[], const char stop[],
     17                         const SkPaint& paint, SkScalar margin,
     18                         size_t* trailing = NULL)
     19 {
     20     size_t lengthBreak = paint.breakText(text, stop - text, margin);
     21 
     22     //Check for white space or line breakers before the lengthBreak
     23     const char* start = text;
     24     const char* word_start = text;
     25     int prevWS = true;
     26     if (trailing) {
     27         *trailing = 0;
     28     }
     29 
     30     while (text < stop) {
     31         const char* prevText = text;
     32         SkUnichar uni = SkUTF8_NextUnichar(&text);
     33         int currWS = is_ws(uni);
     34 
     35         if (!currWS && prevWS) {
     36             word_start = prevText;
     37         }
     38         prevWS = currWS;
     39 
     40         if (text > start + lengthBreak) {
     41             if (currWS) {
     42                 // eat the rest of the whitespace
     43                 while (text < stop && is_ws(SkUTF8_ToUnichar(text))) {
     44                     text += SkUTF8_CountUTF8Bytes(text);
     45                 }
     46                 if (trailing) {
     47                     *trailing = text - prevText;
     48                 }
     49             } else {
     50                 // backup until a whitespace (or 1 char)
     51                 if (word_start == start) {
     52                     if (prevText > start) {
     53                         text = prevText;
     54                     }
     55                 } else {
     56                     text = word_start;
     57                 }
     58             }
     59             break;
     60         }
     61 
     62         if ('\n' == uni) {
     63             size_t ret = text - start;
     64             size_t lineBreakSize = 1;
     65             if (text < stop) {
     66                 uni = SkUTF8_NextUnichar(&text);
     67                 if ('\r' == uni) {
     68                     ret = text - start;
     69                     ++lineBreakSize;
     70                 }
     71             }
     72             if (trailing) {
     73                 *trailing = lineBreakSize;
     74             }
     75             return ret;
     76         }
     77 
     78         if ('\r' == uni) {
     79             size_t ret = text - start;
     80             size_t lineBreakSize = 1;
     81             if (text < stop) {
     82                 uni = SkUTF8_NextUnichar(&text);
     83                 if ('\n' == uni) {
     84                     ret = text - start;
     85                     ++lineBreakSize;
     86                 }
     87             }
     88             if (trailing) {
     89                 *trailing = lineBreakSize;
     90             }
     91             return ret;
     92         }
     93     }
     94 
     95     return text - start;
     96 }
     97 
     98 int SkTextLineBreaker::CountLines(const char text[], size_t len, const SkPaint& paint, SkScalar width)
     99 {
    100     const char* stop = text + len;
    101     int         count = 0;
    102 
    103     if (width > 0)
    104     {
    105         do {
    106             count += 1;
    107             text += linebreak(text, stop, paint, width);
    108         } while (text < stop);
    109     }
    110     return count;
    111 }
    112 
    113 //////////////////////////////////////////////////////////////////////////////
    114 
    115 SkTextBox::SkTextBox()
    116 {
    117     fBox.setEmpty();
    118     fSpacingMul = SK_Scalar1;
    119     fSpacingAdd = 0;
    120     fMode = kLineBreak_Mode;
    121     fSpacingAlign = kStart_SpacingAlign;
    122 }
    123 
    124 void SkTextBox::setMode(Mode mode)
    125 {
    126     SkASSERT((unsigned)mode < kModeCount);
    127     fMode = SkToU8(mode);
    128 }
    129 
    130 void SkTextBox::setSpacingAlign(SpacingAlign align)
    131 {
    132     SkASSERT((unsigned)align < kSpacingAlignCount);
    133     fSpacingAlign = SkToU8(align);
    134 }
    135 
    136 void SkTextBox::getBox(SkRect* box) const
    137 {
    138     if (box)
    139         *box = fBox;
    140 }
    141 
    142 void SkTextBox::setBox(const SkRect& box)
    143 {
    144     fBox = box;
    145 }
    146 
    147 void SkTextBox::setBox(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom)
    148 {
    149     fBox.set(left, top, right, bottom);
    150 }
    151 
    152 void SkTextBox::getSpacing(SkScalar* mul, SkScalar* add) const
    153 {
    154     if (mul)
    155         *mul = fSpacingMul;
    156     if (add)
    157         *add = fSpacingAdd;
    158 }
    159 
    160 void SkTextBox::setSpacing(SkScalar mul, SkScalar add)
    161 {
    162     fSpacingMul = mul;
    163     fSpacingAdd = add;
    164 }
    165 
    166 /////////////////////////////////////////////////////////////////////////////////////////////
    167 
    168 void SkTextBox::draw(SkCanvas* canvas, const char text[], size_t len, const SkPaint& paint)
    169 {
    170     SkASSERT(canvas && &paint && (text || len == 0));
    171 
    172     SkScalar marginWidth = fBox.width();
    173 
    174     if (marginWidth <= 0 || len == 0)
    175         return;
    176 
    177     const char* textStop = text + len;
    178 
    179     SkScalar                x, y, scaledSpacing, height, fontHeight;
    180     SkPaint::FontMetrics    metrics;
    181 
    182     switch (paint.getTextAlign()) {
    183     case SkPaint::kLeft_Align:
    184         x = 0;
    185         break;
    186     case SkPaint::kCenter_Align:
    187         x = SkScalarHalf(marginWidth);
    188         break;
    189     default:
    190         x = marginWidth;
    191         break;
    192     }
    193     x += fBox.fLeft;
    194 
    195     fontHeight = paint.getFontMetrics(&metrics);
    196     scaledSpacing = SkScalarMul(fontHeight, fSpacingMul) + fSpacingAdd;
    197     height = fBox.height();
    198 
    199     //  compute Y position for first line
    200     {
    201         SkScalar textHeight = fontHeight;
    202 
    203         if (fMode == kLineBreak_Mode && fSpacingAlign != kStart_SpacingAlign)
    204         {
    205             int count = SkTextLineBreaker::CountLines(text, textStop - text, paint, marginWidth);
    206             SkASSERT(count > 0);
    207             textHeight += scaledSpacing * (count - 1);
    208         }
    209 
    210         switch (fSpacingAlign) {
    211         case kStart_SpacingAlign:
    212             y = 0;
    213             break;
    214         case kCenter_SpacingAlign:
    215             y = SkScalarHalf(height - textHeight);
    216             break;
    217         default:
    218             SkASSERT(fSpacingAlign == kEnd_SpacingAlign);
    219             y = height - textHeight;
    220             break;
    221         }
    222         y += fBox.fTop - metrics.fAscent;
    223     }
    224 
    225     for (;;)
    226     {
    227         size_t trailing;
    228         len = linebreak(text, textStop, paint, marginWidth, &trailing);
    229         if (y + metrics.fDescent + metrics.fLeading > 0)
    230             canvas->drawText(text, len - trailing, x, y, paint);
    231         text += len;
    232         if (text >= textStop)
    233             break;
    234         y += scaledSpacing;
    235         if (y + metrics.fAscent >= fBox.fBottom)
    236             break;
    237     }
    238 }
    239 
    240 ///////////////////////////////////////////////////////////////////////////////
    241 
    242 void SkTextBox::setText(const char text[], size_t len, const SkPaint& paint) {
    243     fText = text;
    244     fLen = len;
    245     fPaint = &paint;
    246 }
    247 
    248 void SkTextBox::draw(SkCanvas* canvas) {
    249     this->draw(canvas, fText, fLen, *fPaint);
    250 }
    251 
    252 int SkTextBox::countLines() const {
    253     return SkTextLineBreaker::CountLines(fText, fLen, *fPaint, fBox.width());
    254 }
    255 
    256 SkScalar SkTextBox::getTextHeight() const {
    257     SkScalar spacing = SkScalarMul(fPaint->getTextSize(), fSpacingMul) + fSpacingAdd;
    258     return this->countLines() * spacing;
    259 }
    260