1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "text_span.h"
16
17 #include <iomanip>
18 #include <stack>
19 #include <utility>
20
21 #include <hb-icu.h>
22 #include <unicode/ubidi.h>
23
24 #include "font_collection.h"
25 #include "font_styles.h"
26 #include "measurer.h"
27 #include "texgine_dash_path_effect.h"
28 #include "texgine_exception.h"
29 #include "texgine_mask_filter.h"
30 #include "texgine_path.h"
31 #include "texgine_path_1d_path_effect.h"
32 #include "texgine/utils/exlog.h"
33 #ifdef LOGGER_ENABLE_SCOPE
34 #include "texgine/utils/trace.h"
35 #endif
36 #include "text_converter.h"
37 #include "word_breaker.h"
38 #include "symbol_engine/hm_symbol_run.h"
39 #include "utils/system_properties.h"
40
41 namespace OHOS {
42 namespace Rosen {
43 namespace TextEngine {
44 #define MAXRGB 255
45 #define MAXALPHA 255
46 #define OFFSETY 3
47 #define HALF 0.5f
48 #define DEFAULT_FONT_SIZE 14.0f
49 #define SCALE 0.7f
50 #define WIDTH_SCALAR 5.0f
51 #define HEIGHT_SCALAR 5.0f
52 #define DOTTED_ADVANCE 10.0f
53 #define WAVY_ADVANCE 6.0f
54 #define PHASE 0.0f
55 #define COUNT 2
56 #define MAX_BLURRADIUS 64u
57 #define POINTX0 0
58 #define POINTX1 1
59 #define POINTX2 2
60 #define POINTX3 3
61 #define POINTX4 4
62 #define POINTX5 5
63 #define POINTX6 6
64 #define POINTY0 0
65 #define POINTY2 2
66 #define POINTY4 4
67 #define POINTY6 6
68
69 #ifdef BUILD_NON_SDK_VER
70 const bool G_IS_HMSYMBOL_ENABLE = Drawing::SystemProperties::GetHMSymbolEnable();
71 #else
72 const bool G_IS_HMSYMBOL_ENABLE = true;
73 #endif
74
MakeEmpty()75 std::shared_ptr<TextSpan> TextSpan::MakeEmpty()
76 {
77 return std::make_shared<TextSpan>();
78 }
79
MakeFromText(const std::string & text)80 std::shared_ptr<TextSpan> TextSpan::MakeFromText(const std::string &text)
81 {
82 auto span = MakeEmpty();
83 auto decodeText = TextConverter::ToUTF16(text);
84 span->AddUTF16Text(decodeText);
85 return span;
86 }
87
MakeFromText(const std::u16string & text)88 std::shared_ptr<TextSpan> TextSpan::MakeFromText(const std::u16string &text)
89 {
90 auto span = MakeEmpty();
91 std::vector<uint16_t> u16;
92 for (const auto &t : text) {
93 u16.push_back(t);
94 }
95 span->AddUTF16Text(u16);
96 return span;
97 }
98
MakeFromText(const std::u32string & text)99 std::shared_ptr<TextSpan> TextSpan::MakeFromText(const std::u32string &text)
100 {
101 auto span = MakeEmpty();
102 std::vector<uint32_t> u32;
103 for (const auto &t : text) {
104 u32.push_back(t);
105 }
106 auto decodeText = TextConverter::ToUTF16(u32);
107 span->AddUTF16Text(decodeText);
108 return span;
109 }
110
MakeFromText(const std::vector<uint16_t> & text)111 std::shared_ptr<TextSpan> TextSpan::MakeFromText(const std::vector<uint16_t> &text)
112 {
113 auto span = MakeEmpty();
114 span->AddUTF16Text(text);
115 return span;
116 }
117
MakeFromText(const std::vector<uint32_t> & text)118 std::shared_ptr<TextSpan> TextSpan::MakeFromText(const std::vector<uint32_t> &text)
119 {
120 auto span = MakeEmpty();
121 auto decodeText = TextConverter::ToUTF16(text);
122 span->AddUTF16Text(decodeText);
123 return span;
124 }
125
MakeFromCharGroups(const CharGroups & cgs)126 std::shared_ptr<TextSpan> TextSpan::MakeFromCharGroups(const CharGroups &cgs)
127 {
128 auto span = MakeEmpty();
129 span->cgs_= cgs;
130 return span;
131 }
132
AddUTF16Text(const std::vector<uint16_t> & text)133 void TextSpan::AddUTF16Text(const std::vector<uint16_t> &text)
134 {
135 u16vect_.insert(u16vect_.end(), text.begin(), text.end());
136 }
137
CloneWithCharGroups(const CharGroups & cgs)138 std::shared_ptr<TextSpan> TextSpan::CloneWithCharGroups(const CharGroups &cgs)
139 {
140 auto span = MakeEmpty();
141 *span = *this;
142 span->cgs_ = cgs;
143 return span;
144 }
145
GetHeight() const146 double TextSpan::GetHeight() const
147 {
148 return *tmetrics_->fDescent_ - *tmetrics_->fAscent_;
149 }
150
GetWidth() const151 double TextSpan::GetWidth() const
152 {
153 return width_;
154 }
155
GetPreBreak() const156 double TextSpan::GetPreBreak() const
157 {
158 return preBreak_;
159 }
160
GetPostBreak() const161 double TextSpan::GetPostBreak() const
162 {
163 return postBreak_;
164 }
165
IsRTL() const166 bool TextSpan::IsRTL() const
167 {
168 return rtl_;
169 }
170
Paint(TexgineCanvas & canvas,double offsetX,double offsetY,const TextStyle & xs,const RoundRectType & rType)171 void TextSpan::Paint(TexgineCanvas &canvas, double offsetX, double offsetY, const TextStyle &xs,
172 const RoundRectType &rType)
173 {
174 TexginePaint paint;
175 paint.SetAntiAlias(true);
176 #ifndef USE_GRAPHIC_TEXT_GINE
177 paint.SetARGB(MAXRGB, MAXRGB, 0, 0);
178 #else
179 paint.SetAlpha(MAXALPHA);
180 #endif
181 if (xs.background.has_value()) {
182 auto rect = TexgineRect::MakeXYWH(offsetX, offsetY + *tmetrics_->fAscent_, width_,
183 *tmetrics_->fDescent_ - *tmetrics_->fAscent_);
184 #ifdef CROSS_PLATFORM
185 canvas.DrawRect(rect, xs.background.__get());
186 #else
187 canvas.DrawRect(rect, xs.background.value());
188 #endif
189 }
190
191 if (xs.backgroundRect.color != 0) {
192 paint.SetColor(xs.backgroundRect.color);
193 double ltRadius = 0.0;
194 double rtRadius = 0.0;
195 double rbRadius = 0.0;
196 double lbRadius = 0.0;
197 if (rType == RoundRectType::ALL || rType == RoundRectType::LEFT_ONLY) {
198 ltRadius = std::fmin(xs.backgroundRect.leftTopRadius, maxRoundRectRadius_);
199 lbRadius = std::fmin(xs.backgroundRect.leftBottomRadius, maxRoundRectRadius_);
200 }
201 if (rType == RoundRectType::ALL || rType == RoundRectType::RIGHT_ONLY) {
202 rtRadius = std::fmin(xs.backgroundRect.rightTopRadius, maxRoundRectRadius_);
203 rbRadius = std::fmin(xs.backgroundRect.rightBottomRadius, maxRoundRectRadius_);
204 }
205 const SkVector fRadii[4] = {{ltRadius, ltRadius}, {rtRadius, rtRadius}, {rbRadius, rbRadius},
206 {lbRadius, lbRadius}};
207 auto rect = TexgineRect::MakeRRect(offsetX, offsetY + topInGroup_, width_,
208 bottomInGroup_ - topInGroup_, fRadii);
209 paint.SetAntiAlias(false);
210 canvas.DrawRRect(rect, paint);
211 }
212
213 paint.SetAntiAlias(true);
214 paint.SetColor(xs.color);
215 if (xs.foreground.has_value()) {
216 #ifdef CROSS_PLATFORM
217 paint = xs.foreground.__get();
218 #else
219 paint = xs.foreground.value();
220 #endif
221 }
222
223 PaintShadow(canvas, offsetX, offsetY, xs.shadows);
224 if (xs.isSymbolGlyph && G_IS_HMSYMBOL_ENABLE) {
225 std::pair<double, double> offset(offsetX, offsetY);
226 HMSymbolRun hmSymbolRun = HMSymbolRun();
227 hmSymbolRun.SetAnimation(animationFunc_);
228 hmSymbolRun.SetSymbolId(symbolId_);
229 hmSymbolRun.DrawSymbol(canvas, textBlob_, offset, paint, xs);
230 } else {
231 canvas.DrawTextBlob(textBlob_, offsetX, offsetY, paint);
232 }
233 PaintDecoration(canvas, offsetX, offsetY, xs);
234 }
235
236
SymbolAnimation(const TextStyle & xs)237 void TextSpan::SymbolAnimation(const TextStyle &xs)
238 {
239 if (animationFunc_ == nullptr) {
240 return;
241 }
242 auto spanSymbolAnimationConfig = std::make_shared<SymbolAnimationConfig>();
243 if (spanSymbolAnimationConfig == nullptr) {
244 return;
245 }
246 spanSymbolAnimationConfig->effectStrategy = SymbolAnimationEffectStrategy(xs.symbol.GetEffectStrategy());
247 if (spanSymbolAnimationConfig->effectStrategy == SymbolAnimationEffectStrategy::SYMBOL_HIERARCHICAL) {
248 animationFunc_(spanSymbolAnimationConfig);
249 }
250 }
251
PaintDecoration(TexgineCanvas & canvas,double offsetX,double offsetY,const TextStyle & xs)252 void TextSpan::PaintDecoration(TexgineCanvas &canvas, double offsetX, double offsetY, const TextStyle &xs)
253 {
254 double left = offsetX;
255 double right = left + GetWidth();
256
257 if ((xs.decoration & TextDecoration::UNDERLINE) == TextDecoration::UNDERLINE) {
258 double y = offsetY + *tmetrics_->fUnderlinePosition_;
259 PaintDecorationStyle(canvas, left, right, y, xs);
260 }
261 if ((xs.decoration & TextDecoration::OVERLINE) == TextDecoration::OVERLINE) {
262 double y = offsetY - abs(*tmetrics_->fAscent_);
263 PaintDecorationStyle(canvas, left, right, y, xs);
264 }
265 if ((xs.decoration & TextDecoration::LINE_THROUGH) == TextDecoration::LINE_THROUGH) {
266 double y = offsetY - (*tmetrics_->fCapHeight_ * HALF) +
267 (xs.fontSize / DEFAULT_FONT_SIZE * xs.decorationThicknessScale * HALF);
268 PaintDecorationStyle(canvas, left, right, y, xs);
269 }
270 if ((xs.decoration & TextDecoration::BASELINE) == TextDecoration::BASELINE) {
271 double y = offsetY;
272 PaintDecorationStyle(canvas, left, right, y, xs);
273 }
274 }
275
PaintDecorationStyle(TexgineCanvas & canvas,double left,double right,double y,const TextStyle & xs)276 void TextSpan::PaintDecorationStyle(TexgineCanvas &canvas, double left, double right, double y, const TextStyle &xs)
277 {
278 TexginePaint paint;
279 paint.SetAntiAlias(true);
280 paint.SetARGB(MAXRGB, MAXRGB, 0, 0);
281 paint.SetColor(xs.decorationColor.value_or(xs.color));
282 paint.SetStrokeWidth(xs.fontSize / DEFAULT_FONT_SIZE * xs.decorationThicknessScale * SCALE);
283
284 switch (xs.decorationStyle) {
285 case TextDecorationStyle::SOLID:
286 break;
287 case TextDecorationStyle::DOUBLE:
288 canvas.DrawLine(left, y, right, y, paint);
289 y += OFFSETY;
290 break;
291 case TextDecorationStyle::DOTTED: {
292 TexginePath circle;
293 auto rect = TexgineRect::MakeWH(WIDTH_SCALAR, HEIGHT_SCALAR);
294 circle.AddOval(rect);
295 paint.SetPathEffect(TexginePath1DPathEffect::Make(circle, DOTTED_ADVANCE, PHASE,
296 TexginePath1DPathEffect::K_ROTATE_STYLE));
297 break;
298 }
299 case TextDecorationStyle::DASHED: {
300 const float intervals[2] = {WIDTH_SCALAR, HEIGHT_SCALAR};
301 paint.SetPathEffect(TexgineDashPathEffect::Make(intervals, COUNT, PHASE));
302 paint.SetStyle(TexginePaint::STROKE);
303 break;
304 }
305 case TextDecorationStyle::WAVY: {
306 TexginePath wavy;
307 float thickness = xs.decorationThicknessScale;
308 wavy.MoveTo({POINTX0, POINTY2 - thickness});
309 wavy.QuadTo({POINTX1, POINTY0 - thickness}, {POINTX2, POINTY2 - thickness});
310 wavy.LineTo({POINTX3, POINTY4 - thickness});
311 wavy.QuadTo({POINTX4, POINTY6 - thickness}, {POINTX5, POINTY4 - thickness});
312 wavy.LineTo({POINTX6, POINTY2 - thickness});
313 wavy.LineTo({POINTX6, POINTY2 + thickness});
314 wavy.LineTo({POINTX5, POINTY4 + thickness});
315 wavy.QuadTo({POINTX4, POINTY6 + thickness}, {POINTX3, POINTY4 + thickness});
316 wavy.LineTo({POINTX2, POINTY2 + thickness});
317 wavy.QuadTo({POINTX1, POINTY0 + thickness}, {POINTX0, POINTY2 + thickness});
318 wavy.LineTo({POINTX0, POINTY2 - thickness});
319 paint.SetPathEffect(TexginePath1DPathEffect::Make(wavy, WAVY_ADVANCE, PHASE,
320 TexginePath1DPathEffect::K_ROTATE_STYLE));
321 paint.SetStyle(TexginePaint::STROKE);
322 break;
323 }
324 }
325 canvas.DrawLine(left, y, right, y, paint);
326 }
327
PaintShadow(TexgineCanvas & canvas,double offsetX,double offsetY,const std::vector<TextShadow> & shadows)328 void TextSpan::PaintShadow(TexgineCanvas &canvas, double offsetX, double offsetY,
329 const std::vector<TextShadow> &shadows)
330 {
331 for (const auto &shadow : shadows) {
332 if (!shadow.HasShadow()) {
333 continue;
334 }
335 auto x = offsetX + shadow.offsetX;
336 auto y = offsetY + shadow.offsetY;
337 TexginePaint paint;
338 paint.SetAntiAlias(true);
339 paint.SetColor(shadow.color);
340 paint.SetMaskFilter(TexgineMaskFilter::MakeBlur(TexgineMaskFilter::K_NORMAL_SK_BLUR_STYLE,
341 shadow.blurLeave, false));
342
343 canvas.DrawTextBlob(textBlob_, x, y, paint);
344 }
345 }
346 } // namespace TextEngine
347 } // namespace Rosen
348 } // namespace OHOS
349