1 | /* |
2 | * Copyright 2018 Google Inc. |
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 "modules/sksg/include/SkSGText.h" |
9 | |
10 | #include "include/core/SkCanvas.h" |
11 | #include "include/core/SkPaint.h" |
12 | #include "include/core/SkPath.h" |
13 | #include "include/core/SkTypeface.h" |
14 | #include "include/private/SkTArray.h" |
15 | |
16 | namespace sksg { |
17 | |
18 | sk_sp<Text> Text::Make(sk_sp<SkTypeface> tf, const SkString& text) { |
19 | return sk_sp<Text>(new Text(std::move(tf), text)); |
20 | } |
21 | |
22 | Text::Text(sk_sp<SkTypeface> tf, const SkString& text) |
23 | : fTypeface(std::move(tf)) |
24 | , fText(text) {} |
25 | |
26 | Text::~Text() = default; |
27 | |
28 | SkPoint Text::alignedPosition(SkScalar advance) const { |
29 | auto aligned = fPosition; |
30 | |
31 | switch (fAlign) { |
32 | case SkTextUtils::kLeft_Align: |
33 | break; |
34 | case SkTextUtils::kCenter_Align: |
35 | aligned.offset(-advance / 2, 0); |
36 | break; |
37 | case SkTextUtils::kRight_Align: |
38 | aligned.offset(-advance, 0); |
39 | break; |
40 | } |
41 | |
42 | return aligned; |
43 | } |
44 | |
45 | SkRect Text::onRevalidate(InvalidationController*, const SkMatrix&) { |
46 | // TODO: we could potentially track invals which don't require rebuilding the blob. |
47 | |
48 | SkFont font; |
49 | font.setTypeface(fTypeface); |
50 | font.setSize(fSize); |
51 | font.setScaleX(fScaleX); |
52 | font.setSkewX(fSkewX); |
53 | font.setEdging(fEdging); |
54 | font.setHinting(fHinting); |
55 | |
56 | // N.B.: fAlign is applied externally (in alignedPosition()), because |
57 | // 1) SkTextBlob has some trouble computing accurate bounds with alignment. |
58 | // 2) SkPaint::Align is slated for deprecation. |
59 | |
60 | fBlob = SkTextBlob::MakeFromText(fText.c_str(), fText.size(), font, SkTextEncoding::kUTF8); |
61 | if (!fBlob) { |
62 | return SkRect::MakeEmpty(); |
63 | } |
64 | |
65 | const auto& bounds = fBlob->bounds(); |
66 | const auto aligned_pos = this->alignedPosition(bounds.width()); |
67 | |
68 | return bounds.makeOffset(aligned_pos.x(), aligned_pos.y()); |
69 | } |
70 | |
71 | void Text::onDraw(SkCanvas* canvas, const SkPaint& paint) const { |
72 | const auto aligned_pos = this->alignedPosition(this->bounds().width()); |
73 | canvas->drawTextBlob(fBlob, aligned_pos.x(), aligned_pos.y(), paint); |
74 | } |
75 | |
76 | bool Text::onContains(const SkPoint& p) const { |
77 | return this->asPath().contains(p.x(), p.y()); |
78 | } |
79 | |
80 | SkPath Text::onAsPath() const { |
81 | // TODO |
82 | return SkPath(); |
83 | } |
84 | |
85 | void Text::onClip(SkCanvas* canvas, bool antiAlias) const { |
86 | canvas->clipPath(this->asPath(), antiAlias); |
87 | } |
88 | |
89 | sk_sp<TextBlob> TextBlob::Make(sk_sp<SkTextBlob> blob) { |
90 | return sk_sp<TextBlob>(new TextBlob(std::move(blob))); |
91 | } |
92 | |
93 | TextBlob::TextBlob(sk_sp<SkTextBlob> blob) |
94 | : fBlob(std::move(blob)) {} |
95 | |
96 | TextBlob::~TextBlob() = default; |
97 | |
98 | SkRect TextBlob::onRevalidate(InvalidationController*, const SkMatrix&) { |
99 | return fBlob ? fBlob->bounds().makeOffset(fPosition.x(), fPosition.y()) |
100 | : SkRect::MakeEmpty(); |
101 | } |
102 | |
103 | void TextBlob::onDraw(SkCanvas* canvas, const SkPaint& paint) const { |
104 | canvas->drawTextBlob(fBlob, fPosition.x(), fPosition.y(), paint); |
105 | } |
106 | |
107 | bool TextBlob::onContains(const SkPoint& p) const { |
108 | return this->asPath().contains(p.x(), p.y()); |
109 | } |
110 | |
111 | SkPath TextBlob::onAsPath() const { |
112 | // TODO |
113 | return SkPath(); |
114 | } |
115 | |
116 | void TextBlob::onClip(SkCanvas* canvas, bool antiAlias) const { |
117 | canvas->clipPath(this->asPath(), antiAlias); |
118 | } |
119 | |
120 | } // namespace sksg |
121 | |