1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
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 #include "hm_symbol_node_build.h"
16
17 #include "common/rs_common_def.h"
18 #include "include/pathops/SkPathOps.h"
19 #include "utils/text_log.h"
20
21 namespace OHOS {
22 namespace Rosen {
23 namespace SPText {
24
25 /**
26 * @brief Obtain the group id to layer
27 * @param groupIds (output paramer) the index of groupIds if layer index, the groupIds[index] is the group index
28 * @param renderGroup the renderGroup info of symbol
29 * @param index the renderGroup index
30 */
GetLayersGroupId(std::vector<size_t> & groupIds,const RSRenderGroup & renderGroup,size_t index)31 static void GetLayersGroupId(std::vector<size_t>& groupIds, const RSRenderGroup& renderGroup, size_t index)
32 {
33 for (const auto& groupInfo : renderGroup.groupInfos) {
34 for (size_t j : groupInfo.layerIndexes) {
35 if (j < groupIds.size()) {
36 groupIds[j] = index;
37 }
38 }
39 for (size_t j : groupInfo.maskIndexes) {
40 if (j < groupIds.size()) {
41 groupIds[j] = index;
42 }
43 }
44 }
45 }
46
47 /**
48 * @brief Merge the mask paths.
49 * @param maskIndexes the indexes of maskLayer of group informations
50 * @param pathLayers the path data of layers on symbol, the value of maskIndexes < pathLayers.size
51 * @param pathsColorIndex the start pathsColor position for merging mask pathh
52 * @param pathsColor (output paramer) the result of the merge
53 */
MergeMaskPath(const std::vector<size_t> & maskIndexes,std::vector<RSPath> & pathLayers,size_t pathsColorIndex,std::vector<TextEngine::NodeLayerInfo> & pathsColor)54 static void MergeMaskPath(const std::vector<size_t>& maskIndexes, std::vector<RSPath>& pathLayers,
55 size_t pathsColorIndex, std::vector<TextEngine::NodeLayerInfo>& pathsColor)
56 {
57 RSPath maskPath;
58 for (size_t maskIndex : maskIndexes) {
59 if (maskIndex < pathLayers.size()) {
60 maskPath.AddPath(pathLayers[maskIndex]);
61 }
62 }
63 if (!maskPath.IsValid()) {
64 return;
65 }
66
67 for (size_t j = pathsColorIndex; j < pathsColor.size(); j++) {
68 Drawing::Path outPath;
69 bool isOk = outPath.Op(pathsColor[j].path, maskPath, Drawing::PathOp::DIFFERENCE);
70 pathsColor[j].path = isOk ? outPath : pathsColor[j].path;
71 }
72 }
73
74 /**
75 * @brief Merge the paths of Group by layer color.
76 * @param groupInfos a group informations of dynamic drawing
77 * @param pathLayers the path data of layers on symbol
78 * @param groupIndexes the indexes list of groups on colorGroups, the groupIndexes.size == pathLayers.size, \n
79 * groupIndexes.size == pathLayers.size, groupIndexes[groupIndexes.size - 1] < colorGroups.size
80 * @param pathsColor (output paramer) the result of the merge, where each merged path corresponds to a specific color
81 * @param colorGroups the groups informations of static drawing, include color data
82 */
MergePathByLayerColor(const std::vector<RSGroupInfo> & groupInfos,std::vector<RSPath> & pathLayers,const std::vector<size_t> & groupIndexes,std::vector<TextEngine::NodeLayerInfo> & pathsColor,std::vector<std::shared_ptr<SymbolGradient>> & colorGroups)83 static void MergePathByLayerColor(const std::vector<RSGroupInfo>& groupInfos,
84 std::vector<RSPath>& pathLayers, const std::vector<size_t>& groupIndexes,
85 std::vector<TextEngine::NodeLayerInfo>& pathsColor,
86 std::vector<std::shared_ptr<SymbolGradient>>& colorGroups)
87 {
88 size_t pathsColorIndex = 0;
89 std::shared_ptr<SymbolGradient> color = nullptr;
90 for (const auto& groupInfo : groupInfos) {
91 TextEngine::NodeLayerInfo tempLayer;
92 size_t currentIndex = 0; // the current layerindex, that effective index of tempLayer
93 bool isFirst = true;
94 for (size_t layerIndex: groupInfo.layerIndexes) {
95 if (layerIndex >= pathLayers.size() || layerIndex >= groupIndexes.size()) {
96 continue;
97 }
98 if (isFirst) { // initialize tempLayer
99 auto groupIndex = groupIndexes[layerIndex];
100 tempLayer.color = groupIndex < colorGroups.size() ? colorGroups[groupIndex] : color;
101 tempLayer.path.AddPath(pathLayers[layerIndex]);
102 currentIndex = layerIndex;
103 isFirst = false;
104 continue;
105 }
106 // If the groupIndex of two paths is different, update pathsColor and tempLayer
107 if (groupIndexes[currentIndex] != groupIndexes[layerIndex]) {
108 pathsColor.push_back(tempLayer);
109 tempLayer.path.Reset();
110 auto groupIndex = groupIndexes[layerIndex];
111 tempLayer.color = groupIndex < colorGroups.size() ? colorGroups[groupIndex] : color;
112 currentIndex = layerIndex;
113 }
114 tempLayer.path.AddPath(pathLayers[layerIndex]);
115 }
116 pathsColor.push_back(tempLayer);
117 if (!groupInfo.maskIndexes.empty()) {
118 MergeMaskPath(groupInfo.maskIndexes, pathLayers, pathsColorIndex, pathsColor);
119 }
120 pathsColorIndex = pathsColor.size();
121 }
122 }
123
124 /**
125 * @brief check if the symbol group is a mask layer, that it has only mask indexes
126 * @param multPath (output paramer) combine all paths of mask indexes in groupInfos
127 * @param groupInfos the groupsInfos of symbol
128 * @param pathLayers the all paths of symbol
129 * @return true if the group is a mask layer
130 */
IsMaskLayer(RSPath & multPath,const std::vector<RSGroupInfo> & groupInfos,std::vector<RSPath> & pathLayers)131 static bool IsMaskLayer(RSPath& multPath, const std::vector<RSGroupInfo>& groupInfos, std::vector<RSPath>& pathLayers)
132 {
133 for (const auto& groupInfo : groupInfos) {
134 RSPath pathTemp;
135 if (groupInfo.layerIndexes.size() > 0) {
136 return false;
137 }
138 for (auto k : groupInfo.maskIndexes) {
139 if (k >= pathLayers.size()) {
140 continue;
141 }
142 pathTemp.AddPath(pathLayers[k]);
143 }
144 multPath.AddPath(pathTemp);
145 }
146 return true;
147 }
148
MergeDrawingPath(RSPath & multPath,const RSRenderGroup & group,std::vector<RSPath> & pathLayers)149 void SymbolNodeBuild::MergeDrawingPath(RSPath& multPath, const RSRenderGroup& group, std::vector<RSPath>& pathLayers)
150 {
151 for (const auto& groupInfo : group.groupInfos) {
152 RSPath pathTemp;
153 for (auto i : groupInfo.layerIndexes) {
154 if (i < pathLayers.size()) {
155 pathTemp.AddPath(pathLayers[i]);
156 }
157 }
158 RSPath maskPath;
159 for (auto j : groupInfo.maskIndexes) {
160 if (j < pathLayers.size()) {
161 maskPath.AddPath(pathLayers[j]);
162 }
163 }
164 if (maskPath.IsValid()) {
165 Drawing::Path outPath;
166 auto isOk = outPath.Op(pathTemp, maskPath, Drawing::PathOp::DIFFERENCE);
167 pathTemp = isOk ? outPath : pathTemp;
168 multPath.SetFillStyle(pathTemp.GetFillStyle());
169 }
170 multPath.AddPath(pathTemp);
171 }
172 }
173
SymbolNodeBuild(const RSAnimationSetting & animationSetting,const RSHMSymbolData & symbolData,const RSEffectStrategy & effectStrategy,const std::pair<float,float> & offset)174 SymbolNodeBuild::SymbolNodeBuild(const RSAnimationSetting &animationSetting, const RSHMSymbolData &symbolData,
175 const RSEffectStrategy &effectStrategy,
176 const std::pair<float, float> &offset) : animationSetting_(animationSetting),
177 symbolData_(symbolData), effectStrategy_(effectStrategy),
178 offsetX_(offset.first), offsetY_(offset.second) {}
179
AddWholeAnimation(const RSHMSymbolData & symbolData,const Vector4f & nodeBounds,std::shared_ptr<TextEngine::SymbolAnimationConfig> symbolAnimationConfig)180 void SymbolNodeBuild::AddWholeAnimation(const RSHMSymbolData &symbolData, const Vector4f &nodeBounds,
181 std::shared_ptr<TextEngine::SymbolAnimationConfig> symbolAnimationConfig)
182 {
183 TextEngine::SymbolNode symbolNode;
184 symbolNode.symbolData = symbolData;
185 symbolNode.nodeBoundary = nodeBounds;
186
187 std::vector<RSPath> paths;
188 RSHMSymbol::PathOutlineDecompose(symbolData.path_, paths);
189 std::vector<RSPath> pathLayers;
190 RSHMSymbol::MultilayerPath(symbolData.symbolInfo_.layers, paths, pathLayers);
191 std::vector<RSRenderGroup> groups = symbolData.symbolInfo_.renderGroups;
192 UpdateGradient(groups, pathLayers, symbolData.path_);
193 TEXT_LOGD("Render group size %{public}zu", groups.size());
194 for (size_t i = 0; i < groups.size(); i++) {
195 RSPath multPath;
196 MergeDrawingPath(multPath, groups[i], pathLayers);
197 TextEngine::NodeLayerInfo pathInfo;
198 pathInfo.path = multPath;
199 bool isValid = i < gradients_.size() && gradients_[i] != nullptr;
200 if (isValid) {
201 pathInfo.color = gradients_[i];
202 }
203 symbolNode.pathsInfo.push_back(pathInfo);
204 }
205
206 symbolAnimationConfig->symbolNodes.push_back(symbolNode);
207 symbolAnimationConfig->numNodes = symbolAnimationConfig->symbolNodes.size();
208 }
209
SetSymbolNodeColors(const TextEngine::SymbolNode & symbolNode,TextEngine::SymbolNode & outSymbolNode)210 void SymbolNodeBuild::SetSymbolNodeColors(const TextEngine::SymbolNode& symbolNode,
211 TextEngine::SymbolNode& outSymbolNode)
212 {
213 bool isNoNeed = symbolNode.pathsInfo.empty() || outSymbolNode.pathsInfo.empty() ||
214 outSymbolNode.pathsInfo[0].color; // If color != nullptr is no need to set
215 if (isNoNeed) {
216 return;
217 }
218
219 std::shared_ptr<SymbolGradient> color;
220 if (disableSlashColor_ != nullptr) {
221 disableSlashColor_->Make(outSymbolNode.pathsInfo[0].path.GetBounds());
222 color = disableSlashColor_;
223 } else {
224 color = symbolNode.pathsInfo[0].color;
225 }
226 for (auto& pathInfo: outSymbolNode.pathsInfo) {
227 pathInfo.color = color;
228 }
229 }
230
AddHierarchicalAnimation(const RSHMSymbolData & symbolData,const Vector4f & nodeBounds,const std::vector<RSGroupSetting> & groupSettings,std::shared_ptr<TextEngine::SymbolAnimationConfig> symbolAnimationConfig)231 void SymbolNodeBuild::AddHierarchicalAnimation(const RSHMSymbolData &symbolData, const Vector4f &nodeBounds,
232 const std::vector<RSGroupSetting> &groupSettings,
233 std::shared_ptr<TextEngine::SymbolAnimationConfig> symbolAnimationConfig)
234 {
235 if (symbolAnimationConfig == nullptr) {
236 symbolAnimationConfig = std::make_shared<TextEngine::SymbolAnimationConfig>();
237 }
238 std::vector<RSPath> paths;
239 RSHMSymbol::PathOutlineDecompose(symbolData.path_, paths);
240 std::vector<RSPath> pathLayers;
241 RSHMSymbol::MultilayerPath(symbolData.symbolInfo_.layers, paths, pathLayers);
242 UpdateGradient(symbolData.symbolInfo_.renderGroups, pathLayers, symbolData.path_);
243
244 // Obtain the group id of layer
245 std::vector<size_t> groupIds(pathLayers.size(), pathLayers.size());
246 for (size_t idx = 0; idx < symbolData.symbolInfo_.renderGroups.size(); idx++) {
247 GetLayersGroupId(groupIds, symbolData.symbolInfo_.renderGroups[idx], idx);
248 }
249
250 // Splitting animation nodes information
251 for (const auto& groupSetting: groupSettings) {
252 TextEngine::SymbolNode symbolNode;
253 TextEngine::NodeLayerInfo maskPath;
254 bool isMask = IsMaskLayer(maskPath.path, groupSetting.groupInfos, pathLayers);
255 if (!isMask) {
256 MergePathByLayerColor(groupSetting.groupInfos, pathLayers, groupIds, symbolNode.pathsInfo,
257 gradients_);
258 } else {
259 symbolNode.pathsInfo.push_back(maskPath);
260 }
261 symbolNode.nodeBoundary = nodeBounds;
262 symbolNode.symbolData = symbolData;
263 symbolNode.animationIndex = groupSetting.animationIndex;
264 symbolNode.isMask = isMask;
265 symbolAnimationConfig->symbolNodes.push_back(symbolNode);
266 }
267 bool isDisableType = (effectStrategy_ == RSEffectStrategy::DISABLE) && (
268 symbolAnimationConfig->symbolNodes.size() > 2); // 2: disableType symbol nodes size must be greater than 2
269 if (isDisableType) {
270 uint32_t last = symbolAnimationConfig->symbolNodes.size() - 1; // the last
271 uint32_t last3 = symbolAnimationConfig->symbolNodes.size() - 3; // 3: the third from the bottom
272 SetSymbolNodeColors(symbolAnimationConfig->symbolNodes[last3], symbolAnimationConfig->symbolNodes[last]);
273 }
274 symbolAnimationConfig->numNodes = symbolAnimationConfig->symbolNodes.size();
275 }
276
CreateGradient(const std::shared_ptr<SymbolGradient> & gradient)277 std::shared_ptr<SymbolGradient> SymbolNodeBuild::CreateGradient(
278 const std::shared_ptr<SymbolGradient>& gradient)
279 {
280 if (gradient == nullptr) {
281 return nullptr;
282 }
283
284 std::shared_ptr<SymbolGradient> outGradient = nullptr;
285 if (gradient->GetGradientType() == GradientType::LINE_GRADIENT) {
286 auto lineGradient = std::static_pointer_cast<SymbolLineGradient>(gradient);
287 outGradient = std::make_shared<SymbolLineGradient>(lineGradient->GetAngle());
288 } else if (gradient->GetGradientType() == GradientType::RADIAL_GRADIENT) {
289 auto radiaGradient = std::static_pointer_cast<SymbolRadialGradient>(gradient);
290 auto newRadiaGradient = std::make_shared<SymbolRadialGradient>(radiaGradient->GetCenterPoint(),
291 radiaGradient->GetRadiusRatio());
292 if (!radiaGradient->IsRadiusRatio()) {
293 newRadiaGradient->SetRadius(radiaGradient->GetRadius());
294 }
295 outGradient = newRadiaGradient;
296 } else {
297 outGradient = std::make_shared<SymbolGradient>();
298 }
299
300 outGradient->SetTileMode(gradient->GetTileMode());
301 outGradient->SetColors(gradient->GetColors());
302 outGradient->SetPositions(gradient->GetPositions());
303
304 return outGradient;
305 }
306
UpdateGradient(const std::vector<RSRenderGroup> & groups,std::vector<RSPath> & pathLayers,const RSPath & path)307 void SymbolNodeBuild::UpdateGradient(const std::vector<RSRenderGroup>& groups,
308 std::vector<RSPath>& pathLayers, const RSPath& path)
309 {
310 if (gradients_.empty()) {
311 return;
312 }
313
314 std::vector<std::shared_ptr<SymbolGradient>> gradients;
315 if (renderMode_ == RSSymbolRenderingStrategy::SINGLE) {
316 if (gradients_[0] != nullptr) {
317 gradients_[0]->Make(path.GetBounds());
318 }
319 for (size_t i = 0; i < groups.size(); i++) {
320 gradients.push_back(gradients_[0]);
321 }
322 gradients_ = gradients;
323 return;
324 }
325
326 for (size_t i = 0; i < groups.size() && i < gradients_.size(); i++) {
327 RSPath multPath;
328 MergeDrawingPath(multPath, groups[i], pathLayers);
329 auto gradient = gradients_[i];
330 if (gradient != nullptr) {
331 gradient->Make(multPath.GetBounds());
332 gradients.push_back(gradient);
333 }
334 }
335 gradients_ = gradients;
336 }
337
ClearAnimation()338 void SymbolNodeBuild::ClearAnimation()
339 {
340 if (animationFunc_ == nullptr) {
341 return;
342 }
343 auto symbolAnimationConfig = std::make_shared<TextEngine::SymbolAnimationConfig>();
344 if (symbolAnimationConfig == nullptr) {
345 return;
346 }
347 symbolAnimationConfig->effectStrategy = Drawing::DrawingEffectStrategy::NONE;
348 symbolAnimationConfig->symbolSpanId = symblSpanId_;
349 animationFunc_(symbolAnimationConfig);
350 }
351
DecomposeSymbolAndDraw()352 bool SymbolNodeBuild::DecomposeSymbolAndDraw()
353 {
354 if (symbolData_.symbolInfo_.renderGroups.empty()) {
355 TEXT_LOGD("Render group is empty");
356 return false;
357 }
358 if (animationFunc_ == nullptr) {
359 TEXT_LOGD("Null animation func");
360 return false;
361 }
362 auto symbolAnimationConfig = std::make_shared<TextEngine::SymbolAnimationConfig>();
363 if (symbolAnimationConfig == nullptr) {
364 return false;
365 }
366 auto rect = symbolData_.path_.GetBounds();
367 float nodeWidth = rect.GetWidth();
368 float nodeHeight = rect.GetHeight();
369 Vector4f nodeBounds = Vector4f(offsetX_, offsetY_, nodeWidth, nodeHeight);
370
371 if (effectStrategy_ == RSEffectStrategy::VARIABLE_COLOR || animationMode_ == 0) {
372 AddHierarchicalAnimation(symbolData_, nodeBounds, animationSetting_.groupSettings, symbolAnimationConfig);
373 } else {
374 AddWholeAnimation(symbolData_, nodeBounds, symbolAnimationConfig);
375 }
376
377 symbolAnimationConfig->effectStrategy = effectStrategy_;
378 symbolAnimationConfig->repeatCount = repeatCount_;
379 symbolAnimationConfig->animationMode = animationMode_;
380 symbolAnimationConfig->animationStart = animationStart_;
381 symbolAnimationConfig->symbolSpanId = symblSpanId_;
382 symbolAnimationConfig->commonSubType = commonSubType_;
383 symbolAnimationConfig->currentAnimationHasPlayed = currentAnimationHasPlayed_;
384 symbolAnimationConfig->slope = slope_;
385 symbolAnimationConfig->symbolShadow = symbolShadow_;
386 return animationFunc_(symbolAnimationConfig);
387 }
388 }
389 }
390 }