• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
is_ws(int c)11 static inline int is_ws(int c)
12 {
13     return !((c - 1) >> 5);
14 }
15 
linebreak(const char text[],const char stop[],const SkPaint & paint,SkScalar margin,size_t * trailing=NULL)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 
CountLines(const char text[],size_t len,const SkPaint & paint,SkScalar width)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 
SkTextBox()115 SkTextBox::SkTextBox()
116 {
117     fBox.setEmpty();
118     fSpacingMul = SK_Scalar1;
119     fSpacingAdd = 0;
120     fMode = kLineBreak_Mode;
121     fSpacingAlign = kStart_SpacingAlign;
122 }
123 
setMode(Mode mode)124 void SkTextBox::setMode(Mode mode)
125 {
126     SkASSERT((unsigned)mode < kModeCount);
127     fMode = SkToU8(mode);
128 }
129 
setSpacingAlign(SpacingAlign align)130 void SkTextBox::setSpacingAlign(SpacingAlign align)
131 {
132     SkASSERT((unsigned)align < kSpacingAlignCount);
133     fSpacingAlign = SkToU8(align);
134 }
135 
getBox(SkRect * box) const136 void SkTextBox::getBox(SkRect* box) const
137 {
138     if (box)
139         *box = fBox;
140 }
141 
setBox(const SkRect & box)142 void SkTextBox::setBox(const SkRect& box)
143 {
144     fBox = box;
145 }
146 
setBox(SkScalar left,SkScalar top,SkScalar right,SkScalar bottom)147 void SkTextBox::setBox(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom)
148 {
149     fBox.set(left, top, right, bottom);
150 }
151 
getSpacing(SkScalar * mul,SkScalar * add) const152 void SkTextBox::getSpacing(SkScalar* mul, SkScalar* add) const
153 {
154     if (mul)
155         *mul = fSpacingMul;
156     if (add)
157         *add = fSpacingAdd;
158 }
159 
setSpacing(SkScalar mul,SkScalar add)160 void SkTextBox::setSpacing(SkScalar mul, SkScalar add)
161 {
162     fSpacingMul = mul;
163     fSpacingAdd = add;
164 }
165 
166 /////////////////////////////////////////////////////////////////////////////////////////////
167 
draw(SkCanvas * canvas,const char text[],size_t len,const SkPaint & paint)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 >= height)
236             break;
237     }
238 }
239 
240 ///////////////////////////////////////////////////////////////////////////////
241 
setText(const char text[],size_t len,const SkPaint & paint)242 void SkTextBox::setText(const char text[], size_t len, const SkPaint& paint) {
243     fText = text;
244     fLen = len;
245     fPaint = &paint;
246 }
247 
draw(SkCanvas * canvas)248 void SkTextBox::draw(SkCanvas* canvas) {
249     this->draw(canvas, fText, fLen, *fPaint);
250 }
251 
countLines() const252 int SkTextBox::countLines() const {
253     return SkTextLineBreaker::CountLines(fText, fLen, *fPaint, fBox.width());
254 }
255 
getTextHeight() const256 SkScalar SkTextBox::getTextHeight() const {
257     SkScalar spacing = SkScalarMul(fPaint->getTextSize(), fSpacingMul) + fSpacingAdd;
258     return this->countLines() * spacing;
259 }
260