• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* libs/graphics/views/SkTextBox.cpp
2 **
3 ** Copyright 2006, The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 **     http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17 
18 #include "SkTextBox.h"
19 #include "SkGlyphCache.h"
20 #include "SkUtils.h"
21 #include "SkAutoKern.h"
22 
is_ws(int c)23 static inline int is_ws(int c)
24 {
25     return !((c - 1) >> 5);
26 }
27 
linebreak(const char text[],const char stop[],const SkPaint & paint,SkScalar margin)28 static size_t linebreak(const char text[], const char stop[], const SkPaint& paint, SkScalar margin)
29 {
30     const char* start = text;
31 
32     SkAutoGlyphCache    ac(paint, NULL);
33     SkGlyphCache*       cache = ac.getCache();
34     SkFixed             w = 0;
35     SkFixed             limit = SkScalarToFixed(margin);
36     SkAutoKern          autokern;
37 
38     const char* word_start = text;
39     int         prevWS = true;
40 
41     while (text < stop)
42     {
43         const char* prevText = text;
44         SkUnichar   uni = SkUTF8_NextUnichar(&text);
45         int         currWS = is_ws(uni);
46         const SkGlyph&  glyph = cache->getUnicharMetrics(uni);
47 
48         if (!currWS && prevWS)
49             word_start = prevText;
50         prevWS = currWS;
51 
52         w += autokern.adjust(glyph) + glyph.fAdvanceX;
53         if (w > limit)
54         {
55             if (currWS) // eat the rest of the whitespace
56             {
57                 while (text < stop && is_ws(SkUTF8_ToUnichar(text)))
58                     text += SkUTF8_CountUTF8Bytes(text);
59             }
60             else    // backup until a whitespace (or 1 char)
61             {
62                 if (word_start == start)
63                 {
64                     if (prevText > start)
65                         text = prevText;
66                 }
67                 else
68                     text = word_start;
69             }
70             break;
71         }
72     }
73     return text - start;
74 }
75 
CountLines(const char text[],size_t len,const SkPaint & paint,SkScalar width)76 int SkTextLineBreaker::CountLines(const char text[], size_t len, const SkPaint& paint, SkScalar width)
77 {
78     const char* stop = text + len;
79     int         count = 0;
80 
81     if (width > 0)
82     {
83         do {
84             count += 1;
85             text += linebreak(text, stop, paint, width);
86         } while (text < stop);
87     }
88     return count;
89 }
90 
91 //////////////////////////////////////////////////////////////////////////////
92 
SkTextBox()93 SkTextBox::SkTextBox()
94 {
95     fBox.setEmpty();
96     fSpacingMul = SK_Scalar1;
97     fSpacingAdd = 0;
98     fMode = kLineBreak_Mode;
99     fSpacingAlign = kStart_SpacingAlign;
100 }
101 
setMode(Mode mode)102 void SkTextBox::setMode(Mode mode)
103 {
104     SkASSERT((unsigned)mode < kModeCount);
105     fMode = SkToU8(mode);
106 }
107 
setSpacingAlign(SpacingAlign align)108 void SkTextBox::setSpacingAlign(SpacingAlign align)
109 {
110     SkASSERT((unsigned)align < kSpacingAlignCount);
111     fSpacingAlign = SkToU8(align);
112 }
113 
getBox(SkRect * box) const114 void SkTextBox::getBox(SkRect* box) const
115 {
116     if (box)
117         *box = fBox;
118 }
119 
setBox(const SkRect & box)120 void SkTextBox::setBox(const SkRect& box)
121 {
122     fBox = box;
123 }
124 
setBox(SkScalar left,SkScalar top,SkScalar right,SkScalar bottom)125 void SkTextBox::setBox(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom)
126 {
127     fBox.set(left, top, right, bottom);
128 }
129 
getSpacing(SkScalar * mul,SkScalar * add) const130 void SkTextBox::getSpacing(SkScalar* mul, SkScalar* add) const
131 {
132     if (mul)
133         *mul = fSpacingMul;
134     if (add)
135         *add = fSpacingAdd;
136 }
137 
setSpacing(SkScalar mul,SkScalar add)138 void SkTextBox::setSpacing(SkScalar mul, SkScalar add)
139 {
140     fSpacingMul = mul;
141     fSpacingAdd = add;
142 }
143 
144 /////////////////////////////////////////////////////////////////////////////////////////////
145 
draw(SkCanvas * canvas,const char text[],size_t len,const SkPaint & paint)146 void SkTextBox::draw(SkCanvas* canvas, const char text[], size_t len, const SkPaint& paint)
147 {
148     SkASSERT(canvas && &paint && (text || len == 0));
149 
150     SkScalar marginWidth = fBox.width();
151 
152     if (marginWidth <= 0 || len == 0)
153         return;
154 
155     const char* textStop = text + len;
156 
157     SkScalar                x, y, scaledSpacing, height, fontHeight;
158     SkPaint::FontMetrics    metrics;
159 
160     switch (paint.getTextAlign()) {
161     case SkPaint::kLeft_Align:
162         x = 0;
163         break;
164     case SkPaint::kCenter_Align:
165         x = SkScalarHalf(marginWidth);
166         break;
167     default:
168         x = marginWidth;
169         break;
170     }
171     x += fBox.fLeft;
172 
173     fontHeight = paint.getFontMetrics(&metrics);
174     scaledSpacing = SkScalarMul(fontHeight, fSpacingMul) + fSpacingAdd;
175     height = fBox.height();
176 
177     //  compute Y position for first line
178     {
179         SkScalar textHeight = fontHeight;
180 
181         if (fMode == kLineBreak_Mode && fSpacingAlign != kStart_SpacingAlign)
182         {
183             int count = SkTextLineBreaker::CountLines(text, textStop - text, paint, marginWidth);
184             SkASSERT(count > 0);
185             textHeight += scaledSpacing * (count - 1);
186         }
187 
188         switch (fSpacingAlign) {
189         case kStart_SpacingAlign:
190             y = 0;
191             break;
192         case kCenter_SpacingAlign:
193             y = SkScalarHalf(height - textHeight);
194             break;
195         default:
196             SkASSERT(fSpacingAlign == kEnd_SpacingAlign);
197             y = height - textHeight;
198             break;
199         }
200         y += fBox.fTop - metrics.fAscent;
201     }
202 
203     for (;;)
204     {
205         len = linebreak(text, textStop, paint, marginWidth);
206         if (y + metrics.fDescent + metrics.fLeading > 0)
207             canvas->drawText(text, len, x, y, paint);
208         text += len;
209         if (text >= textStop)
210             break;
211         y += scaledSpacing;
212         if (y + metrics.fAscent >= height)
213             break;
214     }
215 }
216 
217