1 /*
2  * Copyright (c) 2022-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 
16 #include "core/components_ng/svg/parse/svg_graphic.h"
17 
18 #include "include/core/SkScalar.h"
19 #include "include/effects/SkDashPathEffect.h"
20 #include "include/effects/SkGradientShader.h"
21 
22 #include "base/utils/utils.h"
23 #include "core/components/declaration/svg/svg_declaration.h"
24 
25 namespace OHOS::Ace::NG {
OnDraw(RSCanvas & canvas,const Size & layout,const std::optional<Color> & color)26 void SvgGraphic::OnDraw(RSCanvas& canvas, const Size& layout, const std::optional<Color>& color)
27 {
28 #ifndef USE_ROSEN_DRAWING
29     fillPaint_.reset();
30     strokePaint_.reset();
31 #else
32     fillBrush_.Reset();
33     strokePen_.Reset();
34 #endif
35     path_ = AsPath(layout); // asPath override by graphic tag
36     UpdateFillGradient(layout);
37     if (UpdateFillStyle(color)) {
38         OnGraphicFill();
39     }
40     UpdateStrokeGradient(layout);
41     if (UpdateStrokeStyle()) {
42         OnGraphicStroke();
43     }
44     if (!fillState_.GetHref().empty()) {
45         auto svgContext = svgContext_.Upgrade();
46         auto refSvgNode = svgContext->GetSvgNodeById(fillState_.GetHref());
47         CHECK_NULL_VOID(refSvgNode);
48         refSvgNode->Draw(canvas, layout, color);
49     }
50 }
51 
SetLinearGradient(const Size & viewPort,Gradient & gradient)52 void SvgGraphic::SetLinearGradient(const Size& viewPort, Gradient& gradient)
53 {
54     auto bounds = AsBounds(viewPort);
55     auto width = bounds.Width();
56     auto height = bounds.Height();
57 
58     const auto& linearGradient = gradient.GetLinearGradient();
59     auto gradientInfo = LinearGradientInfo();
60 
61     gradientInfo.x1 = linearGradient.x1 ? ConvertDimensionToPx(linearGradient.x1.value(), width) : 0.0;
62     if (linearGradient.x1 && linearGradient.x1.value().Unit() == DimensionUnit::PERCENT) {
63         gradientInfo.x1 += bounds.Left();
64     }
65     gradientInfo.y1 = linearGradient.y1 ? ConvertDimensionToPx(linearGradient.y1.value(), height) : 0.0;
66     if (linearGradient.y1 && linearGradient.y1.value().Unit() == DimensionUnit::PERCENT) {
67         gradientInfo.y1 += bounds.Top();
68     }
69     gradientInfo.x2 = ConvertDimensionToPx((linearGradient.x2 ? linearGradient.x2.value() : 1.0_pct), width);
70     if (linearGradient.x2 && linearGradient.x2.value().Unit() == DimensionUnit::PERCENT) {
71         gradientInfo.x2 += bounds.Left();
72     }
73     gradientInfo.y2 = linearGradient.y2 ? ConvertDimensionToPx(linearGradient.y2.value(), height) : 0.0;
74     if (linearGradient.y2 && linearGradient.y2.value().Unit() == DimensionUnit::PERCENT) {
75         gradientInfo.y2 += bounds.Top();
76     }
77 
78     gradient.SetLinearGradientInfo(gradientInfo);
79 }
80 
SetRadialGradient(const Size & viewPort,Gradient & gradient)81 void SvgGraphic::SetRadialGradient(const Size& viewPort, Gradient& gradient)
82 {
83     auto bounds = AsBounds(viewPort);
84     auto width = bounds.Width();
85     auto height = bounds.Height();
86 
87     const auto& radialGradient = gradient.GetRadialGradient();
88     auto gradientInfo = RadialGradientInfo();
89 
90     gradientInfo.r = ConvertDimensionToPx(radialGradient.radialHorizontalSize ?
91         Dimension(radialGradient.radialHorizontalSize.value().Value(),
92         radialGradient.radialHorizontalSize.value().Unit()) : 0.5_pct, sqrt(width * height));
93     gradientInfo.cx = ConvertDimensionToPx(radialGradient.radialCenterX ?
94         Dimension(radialGradient.radialCenterX.value().Value(),
95         radialGradient.radialCenterX.value().Unit()) : 0.5_pct, width) + bounds.Left();
96     gradientInfo.cy = ConvertDimensionToPx(radialGradient.radialCenterY ?
97         Dimension(radialGradient.radialCenterY.value().Value(),
98         radialGradient.radialCenterY.value().Unit()) : 0.5_pct, height) + bounds.Top();
99     if (radialGradient.fRadialCenterX && radialGradient.fRadialCenterX->IsValid()) {
100         gradientInfo.fx = ConvertDimensionToPx(radialGradient.fRadialCenterX.value(), width) + bounds.Left();
101     } else {
102         gradientInfo.fx = gradientInfo.cx;
103     }
104     if (radialGradient.fRadialCenterY && radialGradient.fRadialCenterY->IsValid()) {
105         gradientInfo.fy = ConvertDimensionToPx(radialGradient.fRadialCenterY.value(), height) + bounds.Top();
106     } else {
107         gradientInfo.fy = gradientInfo.cy;
108     }
109     gradient.SetRadialGradientInfo(gradientInfo);
110 }
111 
UpdateFillGradient(const Size & viewPort)112 void SvgGraphic::UpdateFillGradient(const Size& viewPort)
113 {
114     fillState_ = attributes_.fillState;
115     auto& gradient = fillState_.GetGradient();
116     CHECK_NULL_VOID(gradient);
117     if (gradient->GetType() == GradientType::LINEAR) {
118         SetLinearGradient(viewPort, gradient.value());
119     } else if (gradient->GetType() == GradientType::RADIAL) {
120         SetRadialGradient(viewPort, gradient.value());
121     }
122 }
123 
UpdateStrokeGradient(const Size & viewPort)124 void SvgGraphic::UpdateStrokeGradient(const Size& viewPort)
125 {
126     auto& gradient = attributes_.strokeState.GetGradient();
127     CHECK_NULL_VOID(gradient);
128     if (gradient->GetType() == GradientType::LINEAR) {
129         SetLinearGradient(viewPort, gradient.value());
130     } else if (gradient->GetType() == GradientType::RADIAL) {
131         SetRadialGradient(viewPort, gradient.value());
132     }
133 }
134 
UpdateFillStyle(const std::optional<Color> & color,bool antiAlias)135 bool SvgGraphic::UpdateFillStyle(const std::optional<Color>& color, bool antiAlias)
136 {
137     if (!color && fillState_.GetColor() == Color::TRANSPARENT && !fillState_.GetGradient()) {
138         return false;
139     }
140     double curOpacity = fillState_.GetOpacity().GetValue() * opacity_ * (1.0f / UINT8_MAX);
141 #ifndef USE_ROSEN_DRAWING
142     fillPaint_.setStyle(SkPaint::Style::kFill_Style);
143     fillPaint_.setAntiAlias(antiAlias);
144 #else
145     fillBrush_.SetAntiAlias(antiAlias);
146 #endif
147     if (fillState_.GetGradient()) {
148         return SetGradientStyle(curOpacity);
149     } else {
150         auto fillColor = (color) ? *color : fillState_.GetColor();
151 #ifndef USE_ROSEN_DRAWING
152         fillPaint_.setColor(fillColor.BlendOpacity(curOpacity).GetValue());
153 #else
154         fillBrush_.SetColor(fillColor.BlendOpacity(curOpacity).GetValue());
155 #endif
156     }
157     return true;
158 }
159 
SetGradientStyle(double opacity)160 bool SvgGraphic::SetGradientStyle(double opacity)
161 {
162     auto gradient = fillState_.GetGradient();
163     CHECK_NULL_RETURN(gradient, false);
164     auto gradientColors = gradient->GetColors();
165     if (gradientColors.empty()) {
166         return false;
167     }
168 #ifdef USE_ROSEN_DRAWING
169     std::vector<RSScalar> pos;
170     std::vector<RSColorQuad> colors;
171     for (const auto& gradientColor : gradientColors) {
172         pos.push_back(static_cast<RSScalar>(gradientColor.GetDimension().Value()));
173         colors.push_back(
174             gradientColor.GetColor().BlendOpacity(gradientColor.GetOpacity()).BlendOpacity(opacity).GetValue());
175     }
176     if (gradient->GetType() == GradientType::LINEAR) {
177         auto info = gradient->GetLinearGradientInfo();
178         std::array<RSPoint, 2> pts = { RSPoint(static_cast<RSScalar>(info.x1), static_cast<RSScalar>(info.y1)),
179             RSPoint(static_cast<RSScalar>(info.x2), static_cast<RSScalar>(info.y2)) };
180         fillBrush_.SetShaderEffect(RSRecordingShaderEffect::CreateLinearGradient(
181             pts[0], pts[1], colors, pos, static_cast<RSTileMode>(gradient->GetSpreadMethod())));
182     }
183     if (gradient->GetType() == GradientType::RADIAL) {
184         auto info = gradient->GetRadialGradientInfo();
185         auto center = RSPoint(static_cast<RSScalar>(info.cx), static_cast<RSScalar>(info.cy));
186         auto focal = RSPoint(static_cast<RSScalar>(info.fx), static_cast<RSScalar>(info.fx));
187         if (center == focal) {
188             fillBrush_.SetShaderEffect(RSRecordingShaderEffect::CreateRadialGradient(center,
189                 static_cast<RSScalar>(info.r), colors, pos, static_cast<RSTileMode>(gradient->GetSpreadMethod())));
190         } else {
191             RSMatrix matrix;
192             fillBrush_.SetShaderEffect(RSRecordingShaderEffect::CreateTwoPointConical(focal, 0, center,
193                 static_cast<RSScalar>(info.r), colors, pos, static_cast<RSTileMode>(gradient->GetSpreadMethod()),
194                 &matrix));
195         }
196     }
197 #endif
198     return true;
199 }
200 
SetStrokeGradientStyle(double opacity)201 void SvgGraphic::SetStrokeGradientStyle(double opacity)
202 {
203     const auto& strokeState = attributes_.strokeState;
204     auto gradient = strokeState.GetGradient();
205     CHECK_NULL_VOID(gradient);
206     auto gradientColors = gradient->GetColors();
207     if (gradientColors.empty()) {
208         return;
209     }
210 #ifdef USE_ROSEN_DRAWING
211     std::vector<RSScalar> pos;
212     std::vector<RSColorQuad> colors;
213     for (const auto& gradientColor : gradientColors) {
214         pos.push_back(static_cast<RSScalar>(gradientColor.GetDimension().Value()));
215         colors.push_back(
216             gradientColor.GetColor().BlendOpacity(gradientColor.GetOpacity()).BlendOpacity(opacity).GetValue());
217     }
218     if (gradient->GetType() == GradientType::LINEAR) {
219         auto info = gradient->GetLinearGradientInfo();
220         std::array<RSPoint, 2> pts = { RSPoint(static_cast<RSScalar>(info.x1), static_cast<RSScalar>(info.y1)),
221             RSPoint(static_cast<RSScalar>(info.x2), static_cast<RSScalar>(info.y2)) };
222         strokePen_.SetShaderEffect(RSRecordingShaderEffect::CreateLinearGradient(
223             pts[0], pts[1], colors, pos, static_cast<RSTileMode>(gradient->GetSpreadMethod())));
224     }
225     if (gradient->GetType() == GradientType::RADIAL) {
226         auto info = gradient->GetRadialGradientInfo();
227         auto center = RSPoint(static_cast<RSScalar>(info.cx), static_cast<RSScalar>(info.cy));
228         auto focal = RSPoint(static_cast<RSScalar>(info.fx), static_cast<RSScalar>(info.fx));
229         if (center == focal) {
230             strokePen_.SetShaderEffect(RSRecordingShaderEffect::CreateRadialGradient(center,
231                 static_cast<RSScalar>(info.r), colors, pos, static_cast<RSTileMode>(gradient->GetSpreadMethod())));
232         } else {
233             RSMatrix matrix;
234             strokePen_.SetShaderEffect(RSRecordingShaderEffect::CreateTwoPointConical(focal, 0, center,
235                 static_cast<RSScalar>(info.r), colors, pos, static_cast<RSTileMode>(gradient->GetSpreadMethod()),
236                 &matrix));
237         }
238     }
239 #endif
240 }
241 
UpdateStrokeStyle(bool antiAlias)242 bool SvgGraphic::UpdateStrokeStyle(bool antiAlias)
243 {
244     const auto& strokeState = attributes_.strokeState;
245     auto colorFilter = GetColorFilter();
246     if (!colorFilter.has_value() && strokeState.GetColor() == Color::TRANSPARENT && !strokeState.GetGradient()) {
247         return false;
248     }
249     if (!GreatNotEqual(strokeState.GetLineWidth().Value(), 0.0)) {
250         return false;
251     }
252 #ifdef USE_ROSEN_DRAWING
253     double curOpacity = strokeState.GetOpacity().GetValue() * opacity_ * (1.0f / UINT8_MAX);
254     if (strokeState.GetGradient()) {
255         SetStrokeGradientStyle(curOpacity);
256     } else {
257         strokePen_.SetColor(strokeState.GetColor().BlendOpacity(curOpacity).GetValue());
258     }
259     if (strokeState.GetLineCap() == LineCapStyle::ROUND) {
260         strokePen_.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
261     } else if (strokeState.GetLineCap() == LineCapStyle::SQUARE) {
262         strokePen_.SetCapStyle(RSPen::CapStyle::SQUARE_CAP);
263     } else {
264         strokePen_.SetCapStyle(RSPen::CapStyle::FLAT_CAP);
265     }
266     if (strokeState.GetLineJoin() == LineJoinStyle::ROUND) {
267         strokePen_.SetJoinStyle(RSPen::JoinStyle::ROUND_JOIN);
268     } else if (strokeState.GetLineJoin() == LineJoinStyle::BEVEL) {
269         strokePen_.SetJoinStyle(RSPen::JoinStyle::BEVEL_JOIN);
270     } else {
271         strokePen_.SetJoinStyle(RSPen::JoinStyle::MITER_JOIN);
272     }
273     strokePen_.SetWidth(static_cast<RSScalar>(strokeState.GetLineWidth().Value()));
274     strokePen_.SetMiterLimit(static_cast<RSScalar>(strokeState.GetMiterLimit()));
275     strokePen_.SetAntiAlias(antiAlias);
276 
277     auto filter = strokePen_.GetFilter();
278     UpdateColorFilter(filter);
279     strokePen_.SetFilter(filter);
280 #endif
281     UpdateLineDash();
282     return true;
283 }
284 
UpdateLineDash()285 void SvgGraphic::UpdateLineDash()
286 {
287     const auto& strokeState = attributes_.strokeState;
288     if (!strokeState.GetLineDash().lineDash.empty()) {
289         auto lineDashState = strokeState.GetLineDash().lineDash;
290 #ifndef USE_ROSEN_DRAWING
291         std::vector<SkScalar> intervals(lineDashState.size());
292         for (size_t i = 0; i < lineDashState.size(); ++i) {
293             intervals[i] = SkDoubleToScalar(lineDashState[i]);
294         }
295         SkScalar phase = SkDoubleToScalar(strokeState.GetLineDash().dashOffset);
296         strokePaint_.setPathEffect(SkDashPathEffect::Make(intervals.data(), lineDashState.size(), phase));
297 #else
298         RSScalar intervals[lineDashState.size()];
299         for (size_t i = 0; i < lineDashState.size(); ++i) {
300             intervals[i] = static_cast<RSScalar>(lineDashState[i]);
301         }
302         RSScalar phase = static_cast<RSScalar>(strokeState.GetLineDash().dashOffset);
303         strokePen_.SetPathEffect(RSRecordingPathEffect::CreateDashPathEffect(intervals, lineDashState.size(), phase));
304 #endif
305     }
306 }
307 
UpdateColorFilter(RSFilter & filter)308 void SvgGraphic::UpdateColorFilter(RSFilter& filter)
309 {
310     auto colorFilter = GetColorFilter();
311     if (!colorFilter.has_value()) {
312         return;
313     }
314     if (colorFilter.value().colorFilterMatrix_) {
315         RSColorMatrix colorMatrix;
316         colorMatrix.SetArray(colorFilter.value().colorFilterMatrix_->data());
317         filter.SetColorFilter(RSRecordingColorFilter::CreateMatrixColorFilter(colorMatrix));
318         return;
319     }
320     if (!colorFilter.value().colorFilterDrawing_) {
321         return;
322     }
323     auto colorFilterSptrAddr = static_cast<std::shared_ptr<RSColorFilter>*>(
324         colorFilter.value().colorFilterDrawing_->GetDrawingColorFilterSptrAddr());
325     if (!colorFilterSptrAddr || !(*colorFilterSptrAddr)) {
326         return;
327     }
328     filter.SetColorFilter(*colorFilterSptrAddr);
329 }
330 } // namespace OHOS::Ace::NG
331