1 /*
2 * Copyright (c) 2022-2023 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
16 #ifndef USE_ROSEN_DRAWING
17 #include "include/core/SkPath.h"
18 #include "include/core/SkPaint.h"
19 #endif
20
21 #include "core/components/box/rosen_mask_painter.h"
22 #include "core/components/common/painter/rosen_decoration_painter.h"
23 #include "core/components/declaration/common/declaration.h"
24
25 namespace OHOS::Ace {
26 namespace {
27 constexpr int32_t SIZE_PERCENT = 100;
28 } // namespace
29
LoadMask(const WeakPtr<PipelineContext> & context,const RefPtr<RenderNode> & parent)30 void RosenMaskPainter::LoadMask(const WeakPtr<PipelineContext>& context, const RefPtr<RenderNode>& parent)
31 {
32 if (!IsValid()) {
33 return;
34 }
35
36 if (!parent) {
37 return;
38 }
39 parent_ = parent;
40
41 if (loadStatus_ == LoadStatus::LOADSUCCESS) {
42 loadStatus_ = LoadStatus::UPDATING;
43 }
44
45 if (IsSvgImage()) {
46 LoadSVGImage(context);
47 } else if (IsColorGradient()) {
48 LoadGradient(context);
49 } else if (IsPath()) {
50 LoadPath(context);
51 }
52 }
53
LoadSVGImage(const WeakPtr<PipelineContext> & context)54 void RosenMaskPainter::LoadSVGImage(const WeakPtr<PipelineContext>& context)
55 {
56 if (file_.empty()) {
57 LOGW("mask image is empty.");
58 loadStatus_ = LoadStatus::UNLOADED;
59 return;
60 }
61
62 if (loadStatus_ == LoadStatus::UPDATING && file_ == lastFile_) {
63 // File don't need reload when it is same.
64 loadStatus_ = LoadStatus::LOADSUCCESS;
65 return;
66 }
67
68 auto pipelineContext = context.Upgrade();
69 if (!pipelineContext) {
70 LOGE("pipeline context or render node is null!");
71 loadStatus_ = LoadStatus::UNLOADED;
72 return;
73 }
74 auto successCallback = [rsPainter = Claim(this)](const sk_sp<SkSVGDOM>& svgDom) {
75 if (svgDom) {
76 rsPainter->skiaDom_ = svgDom;
77 rsPainter->loadStatus_ = LoadStatus::LOADSUCCESS;
78 if (rsPainter->parent_) {
79 rsPainter->parent_->MarkNeedLayout(true);
80 }
81 }
82 };
83 auto failedCallback = [rsPainter = Claim(this)]() {
84 rsPainter->loadStatus_ = LoadStatus::LOADFAIL;
85 LOGE("Create imageProvider fail! mask image is %{private}s", rsPainter->maskImage_.c_str());
86 if (rsPainter->parent_) {
87 rsPainter->parent_->MarkNeedLayout();
88 }
89 };
90
91 auto onPostBackgroundTaskCallback = [weakPainter = AceType::WeakClaim(this)] (CancelableTask task) {
92 auto rsPainter = weakPainter.Upgrade();
93 if (rsPainter) {
94 rsPainter->SetFetchImageObjBackgroundTask(task);
95 }
96 };
97
98 ImageProvider::GetSVGImageDOMAsyncFromSrc(file_, successCallback, failedCallback, context, 0,
99 onPostBackgroundTaskCallback);
100 }
101
LoadGradient(const WeakPtr<PipelineContext> & context)102 void RosenMaskPainter::LoadGradient(const WeakPtr<PipelineContext>& context)
103 {
104 auto pipelineContext = context.Upgrade();
105 if (!pipelineContext) {
106 LOGE("pipeline context or render node is null!");
107 return;
108 }
109 dipScale_ = pipelineContext->GetDipScale();
110
111 Declaration declaration;
112 declaration.Init();
113 Declaration::SetMaskGradient(maskImage_, declaration);
114 auto& backgroundStyle =
115 declaration.MaybeResetStyle<CommonBackgroundStyle>(StyleTag::COMMON_BACKGROUND_STYLE);
116 if (!backgroundStyle.IsValid()) {
117 return;
118 }
119 decoration_ = AceType::MakeRefPtr<Decoration>();
120 if (decoration_) {
121 decoration_->SetGradient(backgroundStyle.gradient);
122 }
123
124 loadStatus_ = LoadStatus::LOADSUCCESS;
125 parent_->MarkNeedRender();
126 }
127
LoadPath(const WeakPtr<PipelineContext> & context)128 void RosenMaskPainter::LoadPath(const WeakPtr<PipelineContext>& context)
129 {
130 auto pipelineContext = context.Upgrade();
131 if (!pipelineContext) {
132 LOGE("pipeline context or render node is null!");
133 loadStatus_ = LoadStatus::UNLOADED;
134 return;
135 }
136
137 loadStatus_ = LoadStatus::LOADSUCCESS;
138 parent_->MarkNeedRender();
139 }
140
SetFetchImageObjBackgroundTask(CancelableTask task)141 void RosenMaskPainter::SetFetchImageObjBackgroundTask(CancelableTask task)
142 {
143 if (fetchSvgImageTask_) {
144 fetchSvgImageTask_.Cancel(false);
145 }
146 fetchSvgImageTask_ = task;
147 }
148
149 #ifndef USE_ROSEN_DRAWING
GetPathPaint(SkPaint & paint)150 bool RosenMaskPainter::GetPathPaint(SkPaint& paint)
151 #else
152 bool RosenMaskPainter::GetPathPaint(RSBrush& brush)
153 #endif
154 {
155 if (maskPath_ == nullptr) {
156 LOGE("maskPath_ is null.");
157 return false;
158 }
159
160 const auto& basicShape = maskPath_->GetBasicShape();
161 if (basicShape == nullptr) {
162 LOGE("BasicShape is null.");
163 return false;
164 }
165
166 #ifndef USE_ROSEN_DRAWING
167 paint.setAntiAlias(true);
168 paint.setStyle(SkPaint::Style::kFill_Style);
169 paint.setColor(basicShape->GetColor().GetValue());
170 #else
171 brush.SetAntiAlias(true);
172 brush.SetColor(basicShape->GetColor().GetValue());
173 #endif
174 return true;
175 }
176
177 #ifndef USE_ROSEN_DRAWING
GetGradientPaint(const Rect & paintRect,SkPaint & paint)178 bool RosenMaskPainter::GetGradientPaint(const Rect& paintRect, SkPaint& paint)
179 {
180 paint.setAntiAlias(true);
181 auto decorationPainter = AceType::MakeRefPtr<RosenDecorationPainter>(
182 decoration_, paintRect, Size(paintRect.Width(), paintRect.Height()), dipScale_);
183 if (decorationPainter && decorationPainter->GetGradientPaint(paint)) {
184 lastMaskImageType_ = maskImageType_;
185 return true;
186 }
187 return false;
188 }
189 #else
GetGradientPaint(const Rect & paintRect,RSBrush & brush)190 bool RosenMaskPainter::GetGradientPaint(const Rect& paintRect, RSBrush& brush)
191 {
192 brush.SetAntiAlias(true);
193 auto decorationPainter = AceType::MakeRefPtr<RosenDecorationPainter>(
194 decoration_, paintRect, Size(paintRect.Width(), paintRect.Height()), dipScale_);
195 if (decorationPainter && decorationPainter->GetGradientPaint(brush)) {
196 lastMaskImageType_ = maskImageType_;
197 return true;
198 }
199 return false;
200 }
201 #endif
202
UpadteSVGImageDom(double & x,double & y)203 bool RosenMaskPainter::UpadteSVGImageDom(double& x, double& y)
204 {
205 if (!skiaDom_) {
206 LOGW("dom is not ready.");
207 return false;
208 }
209
210 x = 0.0f;
211 y = 0.0f;
212 Size boxSize = parent_->GetLayoutSize();
213 Size svgSize = Size(skiaDom_->containerSize().width(), skiaDom_->containerSize().height());
214 if (boxSize.IsInfinite() || !boxSize.IsValid()) {
215 if (svgSize.IsInfinite() || !svgSize.IsValid()) {
216 return false;
217 }
218 // if layout size is invalid, use svg size
219 boxSize = svgSize;
220 } else {
221 // update svg scale and size by image-size
222 UpdateSVGScale(boxSize, svgSize);
223 Size left = boxSize - svgSize;
224 if (left.Width() < 0) {
225 left.SetWidth(0);
226 }
227 if (left.Height() < 0) {
228 left.SetHeight(0);
229 }
230 x = (position_.GetSizeTypeX() == BackgroundImagePositionType::PERCENT) ?
231 (left.Width() * position_.GetSizeValueX() / SIZE_PERCENT) : position_.GetSizeValueX();
232 y = (position_.GetSizeTypeY() == BackgroundImagePositionType::PERCENT) ?
233 (left.Height() * position_.GetSizeValueY() / SIZE_PERCENT) : position_.GetSizeValueY();
234 }
235
236 if (svgSize.Width() <= 0 || svgSize.Height() <= 0) {
237 int32_t width = static_cast<int32_t>(boxSize.Width());
238 int32_t height = static_cast<int32_t>(boxSize.Height());
239 skiaDom_->setContainerSize({ width, height });
240 }
241
242 lastFile_ = file_;
243 lastMaskImageType_ = maskImageType_;
244 return true;
245 }
246
UpdateSVGScale(const Size & boxSize,Size & svgSize)247 void RosenMaskPainter::UpdateSVGScale(const Size& boxSize, Size& svgSize)
248 {
249 scaleX_ = 1.0f;
250 scaleY_ = 1.0f;
251 if (!size_.IsValid()) {
252 return;
253 }
254
255 auto typeX = size_.GetSizeTypeX();
256 if (typeX == BackgroundImageSizeType::AUTO) {
257 return;
258 }
259 auto typeY = size_.GetSizeTypeY();
260 double valueX = size_.GetSizeValueX();
261 double valueY = size_.GetSizeValueY();
262
263 // cover use max scale
264 if (typeX == BackgroundImageSizeType::COVER) {
265 scaleX_ = fmax(boxSize.Width() / svgSize.Width(), boxSize.Height() / svgSize.Height());
266 scaleY_ = scaleX_;
267 svgSize.ApplyScale(scaleX_);
268 return;
269 }
270
271 // contain use min scale
272 if (typeX == BackgroundImageSizeType::CONTAIN) {
273 scaleX_ = fmin(boxSize.Width() / svgSize.Width(), boxSize.Height() / svgSize.Height());
274 scaleY_ = scaleX_;
275 svgSize.ApplyScale(scaleX_);
276 return;
277 }
278
279 // percent and length
280 if (typeX == BackgroundImageSizeType::LENGTH) {
281 scaleX_ = valueX / svgSize.Width();
282 svgSize.SetWidth(valueX);
283 } else if (typeX == BackgroundImageSizeType::PERCENT) {
284 scaleX_ = valueX / SIZE_PERCENT;
285 svgSize.SetWidth(svgSize.Width() * scaleX_);
286 } else {
287 return;
288 }
289 if (typeY == BackgroundImageSizeType::PERCENT) {
290 scaleY_ = valueY / SIZE_PERCENT;
291 svgSize.SetHeight(svgSize.Height() * scaleY_);
292 } else if (typeY == BackgroundImageSizeType::LENGTH) {
293 scaleY_ = valueY / svgSize.Height();
294 svgSize.SetHeight(valueY);
295 } else if (typeY == BackgroundImageSizeType::AUTO) {
296 scaleY_ = scaleX_;
297 svgSize.SetHeight(svgSize.Height() * scaleY_);
298 }
299 }
300
HasReady() const301 bool RosenMaskPainter::HasReady() const
302 {
303 if (!IsValid()) {
304 return false;
305 }
306
307 if (loadStatus_ == LoadStatus::LOADSUCCESS ||
308 (loadStatus_ == LoadStatus::UPDATING &&
309 lastMaskImageType_ > MaskImageType::NONE && lastMaskImageType_ <= MaskImageType::COLOR)) {
310 return true;
311 }
312 return false;
313 }
314
315 #ifndef USE_ROSEN_DRAWING
GetRSMask(const Rect & paintRect,const SkPath & path)316 std::shared_ptr<Rosen::RSMask> RosenMaskPainter::GetRSMask(const Rect& paintRect, const SkPath& path)
317 {
318 std::shared_ptr<Rosen::RSMask> rsMask = nullptr;
319 if ((loadStatus_ == LoadStatus::LOADSUCCESS && IsSvgImage()) ||
320 (loadStatus_ == LoadStatus::UPDATING && IsLastSvgImage())) {
321 double x = 0.0f;
322 double y = 0.0f;
323 if (UpadteSVGImageDom(x, y)) {
324 rsMask = Rosen::RSMask::CreateSVGMask(x, y, scaleX_, scaleY_, skiaDom_);
325 }
326 } else if (IsColorGradient()) {
327 SkPaint paint;
328 if (GetGradientPaint(paintRect, paint)) {
329 rsMask = Rosen::RSMask::CreateGradientMask(paint);
330 }
331 } else if (IsPath()) {
332 SkPaint paint;
333 if (GetPathPaint(paint)) {
334 rsMask = Rosen::RSMask::CreatePathMask(path, paint);
335 }
336 }
337 return rsMask;
338 }
339 #else
GetRSMask(const Rect & paintRect,const RSPath & path)340 std::shared_ptr<Rosen::RSMask> RosenMaskPainter::GetRSMask(const Rect& paintRect, const RSPath& path)
341 {
342 std::shared_ptr<Rosen::RSMask> rsMask = nullptr;
343 if ((loadStatus_ == LoadStatus::LOADSUCCESS && IsSvgImage()) ||
344 (loadStatus_ == LoadStatus::UPDATING && IsLastSvgImage())) {
345 double x = 0.0f;
346 double y = 0.0f;
347 if (UpadteSVGImageDom(x, y)) {
348 rsMask = Rosen::RSMask::CreateSVGMask(x, y, scaleX_, scaleY_, skiaDom_);
349 }
350 } else if (IsColorGradient()) {
351 RSBrush brush;
352 if (GetGradientPaint(paintRect, brush)) {
353 rsMask = Rosen::RSMask::CreateGradientMask(brush);
354 }
355 } else if (IsPath()) {
356 RSBrush brush;
357 if (GetPathPaint(brush)) {
358 rsMask = Rosen::RSMask::CreatePathMask(path, brush);
359 }
360 }
361 return rsMask;
362 }
363 #endif
364 } // namespace OHOS::Ace
365