• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 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/skottie/src/text/TextValue.h"
9 
10 #include "modules/skottie/src/SkottieJson.h"
11 #include "modules/skottie/src/SkottiePriv.h"
12 #include "modules/skottie/src/SkottieValue.h"
13 
14 namespace skottie::internal {
15 
Parse(const skjson::Value & jv,const internal::AnimationBuilder & abuilder,TextValue * v)16 bool Parse(const skjson::Value& jv, const internal::AnimationBuilder& abuilder, TextValue* v) {
17     const skjson::ObjectValue* jtxt = jv;
18     if (!jtxt) {
19         return false;
20     }
21 
22     const skjson::StringValue* font_name   = (*jtxt)["f"];
23     const skjson::StringValue* text        = (*jtxt)["t"];
24     const skjson::NumberValue* text_size   = (*jtxt)["s"];
25     const skjson::NumberValue* line_height = (*jtxt)["lh"];
26     if (!font_name || !text || !text_size || !line_height) {
27         return false;
28     }
29 
30     const auto* font = abuilder.findFont(SkString(font_name->begin(), font_name->size()));
31     if (!font) {
32         abuilder.log(Logger::Level::kError, nullptr, "Unknown font: \"%s\".", font_name->begin());
33         return false;
34     }
35 
36     v->fText.set(text->begin(), text->size());
37     v->fTextSize   = **text_size;
38     v->fLineHeight = **line_height;
39     v->fTypeface   = font->fTypeface;
40     v->fAscent     = font->fAscentPct * -0.01f * v->fTextSize; // negative ascent per SkFontMetrics
41     v->fLineShift  = ParseDefault((*jtxt)["ls"], 0.0f);
42 
43     static constexpr SkTextUtils::Align gAlignMap[] = {
44         SkTextUtils::kLeft_Align,  // 'j': 0
45         SkTextUtils::kRight_Align, // 'j': 1
46         SkTextUtils::kCenter_Align // 'j': 2
47     };
48     v->fHAlign = gAlignMap[std::min<size_t>(ParseDefault<size_t>((*jtxt)["j"], 0),
49                                             SK_ARRAY_COUNT(gAlignMap) - 1)];
50 
51     // Optional text box size.
52     if (const skjson::ArrayValue* jsz = (*jtxt)["sz"]) {
53         if (jsz->size() == 2) {
54             v->fBox.setWH(ParseDefault<SkScalar>((*jsz)[0], 0),
55                           ParseDefault<SkScalar>((*jsz)[1], 0));
56         }
57     }
58 
59     // Optional text box position.
60     if (const skjson::ArrayValue* jps = (*jtxt)["ps"]) {
61         if (jps->size() == 2) {
62             v->fBox.offset(ParseDefault<SkScalar>((*jps)[0], 0),
63                            ParseDefault<SkScalar>((*jps)[1], 0));
64         }
65     }
66 
67     static constexpr Shaper::ResizePolicy gResizeMap[] = {
68         Shaper::ResizePolicy::kNone,           // 'rs': 0
69         Shaper::ResizePolicy::kScaleToFit,     // 'rs': 1
70         Shaper::ResizePolicy::kDownscaleToFit, // 'rs': 2
71     };
72     // TODO: remove "sk_rs" support after migrating clients.
73     v->fResize = gResizeMap[std::min(std::max(ParseDefault<size_t>((*jtxt)[   "rs"], 0),
74                                               ParseDefault<size_t>((*jtxt)["sk_rs"], 0)),
75                                      SK_ARRAY_COUNT(gResizeMap) - 1)];
76 
77     // Optional min/max font size (used when aute-resizing)
78     v->fMinTextSize = ParseDefault<SkScalar>((*jtxt)["mf"], 0.0f);
79     v->fMaxTextSize = ParseDefault<SkScalar>((*jtxt)["xf"], std::numeric_limits<float>::max());
80 
81     // At the moment, BM uses the paragraph box to discriminate point mode vs. paragraph mode.
82     v->fLineBreak = v->fBox.isEmpty()
83             ? Shaper::LinebreakPolicy::kExplicit
84             : Shaper::LinebreakPolicy::kParagraph;
85 
86     // Optional explicit text mode.
87     // N.b.: this is not being exported by BM, only used for testing.
88     auto text_mode = ParseDefault((*jtxt)["m"], -1);
89     if (text_mode >= 0) {
90         // Explicit text mode.
91         v->fLineBreak = (text_mode == 0)
92                 ? Shaper::LinebreakPolicy::kExplicit   // 'm': 0 -> point text
93                 : Shaper::LinebreakPolicy::kParagraph; // 'm': 1 -> paragraph text
94     }
95 
96     // Optional capitalization.
97     static constexpr Shaper::Capitalization gCapMap[] = {
98         Shaper::Capitalization::kNone,      // 'ca': 0
99         Shaper::Capitalization::kUpperCase, // 'ca': 1
100     };
101     v->fCapitalization = gCapMap[std::min<size_t>(ParseDefault<size_t>((*jtxt)["ca"], 0),
102                                                   SK_ARRAY_COUNT(gCapMap) - 1)];
103 
104     // In point mode, the text is baseline-aligned.
105     v->fVAlign = v->fBox.isEmpty() ? Shaper::VAlign::kTopBaseline
106                                    : Shaper::VAlign::kTop;
107 
108     static constexpr Shaper::VAlign gVAlignMap[] = {
109         Shaper::VAlign::kVisualTop,    // 'vj': 0
110         Shaper::VAlign::kVisualCenter, // 'vj': 1
111         Shaper::VAlign::kVisualBottom, // 'vj': 2
112     };
113     size_t vj;
114     if (skottie::Parse((*jtxt)[   "vj"], &vj) ||
115         skottie::Parse((*jtxt)["sk_vj"], &vj)) { // TODO: remove after migrating clients.
116         if (vj < SK_ARRAY_COUNT(gVAlignMap)) {
117             v->fVAlign = gVAlignMap[vj];
118         } else {
119             // Legacy sk_vj values.
120             // TODO: remove after clients update.
121             switch (vj) {
122             case 3:
123                 // 'sk_vj': 3 -> kVisualCenter/kScaleToFit
124                 v->fVAlign = Shaper::VAlign::kVisualCenter;
125                 v->fResize = Shaper::ResizePolicy::kScaleToFit;
126                 break;
127             case 4:
128                 // 'sk_vj': 4 -> kVisualCenter/kDownscaleToFit
129                 v->fVAlign = Shaper::VAlign::kVisualCenter;
130                 v->fResize = Shaper::ResizePolicy::kDownscaleToFit;
131                 break;
132             default:
133                 abuilder.log(Logger::Level::kWarning, nullptr,
134                              "Ignoring unknown 'vj' value: %zu", vj);
135                 break;
136             }
137         }
138     }
139 
140     const auto& parse_color = [] (const skjson::ArrayValue* jcolor,
141                                   SkColor* c) {
142         if (!jcolor) {
143             return false;
144         }
145 
146         VectorValue color_vec;
147         if (!skottie::Parse(*jcolor, &color_vec)) {
148             return false;
149         }
150 
151         *c = color_vec;
152         return true;
153     };
154 
155     v->fHasFill   = parse_color((*jtxt)["fc"], &v->fFillColor);
156     v->fHasStroke = parse_color((*jtxt)["sc"], &v->fStrokeColor);
157 
158     if (v->fHasStroke) {
159         v->fStrokeWidth = ParseDefault((*jtxt)["sw"], 1.0f);
160         v->fPaintOrder  = ParseDefault((*jtxt)["of"], true)
161                 ? TextPaintOrder::kFillStroke
162                 : TextPaintOrder::kStrokeFill;
163     }
164 
165     return true;
166 }
167 
168 }  // namespace skottie::internal
169