1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.. All rights reserved.
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 
16 #include "paragraph_impl.h"
17 
18 #include <algorithm>
19 #include <numeric>
20 
21 #include "include/core/SkMatrix.h"
22 #include "drawing_painter_impl.h"
23 #include "paragraph_builder_impl.h"
24 #include "skia_adapter/skia_convert_utils.h"
25 #include "text/font_metrics.h"
26 #include "text_line_impl.h"
27 #include "utils/text_log.h"
28 #include "utils/text_trace.h"
29 
30 namespace OHOS {
31 namespace Rosen {
32 namespace SPText {
33 namespace skt = skia::textlayout;
34 using PaintID = skt::ParagraphPainter::PaintID;
35 
36 namespace {
GetTxtFontWeight(int fontWeight)37 FontWeight GetTxtFontWeight(int fontWeight)
38 {
39     constexpr int minWeight = static_cast<int>(FontWeight::W100);
40     constexpr int maxWeight = static_cast<int>(FontWeight::W900);
41 
42     int weight = std::clamp((fontWeight - 100) / 100, minWeight, maxWeight);
43     return static_cast<FontWeight>(weight);
44 }
45 
GetTxtFontStyle(RSFontStyle::Slant slant)46 FontStyle GetTxtFontStyle(RSFontStyle::Slant slant)
47 {
48     return slant == RSFontStyle::Slant::UPRIGHT_SLANT ?
49         FontStyle::NORMAL : FontStyle::ITALIC;
50 }
51 
GetTxtTextBoxes(const std::vector<skt::TextBox> & skiaBoxes)52 std::vector<TextBox> GetTxtTextBoxes(const std::vector<skt::TextBox>& skiaBoxes)
53 {
54     std::vector<TextBox> boxes;
55     for (const skt::TextBox& box : skiaBoxes) {
56         boxes.emplace_back(box.rect, static_cast<TextDirection>(box.direction));
57     }
58     return boxes;
59 }
60 } // anonymous namespace
61 
ParagraphImpl(std::unique_ptr<skt::Paragraph> paragraph,std::vector<PaintRecord> && paints)62 ParagraphImpl::ParagraphImpl(std::unique_ptr<skt::Paragraph> paragraph, std::vector<PaintRecord>&& paints)
63     : paragraph_(std::move(paragraph)), paints_(std::move(paints))
64 {
65     threadId_ = pthread_self();
66 }
67 
GetMaxWidth()68 double ParagraphImpl::GetMaxWidth()
69 {
70     RecordDifferentPthreadCall(__FUNCTION__);
71     return paragraph_->getMaxWidth();
72 }
73 
GetHeight()74 double ParagraphImpl::GetHeight()
75 {
76     RecordDifferentPthreadCall(__FUNCTION__);
77     return paragraph_->lineNumber() == 0 ? 0 : paragraph_->getHeight();
78 }
79 
GetLongestLine()80 double ParagraphImpl::GetLongestLine()
81 {
82     RecordDifferentPthreadCall(__FUNCTION__);
83     return paragraph_->getLongestLine();
84 }
85 
GetLongestLineWithIndent()86 double ParagraphImpl::GetLongestLineWithIndent()
87 {
88     RecordDifferentPthreadCall(__FUNCTION__);
89     return paragraph_->getLongestLineWithIndent();
90 }
91 
GetMinIntrinsicWidth()92 double ParagraphImpl::GetMinIntrinsicWidth()
93 {
94     RecordDifferentPthreadCall(__FUNCTION__);
95     return paragraph_->getMinIntrinsicWidth();
96 }
97 
GetMaxIntrinsicWidth()98 double ParagraphImpl::GetMaxIntrinsicWidth()
99 {
100     RecordDifferentPthreadCall(__FUNCTION__);
101     return paragraph_->getMaxIntrinsicWidth();
102 }
103 
GetAlphabeticBaseline()104 double ParagraphImpl::GetAlphabeticBaseline()
105 {
106     RecordDifferentPthreadCall(__FUNCTION__);
107     return paragraph_->getAlphabeticBaseline();
108 }
109 
GetIdeographicBaseline()110 double ParagraphImpl::GetIdeographicBaseline()
111 {
112     RecordDifferentPthreadCall(__FUNCTION__);
113     return paragraph_->getIdeographicBaseline();
114 }
115 
DidExceedMaxLines()116 bool ParagraphImpl::DidExceedMaxLines()
117 {
118     RecordDifferentPthreadCall(__FUNCTION__);
119     return paragraph_->didExceedMaxLines();
120 }
121 
GetLineCount() const122 size_t ParagraphImpl::GetLineCount() const
123 {
124     RecordDifferentPthreadCall(__FUNCTION__);
125     if (paragraph_ == nullptr || paragraph_->GetMaxLines() == 0) {
126         return 0;
127     }
128     return paragraph_->lineNumber();
129 }
130 
MarkDirty()131 void ParagraphImpl::MarkDirty()
132 {
133     RecordDifferentPthreadCall(__FUNCTION__);
134     if (paragraph_ == nullptr) {
135         return;
136     }
137     paragraph_->markDirty();
138 }
139 
GetUnresolvedGlyphsCount()140 int32_t ParagraphImpl::GetUnresolvedGlyphsCount()
141 {
142     RecordDifferentPthreadCall(__FUNCTION__);
143     if (paragraph_ == nullptr) {
144         return 0;
145     }
146     return paragraph_->unresolvedGlyphs();
147 }
148 
UpdateFontSize(size_t from,size_t to,float fontSize)149 void ParagraphImpl::UpdateFontSize(size_t from, size_t to, float fontSize)
150 {
151     RecordDifferentPthreadCall(__FUNCTION__);
152     if (paragraph_ == nullptr) {
153         return;
154     }
155     paragraph_->updateFontSize(from, to, fontSize);
156 }
157 
SetIndents(const std::vector<float> & indents)158 void ParagraphImpl::SetIndents(const std::vector<float>& indents)
159 {
160     RecordDifferentPthreadCall(__FUNCTION__);
161     paragraph_->setIndents(indents);
162 }
163 
DetectIndents(size_t index)164 float ParagraphImpl::DetectIndents(size_t index)
165 {
166     RecordDifferentPthreadCall(__FUNCTION__);
167     return paragraph_->detectIndents(index);
168 }
169 
Layout(double width)170 void ParagraphImpl::Layout(double width)
171 {
172     TEXT_TRACE_FUNC();
173     RecordDifferentPthreadCall(__FUNCTION__);
174     lineMetrics_.reset();
175     lineMetricsStyles_.clear();
176     paragraph_->layout(width);
177 }
178 
GetGlyphsBoundsTop()179 double ParagraphImpl::GetGlyphsBoundsTop()
180 {
181     RecordDifferentPthreadCall(__FUNCTION__);
182     return paragraph_->getGlyphsBoundsTop();
183 }
184 
GetGlyphsBoundsBottom()185 double ParagraphImpl::GetGlyphsBoundsBottom()
186 {
187     RecordDifferentPthreadCall(__FUNCTION__);
188     return paragraph_->getGlyphsBoundsBottom();
189 }
190 
GetGlyphsBoundsLeft()191 double ParagraphImpl::GetGlyphsBoundsLeft()
192 {
193     RecordDifferentPthreadCall(__FUNCTION__);
194     return paragraph_->getGlyphsBoundsLeft();
195 }
196 
GetGlyphsBoundsRight()197 double ParagraphImpl::GetGlyphsBoundsRight()
198 {
199     RecordDifferentPthreadCall(__FUNCTION__);
200     return paragraph_->getGlyphsBoundsRight();
201 }
202 
MeasureText()203 OHOS::Rosen::Drawing::FontMetrics ParagraphImpl::MeasureText()
204 {
205     RecordDifferentPthreadCall(__FUNCTION__);
206     return paragraph_->measureText();
207 }
208 
Paint(SkCanvas * canvas,double x,double y)209 void ParagraphImpl::Paint(SkCanvas* canvas, double x, double y)
210 {
211     RecordDifferentPthreadCall(__FUNCTION__);
212     paragraph_->paint(canvas, x, y);
213 }
214 
Paint(Drawing::Canvas * canvas,double x,double y)215 void ParagraphImpl::Paint(Drawing::Canvas* canvas, double x, double y)
216 {
217     RecordDifferentPthreadCall(__FUNCTION__);
218     RSCanvasParagraphPainter painter(canvas, paints_);
219     painter.SetAnimation(animationFunc_);
220     painter.SetParagraphId(id_);
221     paragraph_->paint(&painter, x, y);
222 }
223 
Paint(Drawing::Canvas * canvas,Drawing::Path * path,double hOffset,double vOffset)224 void ParagraphImpl::Paint(Drawing::Canvas* canvas, Drawing::Path* path, double hOffset, double vOffset)
225 {
226     RecordDifferentPthreadCall(__FUNCTION__);
227     RSCanvasParagraphPainter painter(canvas, paints_);
228     painter.SetAnimation(animationFunc_);
229     painter.SetParagraphId(id_);
230     paragraph_->paint(&painter, path, hOffset, vOffset);
231 }
232 
GetRectsForRange(size_t start,size_t end,RectHeightStyle rectHeightStyle,RectWidthStyle rectWidthStyle)233 std::vector<TextBox> ParagraphImpl::GetRectsForRange(size_t start, size_t end,
234     RectHeightStyle rectHeightStyle, RectWidthStyle rectWidthStyle)
235 {
236     RecordDifferentPthreadCall(__FUNCTION__);
237     std::vector<skt::TextBox> boxes =
238         paragraph_->getRectsForRange(start, end, static_cast<skt::RectHeightStyle>(rectHeightStyle),
239             static_cast<skt::RectWidthStyle>(rectWidthStyle));
240     return GetTxtTextBoxes(boxes);
241 }
242 
GetRectsForPlaceholders()243 std::vector<TextBox> ParagraphImpl::GetRectsForPlaceholders()
244 {
245     RecordDifferentPthreadCall(__FUNCTION__);
246     return GetTxtTextBoxes(paragraph_->getRectsForPlaceholders());
247 }
248 
GetGlyphPositionAtCoordinate(double dx,double dy)249 PositionWithAffinity ParagraphImpl::GetGlyphPositionAtCoordinate(double dx, double dy)
250 {
251     RecordDifferentPthreadCall(__FUNCTION__);
252     skt::PositionWithAffinity pos = paragraph_->getGlyphPositionAtCoordinate(dx, dy);
253     return PositionWithAffinity(pos.position, static_cast<Affinity>(pos.affinity));
254 }
255 
GetWordBoundary(size_t offset)256 Range<size_t> ParagraphImpl::GetWordBoundary(size_t offset)
257 {
258     RecordDifferentPthreadCall(__FUNCTION__);
259     skt::SkRange<size_t> range = paragraph_->getWordBoundary(offset);
260     return Range<size_t>(range.start, range.end);
261 }
262 
GetActualTextRange(int lineNumber,bool includeSpaces)263 Range<size_t> ParagraphImpl::GetActualTextRange(int lineNumber, bool includeSpaces)
264 {
265     RecordDifferentPthreadCall(__FUNCTION__);
266     if (lineNumber >=0 && lineNumber <= static_cast<int>(paragraph_->lineNumber())) {
267         skt::SkRange<size_t> range = paragraph_->getActualTextRange(lineNumber, includeSpaces);
268         return Range<size_t>(range.start, range.end);
269     } else {
270         return Range<size_t>(0, 0);
271     }
272 }
273 
GetLineMetrics()274 std::vector<skia::textlayout::LineMetrics> ParagraphImpl::GetLineMetrics()
275 {
276     RecordDifferentPthreadCall(__FUNCTION__);
277     std::vector<skt::LineMetrics> metrics;
278     if (!lineMetrics_) {
279         paragraph_->getLineMetrics(metrics);
280     }
281     return metrics;
282 }
283 
GetLineMetricsAt(int lineNumber,skt::LineMetrics * lineMetrics) const284 bool ParagraphImpl::GetLineMetricsAt(int lineNumber, skt::LineMetrics* lineMetrics) const
285 {
286     RecordDifferentPthreadCall(__FUNCTION__);
287     return paragraph_->getLineMetricsAt(lineNumber, lineMetrics);
288 }
289 
SkStyleToTextStyle(const skt::TextStyle & skStyle)290 TextStyle ParagraphImpl::SkStyleToTextStyle(const skt::TextStyle& skStyle)
291 {
292     RecordDifferentPthreadCall(__FUNCTION__);
293 
294     TextStyle txt;
295     txt.color = skStyle.getColor();
296     txt.decoration = static_cast<TextDecoration>(skStyle.getDecorationType());
297     txt.decorationColor = skStyle.getDecorationColor();
298     txt.decorationStyle = static_cast<TextDecorationStyle>(skStyle.getDecorationStyle());
299     txt.decorationThicknessMultiplier = SkScalarToDouble(skStyle.getDecorationThicknessMultiplier());
300     txt.fontWeight = GetTxtFontWeight(skStyle.getFontStyle().GetWeight());
301     txt.fontStyle = GetTxtFontStyle(skStyle.getFontStyle().GetSlant());
302 
303     txt.baseline = static_cast<TextBaseline>(skStyle.getTextBaseline());
304 
305     for (const SkString& fontFamily : skStyle.getFontFamilies()) {
306         txt.fontFamilies.emplace_back(fontFamily.c_str());
307     }
308 
309     txt.fontSize = SkScalarToDouble(skStyle.getFontSize());
310     txt.letterSpacing = SkScalarToDouble(skStyle.getLetterSpacing());
311     txt.wordSpacing = SkScalarToDouble(skStyle.getWordSpacing());
312     txt.height = SkScalarToDouble(skStyle.getHeight());
313 
314     txt.locale = skStyle.getLocale().c_str();
315     if (skStyle.hasBackground()) {
316         PaintID backgroundId = std::get<PaintID>(skStyle.getBackgroundPaintOrID());
317         if ((0 <= backgroundId) && (backgroundId < static_cast<int>(paints_.size()))) {
318             txt.background = paints_[backgroundId];
319         } else {
320             TEXT_LOGW("Invalid background Id:%{public}d", backgroundId);
321         }
322     }
323     if (skStyle.hasForeground()) {
324         PaintID foregroundId = std::get<PaintID>(skStyle.getForegroundPaintOrID());
325         if ((0 <= foregroundId) && (foregroundId < static_cast<int>(paints_.size()))) {
326             txt.foreground = paints_[foregroundId];
327         } else {
328             TEXT_LOGW("Invalid foreground Id:%{public}d", foregroundId);
329         }
330     }
331 
332     txt.textShadows.clear();
333     for (const skt::TextShadow& skShadow : skStyle.getShadows()) {
334         TextShadow shadow;
335         shadow.offset = skShadow.fOffset;
336         shadow.blurSigma = skShadow.fBlurSigma;
337         shadow.color = skShadow.fColor;
338         txt.textShadows.emplace_back(shadow);
339     }
340 
341     return txt;
342 }
343 
GetFontMetricsResult(const SPText::TextStyle & textStyle)344 Drawing::FontMetrics ParagraphImpl::GetFontMetricsResult(const SPText::TextStyle& textStyle)
345 {
346     RecordDifferentPthreadCall(__FUNCTION__);
347 
348     auto skTextStyle = ParagraphBuilderImpl::ConvertTextStyleToSkStyle(textStyle);
349     OHOS::Rosen::Drawing::FontMetrics fontMetrics;
350     skTextStyle.getFontMetrics(&fontMetrics);
351     return fontMetrics;
352 }
353 
GetLineFontMetrics(const size_t lineNumber,size_t & charNumber,std::vector<Drawing::FontMetrics> & fontMetrics)354 bool ParagraphImpl::GetLineFontMetrics(const size_t lineNumber, size_t& charNumber,
355     std::vector<Drawing::FontMetrics>& fontMetrics)
356 {
357     if (!paragraph_) {
358         return false;
359     }
360     return paragraph_->GetLineFontMetrics(lineNumber, charNumber, fontMetrics);
361 }
362 
GetTextLines() const363 std::vector<std::unique_ptr<SPText::TextLineBase>> ParagraphImpl::GetTextLines() const
364 {
365     RecordDifferentPthreadCall(__FUNCTION__);
366     if (!paragraph_) {
367         return {};
368     }
369     std::vector<std::unique_ptr<skt::TextLineBase>> textLineBases = paragraph_->GetTextLines();
370     std::vector<std::unique_ptr<SPText::TextLineBase>> lines;
371     for (std::unique_ptr<skt::TextLineBase>& textLineBase : textLineBases) {
372         std::unique_ptr<SPText::TextLineImpl> textLinePtr =
373             std::make_unique<SPText::TextLineImpl>(std::move(textLineBase), paints_);
374         lines.emplace_back(std::move(textLinePtr));
375     }
376     return lines;
377 }
378 
CloneSelf()379 std::unique_ptr<Paragraph> ParagraphImpl::CloneSelf()
380 {
381     RecordDifferentPthreadCall(__FUNCTION__);
382     if (!paragraph_) {
383         return nullptr;
384     }
385     std::vector<PaintRecord> paints = paints_;
386     std::unique_ptr<skt::Paragraph> sktParagraph = paragraph_->CloneSelf();
387     std::unique_ptr<ParagraphImpl> paragraph = std::make_unique<ParagraphImpl>(std::move(sktParagraph),
388         std::move(paints));
389     return paragraph;
390 }
391 
UpdateColor(size_t from,size_t to,const RSColor & color)392 void ParagraphImpl::UpdateColor(size_t from, size_t to, const RSColor& color)
393 {
394     RecordDifferentPthreadCall(__FUNCTION__);
395     if (!paragraph_) {
396         return;
397     }
398     auto unresolvedPaintID = paragraph_->updateColor(from, to,
399         SkColorSetARGB(color.GetAlpha(), color.GetRed(), color.GetGreen(), color.GetBlue()));
400     for (auto paintID : unresolvedPaintID) {
401         paints_[paintID].SetColor(color);
402     }
403 }
404 
RecordDifferentPthreadCall(const char * caller) const405 void ParagraphImpl::RecordDifferentPthreadCall(const char* caller) const
406 {
407     pthread_t currenetThreadId = pthread_self();
408     if (threadId_ != currenetThreadId) {
409         TEXT_LOGE_LIMIT3_HOUR("New pthread access paragraph builder, old %{public}lu, caller %{public}s",
410             threadId_, caller);
411         threadId_ = currenetThreadId;
412     }
413 }
414 
GeneratePaintRegion(double x,double y)415 Drawing::RectI ParagraphImpl::GeneratePaintRegion(double x, double y)
416 {
417     RecordDifferentPthreadCall("GeneratePaintRegion");
418     if (!paragraph_) {
419         double left = std::floor(x);
420         double top = std::floor(y);
421         return Drawing::RectI(left, top, left, top);
422     }
423 
424     SkIRect skIRect = paragraph_->generatePaintRegion(SkDoubleToScalar(x), SkDoubleToScalar(y));
425     return Drawing::RectI(skIRect.left(), skIRect.top(), skIRect.right(), skIRect.bottom());
426 }
427 } // namespace SPText
428 } // namespace Rosen
429 } // namespace OHOS
430