blob: 0111aa3ca23e61135ee79a1022262256264f155d [file] [log] [blame]
Florin Malitaca449d52019-05-08 10:04:09 -04001/*
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"
Avinash Parchurife125a12019-07-30 17:52:35 -070012#include "modules/skottie/src/SkottieValue.h"
Florin Malitaca449d52019-05-08 10:04:09 -040013
14namespace skottie {
15
Florin Malitaca449d52019-05-08 10:04:09 -040016template <>
17bool ValueTraits<TextValue>::FromJSON(const skjson::Value& jv,
18 const internal::AnimationBuilder* abuilder,
19 TextValue* v) {
20 const skjson::ObjectValue* jtxt = jv;
21 if (!jtxt) {
22 return false;
23 }
24
Florin Malita3fdc05a2019-05-24 12:07:15 -040025 const skjson::StringValue* font_name = (*jtxt)["f"];
26 const skjson::StringValue* text = (*jtxt)["t"];
27 const skjson::NumberValue* text_size = (*jtxt)["s"];
28 const skjson::NumberValue* line_height = (*jtxt)["lh"];
Florin Malitad75fed42019-06-27 09:23:03 -040029 if (!font_name || !text || !text_size || !line_height) {
Florin Malitaca449d52019-05-08 10:04:09 -040030 return false;
31 }
Florin Malitad75fed42019-06-27 09:23:03 -040032
33 const auto* font = abuilder->findFont(SkString(font_name->begin(), font_name->size()));
34 if (!font) {
35 abuilder->log(Logger::Level::kError, nullptr, "Unknown font: \"%s\".", font_name->begin());
36 return false;
37 }
38
Florin Malitaca449d52019-05-08 10:04:09 -040039 v->fText.set(text->begin(), text->size());
Florin Malita3fdc05a2019-05-24 12:07:15 -040040 v->fTextSize = **text_size;
41 v->fLineHeight = **line_height;
Florin Malitad75fed42019-06-27 09:23:03 -040042 v->fTypeface = font->fTypeface;
43 v->fAscent = font->fAscentPct * -0.01f * v->fTextSize; // negative ascent per SkFontMetrics
Florin Malitaca449d52019-05-08 10:04:09 -040044
45 static constexpr SkTextUtils::Align gAlignMap[] = {
46 SkTextUtils::kLeft_Align, // 'j': 0
47 SkTextUtils::kRight_Align, // 'j': 1
48 SkTextUtils::kCenter_Align // 'j': 2
49 };
Brian Osman788b9162020-02-07 10:36:46 -050050 v->fHAlign = gAlignMap[std::min<size_t>(ParseDefault<size_t>((*jtxt)["j"], 0),
Florin Malitaca449d52019-05-08 10:04:09 -040051 SK_ARRAY_COUNT(gAlignMap))];
52
53 // Optional text box size.
54 if (const skjson::ArrayValue* jsz = (*jtxt)["sz"]) {
55 if (jsz->size() == 2) {
56 v->fBox.setWH(ParseDefault<SkScalar>((*jsz)[0], 0),
57 ParseDefault<SkScalar>((*jsz)[1], 0));
58 }
59 }
60
61 // Optional text box position.
62 if (const skjson::ArrayValue* jps = (*jtxt)["ps"]) {
63 if (jps->size() == 2) {
64 v->fBox.offset(ParseDefault<SkScalar>((*jps)[0], 0),
65 ParseDefault<SkScalar>((*jps)[1], 0));
66 }
67 }
68
Florin Malitaad911002020-01-28 13:21:47 -050069 // Skia resizing extension "sk_rs":
70 static constexpr Shaper::ResizePolicy gResizeMap[] = {
71 Shaper::ResizePolicy::kNone, // 'sk_rs': 0
72 Shaper::ResizePolicy::kScaleToFit, // 'sk_rs': 1
73 Shaper::ResizePolicy::kDownscaleToFit, // 'sk_rs': 2
74 };
Brian Osman788b9162020-02-07 10:36:46 -050075 v->fResize = gResizeMap[std::min<size_t>(ParseDefault<size_t>((*jtxt)["sk_rs"], 0),
Florin Malitaad911002020-01-28 13:21:47 -050076 SK_ARRAY_COUNT(gResizeMap))];
77
Florin Malitaca449d52019-05-08 10:04:09 -040078 // In point mode, the text is baseline-aligned.
79 v->fVAlign = v->fBox.isEmpty() ? Shaper::VAlign::kTopBaseline
80 : Shaper::VAlign::kTop;
81
82 // Skia vertical alignment extension "sk_vj":
Florin Malita4f6eb152019-05-09 11:18:16 -040083 static constexpr Shaper::VAlign gVAlignMap[] = {
Florin Malitaad911002020-01-28 13:21:47 -050084 Shaper::VAlign::kVisualTop, // 'sk_vj': 0
85 Shaper::VAlign::kVisualCenter, // 'sk_vj': 1
86 Shaper::VAlign::kVisualBottom, // 'sk_vj': 2
Florin Malitaca449d52019-05-08 10:04:09 -040087 };
Florin Malita4f6eb152019-05-09 11:18:16 -040088 size_t sk_vj;
Florin Malitab9fb29f2019-08-19 13:27:16 -040089 if (Parse((*jtxt)["sk_vj"], &sk_vj)) {
90 if (sk_vj < SK_ARRAY_COUNT(gVAlignMap)) {
91 v->fVAlign = gVAlignMap[sk_vj];
92 } else {
Florin Malitaad911002020-01-28 13:21:47 -050093 // Legacy sk_vj values.
94 // TODO: remove after clients update.
95 switch (sk_vj) {
96 case 3:
97 // 'sk_vj': 3 -> kVisualCenter/kScaleToFit
98 v->fVAlign = Shaper::VAlign::kVisualCenter;
99 v->fResize = Shaper::ResizePolicy::kScaleToFit;
100 break;
101 case 4:
102 // 'sk_vj': 4 -> kVisualCenter/kDownscaleToFit
103 v->fVAlign = Shaper::VAlign::kVisualCenter;
104 v->fResize = Shaper::ResizePolicy::kDownscaleToFit;
105 break;
106 default:
107 abuilder->log(Logger::Level::kWarning, nullptr,
108 "Ignoring unknown 'sk_vj' value: %zu", sk_vj);
109 break;
110 }
Florin Malitab9fb29f2019-08-19 13:27:16 -0400111 }
Florin Malitaca449d52019-05-08 10:04:09 -0400112 }
113
Florin Malitaa3e82662020-02-19 17:59:00 -0500114 if (v->fResize != Shaper::ResizePolicy::kNone && v->fBox.isEmpty()) {
115 abuilder->log(Logger::Level::kWarning, jtxt, "Auto-scaled text requires a paragraph box.");
116 v->fResize = Shaper::ResizePolicy::kNone;
117 }
118
Florin Malitaca449d52019-05-08 10:04:09 -0400119 const auto& parse_color = [] (const skjson::ArrayValue* jcolor,
120 const internal::AnimationBuilder* abuilder,
121 SkColor* c) {
122 if (!jcolor) {
123 return false;
124 }
125
126 VectorValue color_vec;
127 if (!ValueTraits<VectorValue>::FromJSON(*jcolor, abuilder, &color_vec)) {
128 return false;
129 }
130
131 *c = ValueTraits<VectorValue>::As<SkColor>(color_vec);
132 return true;
133 };
134
135 v->fHasFill = parse_color((*jtxt)["fc"], abuilder, &v->fFillColor);
136 v->fHasStroke = parse_color((*jtxt)["sc"], abuilder, &v->fStrokeColor);
137
138 if (v->fHasStroke) {
139 v->fStrokeWidth = ParseDefault((*jtxt)["s"], 0.0f);
140 }
141
142 return true;
143}
144
145template <>
146bool ValueTraits<TextValue>::CanLerp(const TextValue&, const TextValue&) {
147 // Text values are never interpolated, but we pretend that they could be.
148 return true;
149}
150
151template <>
152void ValueTraits<TextValue>::Lerp(const TextValue& v0, const TextValue&, float, TextValue* result) {
153 // Text value keyframes are treated as selectors, not as interpolated values.
154 *result = v0;
155}
156
157} // namespace skottie