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