• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
is_ws(int c)15 static inline int is_ws(int c)
16 {
17     return !((c - 1) >> 5);
18 }
19 
linebreak(const char text[],const char stop[],const SkPaint & paint,SkScalar margin)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 
CountLines(const char text[],size_t len,const SkPaint & paint,SkScalar width)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 
SkTextBox()85 SkTextBox::SkTextBox()
86 {
87     fBox.setEmpty();
88     fSpacingMul = SK_Scalar1;
89     fSpacingAdd = 0;
90     fMode = kLineBreak_Mode;
91     fSpacingAlign = kStart_SpacingAlign;
92 }
93 
setMode(Mode mode)94 void SkTextBox::setMode(Mode mode)
95 {
96     SkASSERT((unsigned)mode < kModeCount);
97     fMode = SkToU8(mode);
98 }
99 
setSpacingAlign(SpacingAlign align)100 void SkTextBox::setSpacingAlign(SpacingAlign align)
101 {
102     SkASSERT((unsigned)align < kSpacingAlignCount);
103     fSpacingAlign = SkToU8(align);
104 }
105 
getBox(SkRect * box) const106 void SkTextBox::getBox(SkRect* box) const
107 {
108     if (box)
109         *box = fBox;
110 }
111 
setBox(const SkRect & box)112 void SkTextBox::setBox(const SkRect& box)
113 {
114     fBox = box;
115 }
116 
setBox(SkScalar left,SkScalar top,SkScalar right,SkScalar bottom)117 void SkTextBox::setBox(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom)
118 {
119     fBox.set(left, top, right, bottom);
120 }
121 
getSpacing(SkScalar * mul,SkScalar * add) const122 void SkTextBox::getSpacing(SkScalar* mul, SkScalar* add) const
123 {
124     if (mul)
125         *mul = fSpacingMul;
126     if (add)
127         *add = fSpacingAdd;
128 }
129 
setSpacing(SkScalar mul,SkScalar add)130 void SkTextBox::setSpacing(SkScalar mul, SkScalar add)
131 {
132     fSpacingMul = mul;
133     fSpacingAdd = add;
134 }
135 
136 /////////////////////////////////////////////////////////////////////////////////////////////
137 
draw(SkCanvas * canvas,const char text[],size_t len,const SkPaint & paint)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 
setText(const char text[],size_t len,const SkPaint & paint)211 void SkTextBox::setText(const char text[], size_t len, const SkPaint& paint) {
212     fText = text;
213     fLen = len;
214     fPaint = &paint;
215 }
216 
draw(SkCanvas * canvas)217 void SkTextBox::draw(SkCanvas* canvas) {
218     this->draw(canvas, fText, fLen, *fPaint);
219 }
220 
countLines() const221 int SkTextBox::countLines() const {
222     return SkTextLineBreaker::CountLines(fText, fLen, *fPaint, fBox.width());
223 }
224 
getTextHeight() const225 SkScalar SkTextBox::getTextHeight() const {
226     SkScalar spacing = SkScalarMul(fPaint->getTextSize(), fSpacingMul) + fSpacingAdd;
227     return this->countLines() * spacing;
228 }
229 
230