OLD | NEW |
| (Empty) |
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 bot
tom) | |
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 SkPa
int& paint) | |
169 { | |
170 SkASSERT(canvas && (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, pai
nt, 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) + fSpacin
gAdd; | |
258 return this->countLines() * spacing; | |
259 } | |
OLD | NEW |