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