1 /*
2  * Copyright (c) 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 "js_lattice.h"
17 #include "js_drawing_utils.h"
18 #include "native_value.h"
19 
20 namespace OHOS::Rosen {
21 namespace Drawing {
22 const std::string CLASS_NAME = "Lattice";
23 thread_local napi_ref JsLattice::constructor_ = nullptr;
Init(napi_env env,napi_value exportObj)24 napi_value JsLattice::Init(napi_env env, napi_value exportObj)
25 {
26     napi_property_descriptor properties[] = {
27         DECLARE_NAPI_STATIC_FUNCTION("createImageLattice", JsLattice::CreateImageLattice),
28     };
29 
30     napi_value constructor = nullptr;
31     napi_status status = napi_define_class(env, CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Constructor, nullptr,
32                                            sizeof(properties) / sizeof(properties[0]), properties, &constructor);
33     if (status != napi_ok) {
34         ROSEN_LOGE("JsLattice::Init failed to define lattice class");
35         return nullptr;
36     }
37 
38     status = napi_create_reference(env, constructor, 1, &constructor_);
39     if (status != napi_ok) {
40         ROSEN_LOGE("JsLattice::Init failed to create reference of constructor");
41         return nullptr;
42     }
43 
44     status = napi_set_named_property(env, exportObj, CLASS_NAME.c_str(), constructor);
45     if (status != napi_ok) {
46         ROSEN_LOGE("JsLattice::Init failed to set constructor");
47         return nullptr;
48     }
49 
50     status = napi_define_properties(env, exportObj, sizeof(properties) / sizeof(properties[0]), properties);
51     if (status != napi_ok) {
52         ROSEN_LOGE("JsLattice::Init failed to define static function");
53         return nullptr;
54     }
55     return exportObj;
56 }
57 
Finalizer(napi_env env,void * data,void * hint)58 void JsLattice::Finalizer(napi_env env, void* data, void* hint)
59 {
60     std::unique_ptr<JsLattice>(static_cast<JsLattice*>(data));
61 }
62 
~JsLattice()63 JsLattice::~JsLattice()
64 {
65     m_lattice = nullptr;
66 }
67 
Constructor(napi_env env,napi_callback_info info)68 napi_value JsLattice::Constructor(napi_env env, napi_callback_info info)
69 {
70     size_t argCount = 0;
71     napi_value jsThis = nullptr;
72     napi_status status = napi_get_cb_info(env, info, &argCount, nullptr, &jsThis, nullptr);
73     if (status != napi_ok) {
74         ROSEN_LOGE("JsLattice::Constructor failed to napi_get_cb_info");
75         return nullptr;
76     }
77 
78     JsLattice *jsLattice = new JsLattice();
79     status = napi_wrap(env, jsThis, jsLattice, JsLattice::Destructor, nullptr, nullptr);
80     if (status != napi_ok) {
81         delete jsLattice;
82         ROSEN_LOGE("JsLattice::Constructor failed to wrap native instance");
83         return nullptr;
84     }
85     return jsThis;
86 }
87 
Destructor(napi_env env,void * nativeObject,void * finalize)88 void JsLattice::Destructor(napi_env env, void *nativeObject, void *finalize)
89 {
90     (void)finalize;
91     if (nativeObject != nullptr) {
92         JsLattice *napi = reinterpret_cast<JsLattice *>(nativeObject);
93         delete napi;
94     }
95 }
96 
GetLatticeDividers(napi_env env,napi_value dividersArray,uint32_t count,std::vector<int> & dividers)97 bool GetLatticeDividers(napi_env env, napi_value dividersArray, uint32_t count, std::vector<int>& dividers)
98 {
99     uint32_t dividersSize = 0;
100     napi_get_array_length(env, dividersArray, &dividersSize);
101     if (dividersSize != count || dividersSize > 5) { // 5: max value
102         ROSEN_LOGE("JsLattice::CreateImageLattice dividers are invalid");
103         return false;
104     }
105     if (dividersSize != 0) {
106         dividers.reserve(dividersSize);
107         for (uint32_t i = 0; i < dividersSize; i++) {
108             napi_value tempDiv = nullptr;
109             napi_get_element(env, dividersArray, i, &tempDiv);
110             int div = 0;
111             if (napi_get_value_int32(env, tempDiv, &div) != napi_ok) {
112                 ROSEN_LOGE("JsLattice::CreateImageLattice divider is invalid");
113                 return false;
114             }
115             dividers.push_back(div);
116         }
117     }
118     return true;
119 }
120 
GetLatticeRectTypes(napi_env env,napi_value rectTypesArray,uint32_t count,std::vector<Lattice::RectType> & latticeRectTypes)121 bool GetLatticeRectTypes(napi_env env, napi_value rectTypesArray, uint32_t count,
122     std::vector<Lattice::RectType>& latticeRectTypes)
123 {
124     uint32_t rectTypesSize = 0;
125     napi_get_array_length(env, rectTypesArray, &rectTypesSize);
126     if ((rectTypesSize != 0 && rectTypesSize != count) || rectTypesSize > 36) { // 36: max value
127         ROSEN_LOGE("JsLattice::CreateImageLattice rectTypes are invalid");
128         return false;
129     }
130     if (rectTypesSize != 0) {
131         latticeRectTypes.reserve(rectTypesSize);
132         for (uint32_t i = 0; i < rectTypesSize; i++) {
133             napi_value tempType = nullptr;
134             napi_get_element(env, rectTypesArray, i, &tempType);
135             int rectType = 0;
136             if (napi_get_value_int32(env, tempType, &rectType) != napi_ok ||
137                 rectType < 0 || rectType > 2) { // 2: FIXEDCOLOR
138                 ROSEN_LOGE("JsLattice::CreateImageLattice rectType is invalid");
139                 return false;
140             }
141             latticeRectTypes.push_back(static_cast<Lattice::RectType>(rectType));
142         }
143     }
144     return true;
145 }
146 
GetLatticeColors(napi_env env,napi_value colorsArray,uint32_t count,std::vector<Color> & latticeColors)147 bool GetLatticeColors(napi_env env, napi_value colorsArray, uint32_t count, std::vector<Color>& latticeColors)
148 {
149     uint32_t colorsSize = 0;
150     napi_get_array_length(env, colorsArray, &colorsSize);
151     if ((colorsSize != 0 && colorsSize != count) || colorsSize > 36) { // 36: max value
152         ROSEN_LOGE("JsLattice::CreateImageLattice colors are invalid");
153         return false;
154     }
155     if (colorsSize != 0) {
156         latticeColors.reserve(colorsSize);
157         for (uint32_t i = 0; i < colorsSize; i++) {
158             napi_value tempColor = nullptr;
159             napi_get_element(env, colorsArray, i, &tempColor);
160             Drawing::Color drawingColor;
161             int32_t argb[ARGC_FOUR] = {0};
162             if (!ConvertFromJsColor(env, tempColor, argb, ARGC_FOUR)) {
163                 ROSEN_LOGE("JsLattice::CreateImageLattice colors is invalid");
164                 return false;
165             }
166             drawingColor = Color::ColorQuadSetARGB(
167                 argb[ARGC_ZERO], argb[ARGC_ONE], argb[ARGC_TWO], argb[ARGC_THREE]);
168             latticeColors.push_back(drawingColor);
169         }
170     }
171     return true;
172 }
173 
CreateImageLattice(napi_env env,napi_callback_info info)174 napi_value JsLattice::CreateImageLattice(napi_env env, napi_callback_info info)
175 {
176     size_t argc = ARGC_SEVEN;
177     napi_value argv[ARGC_SEVEN] = {nullptr};
178     CHECK_PARAM_NUMBER_WITH_OPTIONAL_PARAMS(argv, argc, ARGC_FOUR, ARGC_SEVEN);
179 
180     uint32_t xCount = 0;
181     GET_UINT32_PARAM(ARGC_TWO, xCount);
182     uint32_t yCount = 0;
183     GET_UINT32_PARAM(ARGC_THREE, yCount);
184 
185     Lattice lat;
186     if (!GetLatticeDividers(env, argv[ARGC_ZERO], xCount, lat.fXDivs)) {
187         ROSEN_LOGE("JsLattice::CreateImageLattice xDividers are invalid");
188         return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Incorrect parameter0 type.");
189     }
190     if (!GetLatticeDividers(env, argv[ARGC_ONE], yCount, lat.fYDivs)) {
191         ROSEN_LOGE("JsLattice::CreateImageLattice yDividers are invalid");
192         return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Incorrect parameter1 type.");
193     }
194     lat.fXCount = xCount;
195     lat.fYCount = yCount;
196 
197     if (argc >= ARGC_FIVE) {
198         napi_valuetype valueType = napi_undefined;
199         if (napi_typeof(env, argv[ARGC_FOUR], &valueType) != napi_ok ||
200             (valueType != napi_null && valueType != napi_object)) {
201             return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM,
202                 "Incorrect CreateImageLattice parameter5 type.");
203         }
204         if (valueType == napi_object) {
205             int32_t ltrb[ARGC_FOUR] = {0};
206             if (!ConvertFromJsIRect(env, argv[ARGC_FOUR], ltrb, ARGC_FOUR)) {
207                 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM,
208                     "Incorrect parameter5 type. The type of left, top, right and bottom must be number.");
209             }
210             lat.fBounds.push_back(Drawing::RectI(ltrb[ARGC_ZERO], ltrb[ARGC_ONE], ltrb[ARGC_TWO], ltrb[ARGC_THREE]));
211         }
212     }
213 
214     if (argc >= ARGC_SIX) {
215         int count = (xCount + 1) * (yCount + 1); // 1: grid size need + 1
216         if (!GetLatticeRectTypes(env, argv[ARGC_FIVE], count, lat.fRectTypes)) {
217             return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Incorrect parameter6 type.");
218         }
219 
220         if (argc == ARGC_SEVEN) {
221             if (!GetLatticeColors(env, argv[ARGC_SIX], count, lat.fColors)) {
222                 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Incorrect parameter7 type.");
223             }
224         }
225     }
226     return JsLattice::Create(env, std::make_shared<Lattice>(lat));
227 }
228 
Create(napi_env env,std::shared_ptr<Lattice> lattice)229 napi_value JsLattice::Create(napi_env env, std::shared_ptr<Lattice> lattice)
230 {
231     napi_value objValue = nullptr;
232     napi_create_object(env, &objValue);
233     if (objValue == nullptr || lattice == nullptr) {
234         ROSEN_LOGE("JsLattice::Create object is null!");
235         return nullptr;
236     }
237 
238     std::unique_ptr<JsLattice> jsLattice = std::make_unique<JsLattice>(lattice);
239     napi_wrap(env, objValue, jsLattice.release(), JsLattice::Finalizer, nullptr, nullptr);
240 
241     if (objValue == nullptr) {
242         ROSEN_LOGE("JsLattice::Create object value is null!");
243         return nullptr;
244     }
245     return objValue;
246 }
247 
GetLattice()248 std::shared_ptr<Lattice> JsLattice::GetLattice()
249 {
250     return m_lattice;
251 }
252 } // namespace Drawing
253 } // namespace OHOS::Rosen
254