1 /*
2  * Copyright (c) 2021-2022 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.h"
17 
18 #include "base/geometry/ng/image_mesh.h"
19 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
20 #include "bridge/declarative_frontend/jsview/models/shape_model_impl.h"
21 #include "core/common/container.h"
22 #include "core/components_ng/pattern/shape/shape_abstract_model.h"
23 #include "core/components_ng/pattern/shape/shape_model_ng.h"
24 #include "frameworks/bridge/declarative_frontend/jsview/js_utils.h"
25 
26 namespace OHOS::Ace {
27 namespace {
28 constexpr double DEFAULT_OPACITY = 1.0;
29 constexpr double STROKE_MITERLIMIT_DEFAULT = 4.0f;
30 } // namespace
31 std::unique_ptr<ShapeModel> ShapeModel::instance_;
32 std::mutex ShapeModel::mutex_;
33 
GetInstance()34 ShapeModel* ShapeModel::GetInstance()
35 {
36     if (!instance_) {
37         std::lock_guard<std::mutex> lock(mutex_);
38         if (!instance_) {
39 #ifdef NG_BUILD
40             instance_.reset(new NG::ShapeModelNG());
41 #else
42             if (Container::IsCurrentUseNewPipeline()) {
43                 instance_.reset(new NG::ShapeModelNG());
44             } else {
45                 instance_.reset(new Framework::ShapeModelImpl());
46             }
47 #endif
48         }
49     }
50     return instance_.get();
51 }
52 
53 } // namespace OHOS::Ace
54 
55 namespace OHOS::Ace::Framework {
56 
Create(const JSCallbackInfo & info)57 void JSShape::Create(const JSCallbackInfo& info)
58 {
59     ShapeModel::GetInstance()->Create();
60     JSInteractableView::SetFocusable(true);
61     InitBox(info);
62 }
63 
InitBox(const JSCallbackInfo & info)64 void JSShape::InitBox(const JSCallbackInfo& info)
65 {
66     RefPtr<PixelMap> pixMap = nullptr;
67     if (info.Length() == 1 && info[0]->IsObject()) {
68 #if !defined(PREVIEW)
69         pixMap = CreatePixelMapFromNapiValue(info[0]);
70 #endif
71     }
72     ShapeModel::GetInstance()->InitBox(pixMap);
73 }
74 
SetViewPort(const JSCallbackInfo & info)75 void JSShape::SetViewPort(const JSCallbackInfo& info)
76 {
77     if (info.Length() < 1) {
78         return;
79     }
80     if (info[0]->IsObject()) {
81         JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
82         JSRef<JSVal> leftValue = obj->GetProperty("x");
83         JSRef<JSVal> topValue = obj->GetProperty("y");
84         JSRef<JSVal> widthValue = obj->GetProperty("width");
85         JSRef<JSVal> heightValue = obj->GetProperty("height");
86         ShapeViewBox viewBox;
87         CalcDimension dimLeft;
88         ParseJsDimensionVp(leftValue, dimLeft);
89         CalcDimension dimTop;
90         ParseJsDimensionVp(topValue, dimTop);
91         CalcDimension dimWidth;
92         ParseJsDimensionVp(widthValue, dimWidth);
93         CalcDimension dimHeight;
94         ParseJsDimensionVp(heightValue, dimHeight);
95         ShapeModel::GetInstance()->SetViewPort(dimLeft, dimTop, dimWidth, dimHeight);
96     }
97     info.SetReturnValue(info.This());
98 }
99 
JsWidth(const JSCallbackInfo & info)100 void JSShape::JsWidth(const JSCallbackInfo& info)
101 {
102     if (info.Length() < 1) {
103         return;
104     }
105 
106     JsWidth(info[0]);
107 }
108 
JsWidth(const JSRef<JSVal> & jsValue)109 void JSShape::JsWidth(const JSRef<JSVal>& jsValue)
110 {
111     JSViewAbstract::JsWidth(jsValue);
112     ShapeModel::GetInstance()->SetWidth();
113 }
114 
JsHeight(const JSCallbackInfo & info)115 void JSShape::JsHeight(const JSCallbackInfo& info)
116 {
117     if (info.Length() < 1) {
118         return;
119     }
120 
121     JsHeight(info[0]);
122 }
123 
JsHeight(const JSRef<JSVal> & jsValue)124 void JSShape::JsHeight(const JSRef<JSVal>& jsValue)
125 {
126     JSViewAbstract::JsHeight(jsValue);
127     ShapeModel::GetInstance()->SetHeight();
128 }
129 
JsSize(const JSCallbackInfo & info)130 void JSShape::JsSize(const JSCallbackInfo& info)
131 {
132     if (info.Length() < 1) {
133         return;
134     }
135 
136     if (!info[0]->IsObject()) {
137         return;
138     }
139 
140     JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
141     JsWidth(sizeObj->GetProperty("width"));
142     JsHeight(sizeObj->GetProperty("height"));
143 }
144 
SetStrokeDashArray(const JSCallbackInfo & info)145 void JSShape::SetStrokeDashArray(const JSCallbackInfo& info)
146 {
147     std::vector<Dimension> dashArray;
148     if (info.Length() < 1 || !info[0]->IsArray()) {
149         ShapeModel::GetInstance()->SetStrokeDashArray(dashArray);
150         return;
151     }
152     JSRef<JSArray> array = JSRef<JSArray>::Cast(info[0]);
153     int32_t length = static_cast<int32_t>(array->Length());
154     if (length <= 0) {
155         ShapeModel::GetInstance()->SetStrokeDashArray(dashArray);
156         return;
157     }
158     for (int32_t i = 0; i < length; i++) {
159         JSRef<JSVal> value = array->GetValueAt(i);
160         CalcDimension dim;
161         bool paramIsValid = false;
162         if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
163             paramIsValid = ParseJsDimensionVp(value, dim);
164         } else {
165             paramIsValid = ParseJsDimensionVpNG(value, dim);
166         }
167         if (paramIsValid) {
168             dashArray.emplace_back(dim);
169         } else {
170             dashArray.clear();
171             break;
172         }
173     }
174     // if odd,add twice
175     if (static_cast<uint32_t>(length) == dashArray.size() && (static_cast<uint32_t>(length) & 1)) {
176         for (int32_t i = 0; i < length; i++) {
177             dashArray.emplace_back(dashArray[i]);
178         }
179     }
180     ShapeModel::GetInstance()->SetStrokeDashArray(dashArray);
181     info.SetReturnValue(info.This());
182 }
183 
SetStroke(const JSCallbackInfo & info)184 void JSShape::SetStroke(const JSCallbackInfo& info)
185 {
186     if (info.Length() < 1) {
187         return;
188     }
189     Color strokeColor = Color::TRANSPARENT;
190     ParseJsColor(info[0], strokeColor);
191     ShapeModel::GetInstance()->SetStroke(strokeColor);
192 }
193 
SetFill(const JSCallbackInfo & info)194 void JSShape::SetFill(const JSCallbackInfo& info)
195 {
196     if (info.Length() < 1) {
197         return;
198     }
199     if (info[0]->IsString() && info[0]->ToString() == "none") {
200         ShapeModel::GetInstance()->SetFill(Color::TRANSPARENT);
201     } else {
202         Color fillColor;
203         if (ParseJsColor(info[0], fillColor)) {
204             ShapeModel::GetInstance()->SetFill(fillColor);
205         } else {
206             ShapeModel::GetInstance()->SetFill(Color::BLACK);
207         }
208     }
209 }
210 
SetStrokeDashOffset(const JSCallbackInfo & info)211 void JSShape::SetStrokeDashOffset(const JSCallbackInfo& info)
212 {
213     if (info.Length() < 1) {
214         return;
215     }
216     CalcDimension offset(0.0f);
217     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
218         if (!ParseJsDimensionVp(info[0], offset)) {
219             return;
220         }
221     } else {
222         if (!ParseJsDimensionVpNG(info[0], offset)) {
223             // set to default value(0.0f)
224             offset.SetValue(0.0f);
225         }
226     }
227     ShapeModel::GetInstance()->SetStrokeDashOffset(offset);
228 }
229 
SetStrokeLineCap(int lineCap)230 void JSShape::SetStrokeLineCap(int lineCap)
231 {
232     ShapeModel::GetInstance()->SetStrokeLineCap(lineCap);
233 }
234 
SetStrokeLineJoin(int lineJoin)235 void JSShape::SetStrokeLineJoin(int lineJoin)
236 {
237     ShapeModel::GetInstance()->SetStrokeLineJoin(lineJoin);
238 }
239 
SetStrokeMiterLimit(const JSCallbackInfo & info)240 void JSShape::SetStrokeMiterLimit(const JSCallbackInfo& info)
241 {
242     if (info.Length() < 1) {
243         return;
244     }
245     double miterLimit = STROKE_MITERLIMIT_DEFAULT;
246     ParseJsDouble(info[0], miterLimit);
247     ShapeModel::GetInstance()->SetStrokeMiterLimit(miterLimit);
248 }
249 
SetStrokeOpacity(const JSCallbackInfo & info)250 void JSShape::SetStrokeOpacity(const JSCallbackInfo& info)
251 {
252     if (info.Length() < 1) {
253         return;
254     }
255     double strokeOpacity = DEFAULT_OPACITY;
256     ParseJsDouble(info[0], strokeOpacity);
257     ShapeModel::GetInstance()->SetStrokeOpacity(strokeOpacity);
258 }
259 
SetFillOpacity(const JSCallbackInfo & info)260 void JSShape::SetFillOpacity(const JSCallbackInfo& info)
261 {
262     if (info.Length() < 1) {
263         return;
264     }
265     double fillOpacity = DEFAULT_OPACITY;
266     ParseJsDouble(info[0], fillOpacity);
267     ShapeModel::GetInstance()->SetFillOpacity(fillOpacity);
268 }
269 
SetStrokeWidth(const JSCallbackInfo & info)270 void JSShape::SetStrokeWidth(const JSCallbackInfo& info)
271 {
272     if (info.Length() < 1) {
273         return;
274     }
275     // the default value is 1.0_vp
276     CalcDimension lineWidth = 1.0_vp;
277     if (info[0]->IsString()) {
278         const std::string& value = info[0]->ToString();
279         if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
280             lineWidth = StringUtils::StringToDimensionWithUnit(value, DimensionUnit::VP, 1.0);
281         } else {
282             if (!StringUtils::StringToDimensionWithUnitNG(value, lineWidth, DimensionUnit::VP, 1.0)) {
283                 // unit is invalid, use default value(1.0vp) instead.
284                 lineWidth = 1.0_vp;
285             }
286         }
287     } else {
288         ParseJsDimensionVp(info[0], lineWidth);
289     }
290     if (lineWidth.IsNegative()) {
291         lineWidth = 1.0_vp;
292     }
293     ShapeModel::GetInstance()->SetStrokeWidth(lineWidth);
294 }
295 
SetAntiAlias(bool antiAlias)296 void JSShape::SetAntiAlias(bool antiAlias)
297 {
298     ShapeModel::GetInstance()->SetAntiAlias(antiAlias);
299 }
300 
SetBitmapMesh(const JSCallbackInfo & info)301 void JSShape::SetBitmapMesh(const JSCallbackInfo& info)
302 {
303     if (info.Length() != 3) {
304         return;
305     }
306     std::vector<double> mesh;
307     JSRef<JSVal> meshValue = info[0];
308 
309     if (meshValue->IsObject()) {
310         JSRef<JSObject> meshObj = JSRef<JSObject>::Cast(meshValue);
311         JSRef<JSArray> array = meshObj->GetPropertyNames();
312         for (size_t i = 0; i < array->Length(); i++) {
313             JSRef<JSVal> value = array->GetValueAt(i);
314             if (value->IsString()) {
315                 std::string valueStr;
316                 if (ParseJsString(value, valueStr)) {
317                     double vert;
318                     if (ParseJsDouble(meshObj->GetProperty(valueStr.c_str()), vert)) {
319                         mesh.push_back(vert);
320                     }
321                 }
322             }
323         }
324     }
325     uint32_t column = 0;
326     uint32_t row = 0;
327     JSRef<JSVal> columnValue = info[1];
328     JSRef<JSVal> rowValue = info[2];
329     if (!ParseJsInteger(columnValue, column)) {
330         return;
331     }
332     if (!ParseJsInteger(rowValue, row)) {
333         return;
334     }
335     ShapeModel::GetInstance()->SetBitmapMesh(mesh, static_cast<int32_t>(column), static_cast<int32_t>(row));
336 }
337 
SetForegroundColor(const JSCallbackInfo & info)338 void JSShape::SetForegroundColor(const JSCallbackInfo& info)
339 {
340     if (info.Length() < 1) {
341         return;
342     }
343     Color foregroundColor;
344     ForegroundColorStrategy strategy;
345     if (ParseJsColorStrategy(info[0], strategy)) {
346         ShapeModel::GetInstance()->SetFill(Color::FOREGROUND);
347         ViewAbstractModel::GetInstance()->SetForegroundColorStrategy(strategy);
348         return;
349     }
350     if (!ParseJsColor(info[0], foregroundColor)) {
351         return;
352     }
353     ShapeModel::GetInstance()->SetFill(foregroundColor);
354     ViewAbstractModel::GetInstance()->SetForegroundColor(foregroundColor);
355 }
356 
JSBind(BindingTarget globalObj)357 void JSShape::JSBind(BindingTarget globalObj)
358 {
359     JSClass<JSShape>::Declare("Shape");
360     JSClass<JSShape>::StaticMethod("create", &JSShape::Create);
361     JSClass<JSShape>::StaticMethod("viewPort", &JSShape::SetViewPort);
362 
363     JSClass<JSShape>::StaticMethod("width", &JSShape::JsWidth);
364     JSClass<JSShape>::StaticMethod("height", &JSShape::JsHeight);
365     JSClass<JSShape>::StaticMethod("size", &JSShape::JsSize);
366 
367     JSClass<JSShape>::StaticMethod("stroke", &JSShape::SetStroke);
368     JSClass<JSShape>::StaticMethod("fill", &JSShape::SetFill);
369     JSClass<JSShape>::StaticMethod("strokeDashOffset", &JSShape::SetStrokeDashOffset);
370     JSClass<JSShape>::StaticMethod("strokeDashArray", &JSShape::SetStrokeDashArray);
371     JSClass<JSShape>::StaticMethod("strokeLineCap", &JSShape::SetStrokeLineCap);
372     JSClass<JSShape>::StaticMethod("strokeLineJoin", &JSShape::SetStrokeLineJoin);
373     JSClass<JSShape>::StaticMethod("strokeMiterLimit", &JSShape::SetStrokeMiterLimit);
374     JSClass<JSShape>::StaticMethod("strokeOpacity", &JSShape::SetStrokeOpacity);
375     JSClass<JSShape>::StaticMethod("fillOpacity", &JSShape::SetFillOpacity);
376     JSClass<JSShape>::StaticMethod("strokeWidth", &JSShape::SetStrokeWidth);
377     JSClass<JSShape>::StaticMethod("antiAlias", &JSShape::SetAntiAlias);
378     JSClass<JSShape>::StaticMethod("mesh", &JSShape::SetBitmapMesh);
379     JSClass<JSShape>::StaticMethod("foregroundColor", &JSShape::SetForegroundColor);
380 
381     JSClass<JSShape>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
382     JSClass<JSShape>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
383     JSClass<JSShape>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
384     JSClass<JSShape>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
385     JSClass<JSShape>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
386     JSClass<JSShape>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
387     JSClass<JSShape>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
388     JSClass<JSShape>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
389     JSClass<JSShape>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
390     JSClass<JSShape>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
391     JSClass<JSShape>::InheritAndBind<JSContainerBase>(globalObj);
392 }
393 
394 } // namespace OHOS::Ace::Framework
395