1 /*
2  * Copyright (c) 2021 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 "frameworks/bridge/declarative_frontend/jsview/js_shape_abstract.h"
17 
18 #include "base/utils/utils.h"
19 #include "bridge/declarative_frontend/jsview/models/shape_abstract_model_impl.h"
20 #include "core/common/container.h"
21 #include "core/components_ng/pattern/shape/shape_abstract_model.h"
22 #include "core/components_ng/pattern/shape/shape_abstract_model_ng.h"
23 #include "frameworks/bridge/declarative_frontend/view_stack_processor.h"
24 
25 namespace OHOS::Ace {
26 
27 std::unique_ptr<ShapeAbstractModel> ShapeAbstractModel::instance_ = nullptr;
28 std::mutex ShapeAbstractModel::mutex_;
29 
GetInstance()30 ShapeAbstractModel* ShapeAbstractModel::GetInstance()
31 {
32     if (!instance_) {
33         std::lock_guard<std::mutex> lock(mutex_);
34         if (!instance_) {
35 #ifdef NG_BUILD
36             instance_.reset(new NG::ShapeAbstractModelNG());
37 #else
38             if (Container::IsCurrentUseNewPipeline()) {
39                 instance_.reset(new NG::ShapeAbstractModelNG());
40             } else {
41                 instance_.reset(new Framework::ShapeAbstractModelImpl());
42             }
43 #endif
44         }
45     }
46     return instance_.get();
47 }
48 
49 } // namespace OHOS::Ace
50 
51 namespace OHOS::Ace::Framework {
52 namespace {
53 constexpr double DEFAULT_OPACITY = 1.0;
54 constexpr double MIN_OPACITY = 0.0;
55 constexpr double STROKE_MITERLIMIT_DEFAULT = 4.0f;
56 } // namespace
57 
SetStrokeDashArray(const JSCallbackInfo & info)58 void JSShapeAbstract::SetStrokeDashArray(const JSCallbackInfo& info)
59 {
60     std::vector<Dimension> dashArray;
61     if (info.Length() < 1 || !info[0]->IsArray()) {
62         ShapeAbstractModel::GetInstance()->SetStrokeDashArray(dashArray);
63         return;
64     }
65     JSRef<JSArray> array = JSRef<JSArray>::Cast(info[0]);
66     auto length = static_cast<int32_t>(array->Length());
67     for (int32_t i = 0; i < length; i++) {
68         JSRef<JSVal> value = array->GetValueAt(i);
69         CalcDimension dim;
70         bool paramIsValid = false;
71         if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
72             paramIsValid = ParseJsDimensionVp(value, dim);
73         } else {
74             paramIsValid = ParseJsDimensionVpNG(value, dim);
75         }
76         if (paramIsValid) {
77             dashArray.emplace_back(dim);
78         } else {
79             dashArray.clear();
80             break;
81         }
82     }
83     // if odd,add twice
84     if (static_cast<uint32_t>(length) == dashArray.size() && (static_cast<uint32_t>(length) & 1)) {
85         for (int32_t i = 0; i < length; i++) {
86             dashArray.emplace_back(dashArray[i]);
87         }
88     }
89     ShapeAbstractModel::GetInstance()->SetStrokeDashArray(dashArray);
90 }
91 
SetStroke(const JSCallbackInfo & info)92 void JSShapeAbstract::SetStroke(const JSCallbackInfo& info)
93 {
94     if (info.Length() < 1) {
95         return;
96     }
97     Color strokeColor;
98     if (!ParseJsColor(info[0], strokeColor)) {
99         ShapeAbstractModel::GetInstance()->SetStroke(Color::TRANSPARENT);
100         return;
101     }
102     ShapeAbstractModel::GetInstance()->SetStroke(strokeColor);
103 }
104 
SetFill(const JSCallbackInfo & info)105 void JSShapeAbstract::SetFill(const JSCallbackInfo& info)
106 {
107     if (info.Length() < 1) {
108         return;
109     }
110     if (info[0]->IsString() && info[0]->ToString() == "none") {
111         ShapeAbstractModel::GetInstance()->SetFill(Color::TRANSPARENT);
112     } else {
113         Color fillColor = Color::BLACK;
114         static const char shapeComponentName[] = "";
115         static const char attrsShapeAbstractFill[] = "fill";
116         CheckColor(info[0], fillColor, shapeComponentName, attrsShapeAbstractFill);
117         ShapeAbstractModel::GetInstance()->SetFill(fillColor);
118     }
119 }
120 
SetStrokeDashOffset(const JSCallbackInfo & info)121 void JSShapeAbstract::SetStrokeDashOffset(const JSCallbackInfo& info)
122 {
123     if (info.Length() < 1) {
124         return;
125     }
126     CalcDimension offset(0.0f);
127     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
128         if (!ParseJsDimensionVp(info[0], offset)) {
129             return;
130         }
131     } else {
132         if (!ParseJsDimensionVpNG(info[0], offset)) {
133             // set to default value(0.0f)
134             offset.SetValue(0.0f);
135         }
136     }
137     ShapeAbstractModel::GetInstance()->SetStrokeDashOffset(offset);
138 }
139 
SetStrokeLineCap(int lineCap)140 void JSShapeAbstract::SetStrokeLineCap(int lineCap)
141 {
142     ShapeAbstractModel::GetInstance()->SetStrokeLineCap(lineCap);
143 }
144 
SetStrokeLineJoin(int lineJoin)145 void JSShapeAbstract::SetStrokeLineJoin(int lineJoin)
146 {
147     ShapeAbstractModel::GetInstance()->SetStrokeLineJoin(lineJoin);
148 }
149 
SetStrokeMiterLimit(const JSCallbackInfo & info)150 void JSShapeAbstract::SetStrokeMiterLimit(const JSCallbackInfo& info)
151 {
152     if (info.Length() < 1) {
153         return;
154     }
155     double miterLimit = STROKE_MITERLIMIT_DEFAULT;
156     ParseJsDouble(info[0], miterLimit);
157     ShapeAbstractModel::GetInstance()->SetStrokeMiterLimit(miterLimit);
158 }
159 
SetStrokeOpacity(const JSCallbackInfo & info)160 void JSShapeAbstract::SetStrokeOpacity(const JSCallbackInfo& info)
161 {
162     if (info.Length() < 1) {
163         return;
164     }
165     double strokeOpacity = DEFAULT_OPACITY;
166     ParseJsDouble(info[0], strokeOpacity);
167     if (GreatOrEqual(strokeOpacity, 1.0)) {
168         strokeOpacity = DEFAULT_OPACITY;
169     }
170     if (LessOrEqual(strokeOpacity, 0.0)) {
171         strokeOpacity = MIN_OPACITY;
172     }
173     ShapeAbstractModel::GetInstance()->SetStrokeOpacity(strokeOpacity);
174 }
175 
176 // https://svgwg.org/svg2-draft/painting.html#FillOpacity
SetFillOpacity(const JSCallbackInfo & info)177 void JSShapeAbstract::SetFillOpacity(const JSCallbackInfo& info)
178 {
179     if (info.Length() < 1) {
180         return;
181     }
182     double fillOpacity = DEFAULT_OPACITY;
183     ParseJsDouble(info[0], fillOpacity);
184     if (GreatOrEqual(fillOpacity, DEFAULT_OPACITY)) {
185         fillOpacity = DEFAULT_OPACITY;
186     }
187     if (LessOrEqual(fillOpacity, MIN_OPACITY)) {
188         fillOpacity = MIN_OPACITY;
189     }
190     ShapeAbstractModel::GetInstance()->SetFillOpacity(fillOpacity);
191 }
192 
SetStrokeWidth(const JSCallbackInfo & info)193 void JSShapeAbstract::SetStrokeWidth(const JSCallbackInfo& info)
194 {
195     if (info.Length() < 1) {
196         return;
197     }
198     // the default value is 1.0_vp
199     CalcDimension lineWidth = 1.0_vp;
200     if (info[0]->IsString()) {
201         const std::string& value = info[0]->ToString();
202         if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
203             lineWidth = StringUtils::StringToDimensionWithUnit(value, DimensionUnit::VP, 1.0);
204         } else {
205             if (!StringUtils::StringToDimensionWithUnitNG(value, lineWidth, DimensionUnit::VP, 1.0)) {
206                 // unit is invalid, use default value(1.0vp) instead.
207                 lineWidth = 1.0_vp;
208             }
209         }
210     } else {
211         ParseJsDimensionVp(info[0], lineWidth);
212     }
213     if (lineWidth.IsNegative()) {
214         lineWidth = 1.0_vp;
215     }
216     ShapeAbstractModel::GetInstance()->SetStrokeWidth(lineWidth);
217 }
218 
SetAntiAlias(bool antiAlias)219 void JSShapeAbstract::SetAntiAlias(bool antiAlias)
220 {
221     ShapeAbstractModel::GetInstance()->SetAntiAlias(antiAlias);
222 }
223 
JsWidth(const JSCallbackInfo & info)224 void JSShapeAbstract::JsWidth(const JSCallbackInfo& info)
225 {
226     if (info.Length() < 1) {
227         return;
228     }
229 
230     SetWidth(info[0]);
231 }
232 
SetWidth(const JSRef<JSVal> & jsValue)233 void JSShapeAbstract::SetWidth(const JSRef<JSVal>& jsValue)
234 {
235     CalcDimension value;
236     if (jsValue->IsUndefined()) {
237         ViewAbstractModel::GetInstance()->ClearWidthOrHeight(true);
238         return;
239     }
240     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
241         if (!ParseJsDimensionVp(jsValue, value)) {
242             return;
243         }
244     } else {
245         if (!ParseJsDimensionVpNG(jsValue, value)) {
246             ViewAbstractModel::GetInstance()->ClearWidthOrHeight(true);
247             return;
248         }
249     }
250 
251     if (LessNotEqual(value.Value(), 0.0)) {
252         value.SetValue(0.0);
253     }
254     ShapeAbstractModel::GetInstance()->SetWidth(value);
255 }
256 
JsHeight(const JSCallbackInfo & info)257 void JSShapeAbstract::JsHeight(const JSCallbackInfo& info)
258 {
259     if (info.Length() < 1) {
260         return;
261     }
262 
263     SetHeight(info[0]);
264 }
265 
SetHeight(const JSRef<JSVal> & jsValue)266 void JSShapeAbstract::SetHeight(const JSRef<JSVal>& jsValue)
267 {
268     CalcDimension value;
269     if (jsValue->IsUndefined()) {
270         ViewAbstractModel::GetInstance()->ClearWidthOrHeight(false);
271         return;
272     }
273     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
274         if (!ParseJsDimensionVp(jsValue, value)) {
275             return;
276         }
277     } else {
278         if (!ParseJsDimensionVpNG(jsValue, value)) {
279             ViewAbstractModel::GetInstance()->ClearWidthOrHeight(false);
280             return;
281         }
282     }
283 
284     if (LessNotEqual(value.Value(), 0.0)) {
285         value.SetValue(0.0);
286     }
287     ShapeAbstractModel::GetInstance()->SetHeight(value);
288 }
289 
JsSize(const JSCallbackInfo & info)290 void JSShapeAbstract::JsSize(const JSCallbackInfo& info)
291 {
292     if (info.Length() < 1) {
293         return;
294     }
295 
296     if (!info[0]->IsObject()) {
297         return;
298     }
299 
300     JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
301     SetWidth(sizeObj->GetProperty("width"));
302     SetHeight(sizeObj->GetProperty("height"));
303 }
304 
ObjectWidth(const JSCallbackInfo & info)305 void JSShapeAbstract::ObjectWidth(const JSCallbackInfo& info)
306 {
307     info.ReturnSelf();
308     if (info.Length() < 1) {
309         return;
310     }
311 
312     ObjectWidth(info[0]);
313 }
314 
ObjectWidth(const JSRef<JSVal> & jsValue)315 void JSShapeAbstract::ObjectWidth(const JSRef<JSVal>& jsValue)
316 {
317     CalcDimension value;
318     if (!ParseJsDimensionVp(jsValue, value)) {
319         return;
320     }
321     if (LessNotEqual(value.Value(), 0.0)) {
322         return;
323     }
324     if (basicShape_) {
325         basicShape_->SetWidth(value);
326     }
327 }
328 
ObjectHeight(const JSCallbackInfo & info)329 void JSShapeAbstract::ObjectHeight(const JSCallbackInfo& info)
330 {
331     info.ReturnSelf();
332     if (info.Length() < 1) {
333         return;
334     }
335 
336     ObjectHeight(info[0]);
337 }
338 
ObjectHeight(const JSRef<JSVal> & jsValue)339 void JSShapeAbstract::ObjectHeight(const JSRef<JSVal>& jsValue)
340 {
341     CalcDimension value;
342     if (!ParseJsDimensionVp(jsValue, value)) {
343         return;
344     }
345     if (LessNotEqual(value.Value(), 0.0)) {
346         return;
347     }
348     if (basicShape_) {
349         basicShape_->SetHeight(value);
350     }
351 }
352 
ObjectSize(const JSCallbackInfo & info)353 void JSShapeAbstract::ObjectSize(const JSCallbackInfo& info)
354 {
355     if (info.Length() < 1) {
356         return;
357     }
358 
359     if (!info[0]->IsObject()) {
360         return;
361     }
362 
363     JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
364     ObjectWidth(sizeObj->GetProperty("width"));
365     ObjectHeight(sizeObj->GetProperty("height"));
366 }
367 
ObjectOffset(const JSCallbackInfo & info)368 void JSShapeAbstract::ObjectOffset(const JSCallbackInfo& info)
369 {
370     info.ReturnSelf();
371     if (info.Length() > 0 && info[0]->IsObject()) {
372         JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
373         JSRef<JSVal> xVal = sizeObj->GetProperty("x");
374         JSRef<JSVal> yVal = sizeObj->GetProperty("y");
375         CalcDimension x;
376         CalcDimension y;
377         if (basicShape_ && ParseJsDimensionVp(xVal, x) && ParseJsDimensionVp(yVal, y)) {
378             basicShape_->SetOffset(DimensionOffset(x, y));
379         }
380     }
381 }
382 
ObjectFill(const JSCallbackInfo & info)383 void JSShapeAbstract::ObjectFill(const JSCallbackInfo& info)
384 {
385     info.ReturnSelf();
386     if (info.Length() < 1) {
387         return;
388     }
389 
390     Color color;
391     if (ParseJsColor(info[0], color) && basicShape_) {
392         basicShape_->SetColor(color);
393     }
394 }
395 
JSBind(BindingTarget globalObj)396 void JSShapeAbstract::JSBind(BindingTarget globalObj)
397 {
398     JSClass<JSShapeAbstract>::Declare("JSShapeAbstract");
399     MethodOptions opt = MethodOptions::NONE;
400     JSClass<JSShapeAbstract>::StaticMethod("stroke", &JSShapeAbstract::SetStroke, opt);
401     JSClass<JSShapeAbstract>::StaticMethod("fill", &JSShapeAbstract::SetFill, opt);
402     JSClass<JSShapeAbstract>::StaticMethod("foregroundColor", &JSShapeAbstract::SetForegroundColor, opt);
403     JSClass<JSShapeAbstract>::StaticMethod("strokeDashOffset", &JSShapeAbstract::SetStrokeDashOffset, opt);
404     JSClass<JSShapeAbstract>::StaticMethod("strokeDashArray", &JSShapeAbstract::SetStrokeDashArray);
405     JSClass<JSShapeAbstract>::StaticMethod("strokeLineCap", &JSShapeAbstract::SetStrokeLineCap, opt);
406     JSClass<JSShapeAbstract>::StaticMethod("strokeLineJoin", &JSShapeAbstract::SetStrokeLineJoin, opt);
407     JSClass<JSShapeAbstract>::StaticMethod("strokeMiterLimit", &JSShapeAbstract::SetStrokeMiterLimit, opt);
408     JSClass<JSShapeAbstract>::StaticMethod("strokeOpacity", &JSShapeAbstract::SetStrokeOpacity, opt);
409     JSClass<JSShapeAbstract>::StaticMethod("fillOpacity", &JSShapeAbstract::SetFillOpacity, opt);
410     JSClass<JSShapeAbstract>::StaticMethod("strokeWidth", &JSShapeAbstract::SetStrokeWidth, opt);
411     JSClass<JSShapeAbstract>::StaticMethod("antiAlias", &JSShapeAbstract::SetAntiAlias, opt);
412     JSClass<JSShapeAbstract>::StaticMethod("width", &JSShapeAbstract::JsWidth, opt);
413     JSClass<JSShapeAbstract>::StaticMethod("height", &JSShapeAbstract::JsHeight, opt);
414     JSClass<JSShapeAbstract>::StaticMethod("size", &JSShapeAbstract::JsSize, opt);
415     JSClass<JSShapeAbstract>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
416     JSClass<JSShapeAbstract>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
417     JSClass<JSShapeAbstract>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
418     JSClass<JSShapeAbstract>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
419     JSClass<JSShapeAbstract>::InheritAndBind<JSViewAbstract>(globalObj);
420 }
421 
SetSize(const JSCallbackInfo & info)422 void JSShapeAbstract::SetSize(const JSCallbackInfo& info)
423 {
424     if (info.Length() > 0 && info[0]->IsObject()) {
425         JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
426         JSRef<JSVal> width = obj->GetProperty("width");
427         JSRef<JSVal> height = obj->GetProperty("height");
428         SetWidth(width);
429         SetHeight(height);
430     }
431 }
432 
ObjectPosition(const JSCallbackInfo & info)433 void JSShapeAbstract::ObjectPosition(const JSCallbackInfo& info)
434 {
435     info.ReturnSelf();
436     if (info.Length() > 0 && info[0]->IsObject()) {
437         JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
438         JSRef<JSVal> xVal = sizeObj->GetProperty("x");
439         JSRef<JSVal> yVal = sizeObj->GetProperty("y");
440         CalcDimension x;
441         CalcDimension y;
442         DimensionOffset position(x, y);
443         CHECK_NULL_VOID(basicShape_);
444         if (ParseJsDimensionVp(xVal, x)) {
445             position.SetX(x);
446         }
447         if (ParseJsDimensionVp(yVal, y)) {
448             position.SetY(y);
449         }
450         basicShape_->SetPosition(position);
451     }
452 }
453 
SetForegroundColor(const JSCallbackInfo & info)454 void JSShapeAbstract::SetForegroundColor(const JSCallbackInfo& info)
455 {
456     if (info.Length() < 1) {
457         return;
458     }
459     Color foregroundColor;
460     ForegroundColorStrategy strategy;
461     if (ParseJsColorStrategy(info[0], strategy)) {
462         ShapeAbstractModel::GetInstance()->SetFill(Color::FOREGROUND);
463         ViewAbstractModel::GetInstance()->SetForegroundColorStrategy(strategy);
464         return;
465     }
466     if (!ParseJsColor(info[0], foregroundColor)) {
467         ShapeAbstractModel::GetInstance()->SetFill(Color::BLACK);
468         ViewAbstractModel::GetInstance()->SetForegroundColor(Color::BLACK);
469         return;
470     }
471     ShapeAbstractModel::GetInstance()->SetFill(foregroundColor);
472     ViewAbstractModel::GetInstance()->SetForegroundColor(foregroundColor);
473 }
474 } // namespace OHOS::Ace::Framework
475