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_path_iterator.h"
17
18 #include "native_value.h"
19
20 #include "path_napi/js_path.h"
21
22 namespace OHOS::Rosen {
23 namespace Drawing {
24 thread_local napi_ref JsPathIterator::constructor_ = nullptr;
25 const std::string CLASS_NAME = "PathIterator";
26
27 static const napi_property_descriptor g_properties[] = {
28 DECLARE_NAPI_FUNCTION("next", JsPathIterator::Next),
29 DECLARE_NAPI_FUNCTION("hasNext", JsPathIterator::HasNext),
30 DECLARE_NAPI_FUNCTION("peek", JsPathIterator::Peek),
31 };
32
nextInternal()33 PathVerb JsPathIterator::nextInternal()
34 {
35 if (m_done) {
36 return PathVerb::DONE;
37 }
38 PathVerb verb = m_iter->Next(m_points);
39 if (verb == PathVerb::CONIC) {
40 float weight = m_iter->ConicWeight();
41 m_points[MAX_PAIRS_PATHVERB-1].SetX(weight);
42 } else if (verb == PathVerb::DONE) {
43 m_done = true;
44 }
45 return verb;
46 }
47
getReturnVerb(const PathVerb & cachedVerb)48 PathVerb JsPathIterator::getReturnVerb(const PathVerb& cachedVerb)
49 {
50 switch (cachedVerb) {
51 case PathVerb::MOVE: return PathVerb::MOVE;
52 case PathVerb::LINE: return PathVerb::LINE;
53 case PathVerb::QUAD: return PathVerb::QUAD;
54 case PathVerb::CONIC: return PathVerb::CONIC;
55 case PathVerb::CUBIC: return PathVerb::CUBIC;
56 case PathVerb::CLOSE: return PathVerb::CLOSE;
57 case PathVerb::DONE: return PathVerb::DONE;
58 default: return nextInternal();
59 }
60 }
61
Init(napi_env env,napi_value exportObj)62 napi_value JsPathIterator::Init(napi_env env, napi_value exportObj)
63 {
64 napi_value constructor = nullptr;
65 napi_status status = napi_define_class(env, CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Constructor, nullptr,
66 sizeof(g_properties) / sizeof(g_properties[0]), g_properties, &constructor);
67 if (status != napi_ok) {
68 ROSEN_LOGE("Failed to define PathIterator class");
69 return nullptr;
70 }
71
72 status = napi_create_reference(env, constructor, 1, &constructor_);
73 if (status != napi_ok) {
74 ROSEN_LOGE("Failed to create reference of constructor");
75 return nullptr;
76 }
77
78 status = napi_set_named_property(env, exportObj, CLASS_NAME.c_str(), constructor);
79 if (status != napi_ok) {
80 ROSEN_LOGE("Failed to set constructor");
81 return nullptr;
82 }
83
84 return exportObj;
85 }
86
Constructor(napi_env env,napi_callback_info info)87 napi_value JsPathIterator::Constructor(napi_env env, napi_callback_info info)
88 {
89 size_t argc = ARGC_ONE;
90 napi_value argv[ARGC_ONE] = {nullptr};
91 napi_value jsThis = nullptr;
92 JsPathIterator* jsPathIterator = nullptr;
93 napi_status status = napi_get_cb_info(env, info, &argc, argv, &jsThis, nullptr);
94 if (status != napi_ok) {
95 ROSEN_LOGE("PathIterator::Constructor Failed to napi_get_cb_info");
96 return nullptr;
97 }
98 if (argc == ARGC_ONE) {
99 napi_valuetype valueType = napi_undefined;
100 if (argv[0] == nullptr || napi_typeof(env, argv[0], &valueType) != napi_ok || valueType != napi_object) {
101 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM,
102 "JsPathIterator::Constructor Argv[0] is invalid");
103 }
104 JsPath* path = nullptr;
105 GET_UNWRAP_PARAM(ARGC_ZERO, path);
106 PathIterator* p = new PathIterator(*path->GetPath());
107 jsPathIterator = new JsPathIterator(p);
108 } else {
109 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Incorrect number of parameters.");
110 }
111 if (!jsPathIterator) {
112 ROSEN_LOGE("Failed to create JsPathIterator");
113 return nullptr;
114 }
115 status = napi_wrap(env, jsThis, jsPathIterator,
116 JsPathIterator::Destructor, nullptr, nullptr);
117 if (status != napi_ok) {
118 delete jsPathIterator;
119 ROSEN_LOGE("JsPathIterator::Constructor Failed to wrap native instance");
120 return nullptr;
121 }
122 return jsThis;
123 }
124
Destructor(napi_env env,void * nativeObject,void * finalize)125 void JsPathIterator::Destructor(napi_env env, void *nativeObject, void *finalize)
126 {
127 (void)finalize;
128 if (nativeObject != nullptr) {
129 JsPathIterator *napi = reinterpret_cast<JsPathIterator *>(nativeObject);
130 delete napi;
131 }
132 }
133
CreateJsPathIterator(napi_env env,PathIterator * iter)134 napi_value JsPathIterator::CreateJsPathIterator(napi_env env, PathIterator* iter)
135 {
136 napi_value constructor = nullptr;
137 napi_status status = napi_get_reference_value(env, constructor_, &constructor);
138 if (status != napi_ok) {
139 delete iter;
140 ROSEN_LOGE("JsPathIterator::CreateJsPathIterator Failed to napi_get_reference_value!");
141 return nullptr;
142 }
143 napi_value result = nullptr;
144 napi_create_object(env, &result);
145 if (result == nullptr) {
146 delete iter;
147 ROSEN_LOGE("JsPathIterator::CreateJsPath Create pathiterator object failed!");
148 return nullptr;
149 }
150 JsPathIterator* jsPathIterator = new JsPathIterator(iter);
151 status = napi_wrap(env, result, jsPathIterator, JsPathIterator::Destructor, nullptr, nullptr);
152 if (status != napi_ok) {
153 delete jsPathIterator;
154 ROSEN_LOGE("JsPathIterator::CreateJsPath failed to wrap native instance");
155 return nullptr;
156 }
157 napi_define_properties(env, result, sizeof(g_properties) / sizeof(g_properties[0]), g_properties);
158 return result;
159 }
160
~JsPathIterator()161 JsPathIterator::~JsPathIterator()
162 {
163 if (m_iter != nullptr) {
164 delete m_iter;
165 m_iter = nullptr;
166 }
167 }
168
Next(napi_env env,napi_callback_info info)169 napi_value JsPathIterator::Next(napi_env env, napi_callback_info info)
170 {
171 JsPathIterator* me = CheckParamsAndGetThis<JsPathIterator>(env, info);
172 return (me != nullptr) ? me->OnNext(env, info) : nullptr;
173 }
174
HasNext(napi_env env,napi_callback_info info)175 napi_value JsPathIterator::HasNext(napi_env env, napi_callback_info info)
176 {
177 JsPathIterator* me = CheckParamsAndGetThis<JsPathIterator>(env, info);
178 return (me != nullptr) ? me->OnHasNext(env, info) : nullptr;
179 }
180
Peek(napi_env env,napi_callback_info info)181 napi_value JsPathIterator::Peek(napi_env env, napi_callback_info info)
182 {
183 JsPathIterator* me = CheckParamsAndGetThis<JsPathIterator>(env, info);
184 return (me != nullptr) ? me->OnPeek(env, info) : nullptr;
185 }
186
OnNext(napi_env env,napi_callback_info info)187 napi_value JsPathIterator::OnNext(napi_env env, napi_callback_info info)
188 {
189 if (m_iter == nullptr) {
190 ROSEN_LOGE("JsPathIterator::OnNext pathiterator is nullptr");
191 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
192 }
193
194 size_t argc = ARGC_TWO;
195 napi_value argv[ARGC_TWO] = { nullptr };
196 CHECK_PARAM_NUMBER_WITH_OPTIONAL_PARAMS(argv, argc, ARGC_ONE, ARGC_TWO);
197
198 napi_value array = argv[ARGC_ZERO];
199
200 int32_t offset = 0;
201 if (argc == ARGC_TWO) {
202 GET_INT32_PARAM(ARGC_ONE, offset);
203 if (offset < 0) {
204 ROSEN_LOGE("JsPathIterator::OnNext offset is incorrect.");
205 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM,
206 "Parameter verification failed. The value of offset must be greater than or equal to 0.");
207 }
208 }
209
210 uint32_t size = 0;
211 if (napi_get_array_length(env, array, &size) != napi_ok ||
212 size < static_cast<uint32_t>(offset) + MAX_PAIRS_PATHVERB) {
213 ROSEN_LOGE("JsPathIterator::OnNext array size is incorrect.");
214 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Incorrect array size.");
215 }
216
217 PathVerb returnVerb = getReturnVerb(m_verb);
218 m_verb = PathVerb::UNINIT;
219
220 for (uint32_t i = 0; i < MAX_PAIRS_PATHVERB; i++) {
221 if (napi_set_element(env, array, i + offset, ConvertPointToJsValue(env, m_points[i])) != napi_ok) {
222 ROSEN_LOGE("JsPathIterator::OnNext set array failed");
223 return nullptr;
224 }
225 }
226
227 return CreateJsNumber(env, static_cast<uint32_t>(returnVerb));
228 }
229
OnPeek(napi_env env,napi_callback_info info)230 napi_value JsPathIterator::OnPeek(napi_env env, napi_callback_info info)
231 {
232 if (m_iter == nullptr) {
233 ROSEN_LOGE("JsPathIterator::OnPeek pathiterator is nullptr");
234 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
235 }
236 if (m_done) {
237 return CreateJsNumber(env, static_cast<int32_t>(PathVerb::DONE));
238 }
239 return CreateJsNumber(env, static_cast<int32_t>(m_iter->Peek()));
240 }
241
OnHasNext(napi_env env,napi_callback_info info)242 napi_value JsPathIterator::OnHasNext(napi_env env, napi_callback_info info)
243 {
244 if (m_iter == nullptr) {
245 ROSEN_LOGE("JsPathIterator::OnHasNext pathiterator is nullptr");
246 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, "Invalid params.");
247 }
248 if (m_verb == PathVerb::UNINIT) {
249 m_verb = nextInternal();
250 }
251 return CreateJsValue(env, m_verb != PathVerb::DONE);
252 }
253
GetPathIterator()254 PathIterator* JsPathIterator::GetPathIterator()
255 {
256 return m_iter;
257 }
258 } // namespace Drawing
259 } // namespace OHOS::Rosen
260