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