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#ifdef OHOS_SUPPORT
17#include "ParagraphImpl.h"
18#endif
19#include "modules/skparagraph/src/RunBaseImpl.h"
20
21namespace skia {
22namespace textlayout {
23RunBaseImpl::RunBaseImpl(
24#ifndef USE_SKIA_TXT
25    sk_sp<SkTextBlob> blob,
26#else
27    std::shared_ptr<RSTextBlob> blob,
28#endif
29    SkPoint offset,
30    ParagraphPainter::SkPaintOrID paint,
31    bool clippingNeeded,
32    SkRect clipRect,
33    const Run* visitorRun,
34    size_t visitorPos,
35#ifdef OHOS_SUPPORT
36    size_t visitorGlobalPos,
37    size_t trailSpaces,
38#endif
39    size_t visitorSize)
40    : fBlob(blob),
41    fOffset(offset),
42    fClippingNeeded(clippingNeeded),
43    fClipRect(clipRect),
44    fVisitorRun(visitorRun),
45    fVisitorPos(visitorPos),
46#ifdef OHOS_SUPPORT
47    fVisitorGlobalPos(visitorGlobalPos),
48    fTrailSpaces(trailSpaces),
49#endif
50    fVisitorSize(visitorSize)
51{
52    if (std::holds_alternative<SkPaint>(paint)) {
53        fPaint = std::get<SkPaint>(paint);
54    } else if (std::holds_alternative<ParagraphPainter::PaintID>(paint)) {
55        fPaint = std::get<ParagraphPainter::PaintID>(paint);
56    }
57
58}
59
60#ifndef USE_SKIA_TXT
61const SkFont& RunBaseImpl::font() const
62#else
63const RSFont& RunBaseImpl::font() const
64#endif
65{
66    if (!fVisitorRun) {
67        return {};
68    }
69    return fVisitorRun->font();
70}
71
72size_t RunBaseImpl::size() const
73{
74    return fVisitorSize;
75}
76
77std::vector<uint16_t> RunBaseImpl::getGlyphs() const
78{
79    if (!fVisitorRun) {
80        return {};
81    }
82    SkSpan<const SkGlyphID> glyphIDSpan = fVisitorRun->glyphs();
83    SkSpan<const SkGlyphID> runGlyphIDSpan = glyphIDSpan.subspan(fVisitorPos, fVisitorSize);
84    return std::vector<uint16_t>(runGlyphIDSpan.begin(), runGlyphIDSpan.end());
85}
86
87std::vector<RSPoint> RunBaseImpl::getPositions() const
88{
89    if (!fVisitorRun) {
90        return {};
91    }
92    SkSpan<const SkPoint> positionSpan = fVisitorRun->positions();
93    SkSpan<const SkPoint> runPositionSpan = positionSpan.subspan(fVisitorPos, fVisitorSize);
94    std::vector<RSPoint> positions;
95    for (size_t i = 0; i < runPositionSpan.size(); i++) {
96        RSPoint point(runPositionSpan[i].fX, runPositionSpan[i].fY);
97        positions.emplace_back(point);
98    }
99
100    return positions;
101
102}
103
104std::vector<RSPoint> RunBaseImpl::getOffsets() const
105{
106    if (!fVisitorRun) {
107        return {};
108    }
109    SkSpan<const SkPoint> offsetSpan = fVisitorRun->offsets();
110    SkSpan<const SkPoint> runOffsetSpan = offsetSpan.subspan(fVisitorPos, fVisitorSize);
111    std::vector<RSPoint> offsets;
112    for (size_t i = 0; i < runOffsetSpan.size(); i++) {
113        RSPoint point(runOffsetSpan[i].fX, runOffsetSpan[i].fY);
114        offsets.emplace_back(point);
115    }
116
117    return offsets;
118
119}
120
121void RunBaseImpl::paint(ParagraphPainter* painter, SkScalar x, SkScalar y)
122{
123    if (!painter) {
124        return;
125    }
126    if (fClippingNeeded) {
127        painter->save();
128        painter->clipRect(fClipRect.makeOffset(x, y));
129    }
130    painter->drawTextBlob(fBlob, x + fOffset.x(), y + fOffset.y(), fPaint);
131    if (fClippingNeeded) {
132        painter->restore();
133    }
134}
135
136size_t RunBaseImpl::getVisitorPos() const
137{
138    return fVisitorPos;
139}
140
141size_t RunBaseImpl::getVisitorSize() const
142{
143    return fVisitorSize;
144}
145
146#ifdef OHOS_SUPPORT
147std::vector<uint16_t> RunBaseImpl::getGlyphs(int64_t start, int64_t length) const
148{
149    if (!fVisitorRun) {
150        return {};
151    }
152    uint64_t actualLength = calculateActualLength(start, length);
153    if (actualLength == 0) {
154        return {};
155    }
156    SkSpan<const SkGlyphID> glyphIdSpan = fVisitorRun->glyphs();
157    SkSpan<const SkGlyphID> runGlyphIdSpan = glyphIdSpan.subspan(fVisitorPos + start, actualLength);
158    std::vector<uint16_t> glyphs;
159    for (size_t i = 0; i < runGlyphIdSpan.size(); i++) {
160        glyphs.emplace_back(runGlyphIdSpan[i]);
161    }
162
163    return glyphs;
164}
165
166#ifndef USE_SKIA_TXT
167std::vector<SkPoint> RunBaseImpl::getPositions(int64_t start, int64_t length) const
168#else
169std::vector<RSPoint> RunBaseImpl::getPositions(int64_t start, int64_t length) const
170#endif
171{
172    if (!fVisitorRun) {
173        return {};
174    }
175    uint64_t actualLength = calculateActualLength(start, length);
176    if (actualLength == 0) {
177        return {};
178    }
179    SkSpan<const SkPoint> positionSpan = fVisitorRun->positions();
180    SkSpan<const SkPoint> runPositionSpan = positionSpan.subspan(fVisitorPos + start, actualLength);
181#ifndef USE_SKIA_TXT
182    std::vector<SkPoint> positions;
183#else
184    std::vector<RSPoint> positions;
185#endif
186    for (size_t i = 0; i < runPositionSpan.size(); i++) {
187#ifndef USE_SKIA_TXT
188        positions.emplace_back(SkPoint::Make(runPositionSpan[i].fX, runPositionSpan[i].fY));
189#else
190        positions.emplace_back(runPositionSpan[i].fX, runPositionSpan[i].fY);
191#endif
192    }
193
194    return positions;
195}
196
197void RunBaseImpl::getStringRange(uint64_t* location, uint64_t* length) const
198{
199    if (location == nullptr || length == nullptr) {
200        return;
201    } else if (!fVisitorRun) {
202        *location = 0;
203        *length = 0;
204        return;
205    }
206    *location = fVisitorGlobalPos;
207    *length = fVisitorSize;
208}
209
210std::vector<uint64_t> RunBaseImpl::getStringIndices(int64_t start, int64_t length) const
211{
212    if (!fVisitorRun) {
213        return {};
214    }
215    uint64_t actualLength = calculateActualLength(start, length);
216    if (actualLength == 0) {
217        return {};
218    }
219    std::vector<uint64_t> indices;
220    for (size_t i = 0; i < actualLength; i++) {
221        indices.emplace_back(fVisitorGlobalPos + start + i);
222    }
223
224    return indices;
225}
226
227SkRect RunBaseImpl::getAllGlyphRectInfo(SkSpan<const SkGlyphID>& runGlyphIdSpan, size_t startNotWhiteSpaceIndex,
228    SkScalar startWhiteSpaceWidth, size_t endWhiteSpaceNum, SkScalar endAdvance) const
229{
230    SkRect rect = {0.0, 0.0, 0.0, 0.0};
231    SkScalar runNotWhiteSpaceWidth = 0.0;
232#ifndef USE_SKIA_TXT
233    SkRect joinRect{0.0, 0.0, 0.0, 0.0};
234    SkRect endRect {0.0, 0.0, 0.0, 0.0};
235    SkRect startRect {0.0, 0.0, 0.0, 0.0};
236#else
237    RSRect joinRect{0.0, 0.0, 0.0, 0.0};
238    RSRect endRect {0.0, 0.0, 0.0, 0.0};
239    RSRect startRect {0.0, 0.0, 0.0, 0.0};
240#endif
241    size_t end = runGlyphIdSpan.size() - endWhiteSpaceNum;
242    for (size_t i = startNotWhiteSpaceIndex; i < end; i++) {
243    // Get the bounds of each glyph
244#ifndef USE_SKIA_TXT
245        SkRect glyphBounds;
246        fVisitorRun->font().getBounds(&runGlyphIdSpan[i], 1, &glyphBounds, nullptr);
247#else
248        RSRect glyphBounds;
249        fVisitorRun->font().GetWidths(&runGlyphIdSpan[i], 1, nullptr, &glyphBounds);
250#endif
251        // Record the first non-blank glyph boundary
252        if (i == startNotWhiteSpaceIndex) {
253            startRect = glyphBounds;
254        }
255        if (i == end - 1) {
256            endRect = glyphBounds;
257        }
258        // Stitching removes glyph boundaries at the beginning and end of lines
259        joinRect.Join(glyphBounds);
260        auto& cluster = fVisitorRun->owner()->cluster(fVisitorGlobalPos + i);
261        // Calculates the width of the glyph with the beginning and end of the line removed
262        runNotWhiteSpaceWidth += cluster.width();
263    }
264#ifndef USE_SKIA_TXT
265    // If the first glyph of run is a blank glyph, you need to add startWhitespaceWidth
266    SkScalar x = fClipRect.fLeft + startRect.x() + startWhiteSpaceWidth;
267    SkScalar y = joinRect.bottom();
268    SkScalar width = runNotWhiteSpaceWidth - (endAdvance - endRect.x() - endRect.width()) - startRect.x();
269    SkScalar height = joinRect.height();
270#else
271    SkScalar x = fClipRect.fLeft + startRect.GetLeft() + startWhiteSpaceWidth;
272    SkScalar y = joinRect.GetBottom();
273    SkScalar width = runNotWhiteSpaceWidth - (endAdvance - endRect.GetLeft() - endRect.GetWidth()) - startRect.GetLeft();
274    SkScalar height = joinRect.GetHeight();
275#endif
276     rect.setXYWH(x, y, width, height);
277     return rect;
278}
279
280#ifndef USE_SKIA_TXT
281SkRect RunBaseImpl::getImageBounds() const
282#else
283RSRect RunBaseImpl::getImageBounds() const
284#endif
285{
286    if (!fVisitorRun) {
287        return {};
288    }
289    SkSpan<const SkGlyphID> glyphIdSpan = fVisitorRun->glyphs();
290    SkSpan<const SkGlyphID> runGlyphIdSpan = glyphIdSpan.subspan(fVisitorPos, fVisitorSize);
291    if (runGlyphIdSpan.size() == 0) {
292        return {};
293    }
294    SkScalar endAdvance = 0.0;
295    SkScalar startWhiteSpaceWidth = 0.0;
296    size_t endWhiteSpaceNum = 0;
297    size_t startNotWhiteSpaceIndex = 0;
298    // Gets the width of the first non-blank glyph at the end
299    for (size_t i = runGlyphIdSpan.size() - 1; i >= 0; --i) {
300        auto& cluster = fVisitorRun->owner()->cluster(fVisitorGlobalPos + i);
301        if (!cluster.isWhitespaceBreak()) {
302            endAdvance = cluster.width();
303            break;
304        }
305        ++endWhiteSpaceNum;
306        if (i == 0) {
307            break;
308        }
309    }
310    // Gets the width of the first non-blank glyph at the end
311    for (size_t i = 0; i < runGlyphIdSpan.size(); ++i) {
312        auto& cluster = fVisitorRun->owner()->cluster(fVisitorGlobalPos + i);
313        if (!cluster.isWhitespaceBreak()) {
314            break;
315        }
316        startWhiteSpaceWidth += cluster.width();
317        ++startNotWhiteSpaceIndex;
318    }
319    SkRect rect = getAllGlyphRectInfo(runGlyphIdSpan, startNotWhiteSpaceIndex, startWhiteSpaceWidth, endWhiteSpaceNum, endAdvance);
320    return {rect.fLeft, rect.fTop, rect.fRight, rect.fBottom};
321}
322
323float RunBaseImpl::getTypographicBounds(float* ascent, float* descent, float* leading) const
324{
325    if (ascent == nullptr || descent == nullptr || leading == nullptr) {
326        return 0.0;
327    }
328    if (!fVisitorRun) {
329        *ascent = 0.0;
330        *descent = 0.0;
331        *leading = 0.0;
332        return 0.0;
333    }
334    *ascent = std::abs(fVisitorRun->ascent());
335    *descent = fVisitorRun->descent();
336    *leading = fVisitorRun->leading();
337    return fClipRect.width() + calculateTrailSpacesWidth();
338}
339
340float RunBaseImpl::calculateTrailSpacesWidth() const
341{
342    // Calculates the width of the whitespace character at the end of the line
343    if (!fVisitorRun || fTrailSpaces == 0) {
344        return 0.0;
345    }
346    SkScalar spaceWidth = 0;
347    for (size_t i = 0; i < fTrailSpaces; i++) {
348        auto& cluster = fVisitorRun->owner()->cluster(fVisitorGlobalPos + fVisitorSize + i);
349        // doesn't calculate the width of a hard line wrap at the end of a line
350        if (cluster.isHardBreak()) {
351            break;
352        }
353        spaceWidth += cluster.width();
354    }
355
356    return spaceWidth;
357}
358
359uint64_t RunBaseImpl::calculateActualLength(int64_t start, int64_t length) const
360{
361    // Calculate the actual size of the run,
362    // start and length equal to 0 means that the data is obtained from start to end, so no filtering is required
363    if (start >= fVisitorSize || start < 0 || length < 0) {
364        return 0;
365    }
366    uint64_t actualLength = fVisitorSize - start;
367    actualLength = actualLength > length ? length: actualLength;
368    // If length is equal to 0, the end of the line is obtained
369    if (start >= 0 && length == 0) {
370        return fVisitorSize - start;
371    }
372
373    return actualLength;
374}
375#endif
376}  // namespace textlayout
377}  // namespace skia