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 "image_packer_napi.h"
17
18 #include "image_common.h"
19 #include "image_log.h"
20 #include "image_napi_utils.h"
21 #include "image_packer.h"
22 #include "image_source.h"
23 #include "image_source_napi.h"
24 #include "image_trace.h"
25 #include "media_errors.h"
26 #include "pixel_map_napi.h"
27
28 #undef LOG_DOMAIN
29 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_IMAGE
30
31 #undef LOG_TAG
32 #define LOG_TAG "ImagePackerNapi"
33
34 namespace {
35 constexpr uint32_t NUM_0 = 0;
36 constexpr uint32_t NUM_1 = 1;
37 constexpr uint32_t NUM_2 = 2;
38 constexpr int32_t INVALID_FD = -1;
39 constexpr int32_t SIZE_256 = 256;
40 constexpr int32_t SIZE_512 = 512;
41 constexpr int32_t SIZE_1024 = 1024;
42 constexpr int32_t SIZE_1440 = 1440;
43 constexpr int32_t SIZE_1920 = 1920;
44 constexpr int64_t FILE_SIZE_300K = 300 * 1024;
45 constexpr int64_t FILE_SIZE_1M = 1 * 1024 * 1024;
46 constexpr int64_t FILE_SIZE_4M = 4 * 1024 * 1024;
47 constexpr int64_t FILE_SIZE_10M = 10 * 1024 * 1024;
48 }
49
50 namespace OHOS {
51 namespace Media {
52 static const std::string CLASS_NAME_IMAGEPACKER = "ImagePacker";
53 thread_local std::shared_ptr<ImagePacker> ImagePackerNapi::sImgPck_ = nullptr;
54 thread_local napi_ref ImagePackerNapi::sConstructor_ = nullptr;
55 napi_ref ImagePackerNapi::packingDynamicRangeRef_ = nullptr;
56 struct ImageEnum {
57 std::string name;
58 int32_t numVal;
59 std::string strVal;
60 };
61 static std::vector<struct ImageEnum> sPackingDynamicRangeMap = {
62 {"AUTO", 0, ""},
63 {"SDR", 1, ""},
64 };
65
66 const int ARGS_THREE = 3;
67 const int ARGS_FOUR = 4;
68 const int PARAM0 = 0;
69 const int PARAM1 = 1;
70 const int PARAM2 = 2;
71 const int PARAM3 = 3;
72 const int PARAM4 = 4;
73 const uint8_t BYTE_FULL = 0xFF;
74 const int32_t SIZE = 100;
75 const int32_t TYPE_IMAGE_SOURCE = 1;
76 const int32_t TYPE_PIXEL_MAP = 2;
77 #if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
78 const int32_t TYPE_PICTURE = 3;
79 const int32_t TYPE_ARRAY = 4;
80 #endif
81 const int64_t DEFAULT_BUFFER_SIZE = 25 * 1024 * 1024; // 25M is the maximum default packedSize
82
83 struct ImagePackerError {
84 bool hasErrorCode = false;
85 int32_t errorCode = SUCCESS;
86 std::string msg;
87 };
88
89 struct ImagePackerAsyncContext {
90 napi_env env;
91 napi_async_work work;
92 napi_deferred deferred;
93 napi_ref callbackRef = nullptr;
94 ImagePackerNapi *constructor_;
95 bool status = false;
96 std::shared_ptr<ImageSource> rImageSource;
97 PackOption packOption;
98 std::shared_ptr<ImagePacker> rImagePacker;
99 std::shared_ptr<PixelMap> rPixelMap;
100 #if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
101 std::shared_ptr<Picture> rPicture;
102 #endif
103 std::shared_ptr<std::vector<std::shared_ptr<PixelMap>>> rPixelMaps;
104 std::unique_ptr<uint8_t[]> resultBuffer;
105 int32_t packType = TYPE_IMAGE_SOURCE;
106 int64_t resultBufferSize = 0;
107 int64_t packedSize = 0;
108 int fd = INVALID_FD;
109 ImagePackerError error;
110 bool needReturnErrorCode = true;
111 };
112
113 struct PackingOption {
114 std::string format;
115 uint8_t quality = 100;
116 };
117
ImagePackerNapi()118 ImagePackerNapi::ImagePackerNapi():env_(nullptr)
119 {}
120
~ImagePackerNapi()121 ImagePackerNapi::~ImagePackerNapi()
122 {
123 release();
124 }
125
IsImagePackerErrorOccur(ImagePackerAsyncContext * ctx)126 static bool IsImagePackerErrorOccur(ImagePackerAsyncContext *ctx)
127 {
128 if (ctx == nullptr) {
129 return true;
130 }
131 if (ctx->error.hasErrorCode) {
132 return ctx->error.errorCode != SUCCESS;
133 }
134 return !ctx->error.msg.empty();
135 }
136
ImagePackerErrorToNapiError(napi_env env,ImagePackerAsyncContext * ctx,napi_value & out)137 static void ImagePackerErrorToNapiError(napi_env env, ImagePackerAsyncContext *ctx, napi_value &out)
138 {
139 if (ctx == nullptr || ctx->status == SUCCESS) {
140 napi_get_undefined(env, &out);
141 return;
142 }
143
144 auto msg = (ctx->error.msg.empty()) ? "Internal error" : ctx->error.msg;
145 if (!ctx->error.hasErrorCode) {
146 if (napi_create_string_utf8(env, msg.c_str(), NAPI_AUTO_LENGTH, &out) != napi_ok) {
147 IMAGE_LOGE("Create error msg only error");
148 }
149 return;
150 }
151
152 auto errorCode = (ctx->error.errorCode != SUCCESS) ? ctx->error.errorCode : ctx->status;
153 napi_value message;
154 napi_value code;
155 if (napi_create_object(env, &out) != napi_ok) {
156 IMAGE_LOGE("Create error object error");
157 return;
158 }
159 if (napi_create_int32(env, errorCode, &code) != napi_ok ||
160 napi_set_named_property(env, out, "code", code) != napi_ok) {
161 IMAGE_LOGE("Create error code error");
162 return;
163 }
164 if (napi_create_string_utf8(env, msg.c_str(), NAPI_AUTO_LENGTH, &message) != napi_ok ||
165 napi_set_named_property(env, out, "message", message) != napi_ok) {
166 IMAGE_LOGE("Create error msg error");
167 return;
168 }
169 }
170
CommonCallbackRoutine(napi_env env,ImagePackerAsyncContext * & connect,const napi_value & valueParam)171 static void CommonCallbackRoutine(napi_env env, ImagePackerAsyncContext* &connect, const napi_value &valueParam)
172 {
173 if (connect == nullptr) {
174 return;
175 }
176 napi_value result[NUM_2] = {0};
177 napi_value retVal;
178 napi_value callback = nullptr;
179
180 napi_get_undefined(env, &result[NUM_0]);
181 napi_get_undefined(env, &result[NUM_1]);
182
183 if (connect->status == SUCCESS) {
184 result[NUM_1] = valueParam;
185 } else {
186 ImagePackerErrorToNapiError(env, connect, result[NUM_0]);
187 }
188
189 if (connect->deferred) {
190 if (connect->status == SUCCESS) {
191 napi_resolve_deferred(env, connect->deferred, result[NUM_1]);
192 } else {
193 napi_reject_deferred(env, connect->deferred, result[NUM_0]);
194 }
195 } else {
196 napi_get_reference_value(env, connect->callbackRef, &callback);
197 napi_call_function(env, nullptr, callback, PARAM2, result, &retVal);
198 napi_delete_reference(env, connect->callbackRef);
199 }
200
201 napi_delete_async_work(env, connect->work);
202
203 delete connect;
204 connect = nullptr;
205 }
206
BuildMsgOnError(ImagePackerAsyncContext * ctx,bool assertion,const std::string msg,int32_t errorCode)207 static void BuildMsgOnError(ImagePackerAsyncContext* ctx, bool assertion,
208 const std::string msg, int32_t errorCode)
209 {
210 if (ctx == nullptr || assertion) {
211 return;
212 }
213 IMAGE_LOGE("%{public}s", msg.c_str());
214 ctx->error.hasErrorCode = ctx->needReturnErrorCode;
215 ctx->error.errorCode = errorCode;
216 ctx->error.msg = msg;
217 }
218
getDefaultBufferSize(int32_t width,int32_t height)219 static int64_t getDefaultBufferSize(int32_t width, int32_t height)
220 {
221 if (width <= SIZE_256 && height <= SIZE_256) {
222 return FILE_SIZE_300K;
223 }
224 if (width <= SIZE_512 && height <= SIZE_512) {
225 return FILE_SIZE_1M;
226 }
227 if (width <= SIZE_1024 && height <= SIZE_1024) {
228 return FILE_SIZE_4M;
229 }
230 if (width <= SIZE_1440 && height <= SIZE_1920) {
231 return FILE_SIZE_10M;
232 }
233 return DEFAULT_BUFFER_SIZE;
234 }
235
getDefaultBufferSize(ImagePackerAsyncContext * context)236 static int64_t getDefaultBufferSize(ImagePackerAsyncContext *context)
237 {
238 if (context == nullptr) {
239 return DEFAULT_BUFFER_SIZE;
240 }
241 ImageInfo imageInfo {};
242 if (context->packType == TYPE_IMAGE_SOURCE) {
243 if (context->rImageSource == nullptr) {
244 return DEFAULT_BUFFER_SIZE;
245 }
246 context->rImageSource->GetImageInfo(imageInfo);
247 } else if (context->packType == TYPE_PIXEL_MAP) {
248 if (context->rPixelMap == nullptr) {
249 return DEFAULT_BUFFER_SIZE;
250 }
251 context->rPixelMap->GetImageInfo(imageInfo);
252 }
253 if (imageInfo.size.width <= 0 || imageInfo.size.height <= 0) {
254 return DEFAULT_BUFFER_SIZE;
255 }
256 return getDefaultBufferSize(imageInfo.size.width, imageInfo.size.height);
257 }
258
STATIC_EXEC_FUNC(Packing)259 STATIC_EXEC_FUNC(Packing)
260 {
261 int64_t packedSize = 0;
262 auto context = static_cast<ImagePackerAsyncContext*>(data);
263 IMAGE_LOGD("ImagePacker BufferSize %{public}" PRId64, context->resultBufferSize);
264 context->resultBuffer = std::make_unique<uint8_t[]>(
265 (context->resultBufferSize <= 0) ? getDefaultBufferSize(context) : context->resultBufferSize);
266 int32_t innerEncodeErrorCode = static_cast<int32_t>(
267 context->packType == TYPE_PICTURE ? IMAGE_ENCODE_FAILED : ERR_IMAGE_ENCODE_FAILED);
268 if (context->resultBuffer == nullptr) {
269 BuildMsgOnError(context, false, "ImagePacker buffer alloc error", innerEncodeErrorCode);
270 return;
271 }
272 auto startRes = context->rImagePacker->StartPacking(context->resultBuffer.get(),
273 context->resultBufferSize, context->packOption);
274 if (startRes != SUCCESS) {
275 context->status = ERROR;
276 BuildMsgOnError(context, false, "Packing start packing failed",
277 startRes == ERR_IMAGE_INVALID_PARAMETER ? COMMON_ERR_INVALID_PARAMETER : innerEncodeErrorCode);
278 return;
279 }
280 if (context->packType == TYPE_IMAGE_SOURCE) {
281 IMAGE_LOGI("ImagePacker set image source");
282 if (context->rImageSource == nullptr) {
283 BuildMsgOnError(context, false, "ImageSource is nullptr", COMMON_ERR_INVALID_PARAMETER);
284 return;
285 }
286 context->rImagePacker->AddImage(*(context->rImageSource));
287 } else if (context->packType == TYPE_PIXEL_MAP) {
288 IMAGE_LOGD("ImagePacker set pixelmap");
289 if (context->rPixelMap == nullptr) {
290 BuildMsgOnError(context, false, "Pixelmap is nullptr", COMMON_ERR_INVALID_PARAMETER);
291 return;
292 }
293 context->rImagePacker->AddImage(*(context->rPixelMap));
294 #if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
295 } else if (context->packType == TYPE_PICTURE) {
296 IMAGE_LOGI("ImagePacker set picture");
297 if (context->rPicture == nullptr) {
298 BuildMsgOnError(context, context->rPicture == nullptr, "Picture is nullptr",
299 COMMON_ERR_INVALID_PARAMETER);
300 return;
301 }
302 context->rImagePacker->AddPicture(*(context->rPicture));
303 #endif
304 }
305 auto packRes = context->rImagePacker->FinalizePacking(packedSize);
306 IMAGE_LOGD("packedSize=%{public}" PRId64, packedSize);
307 if (packRes == SUCCESS) {
308 context->packedSize = packedSize;
309 context->status = SUCCESS;
310 } else if (packedSize == context->resultBufferSize) {
311 context->status = ERROR;
312 if (context->packType == TYPE_PICTURE) {
313 BuildMsgOnError(context, false, "output buffer is not enough", IMAGE_ENCODE_FAILED);
314 } else {
315 BuildMsgOnError(context, false, "output buffer is not enough", ERR_IMAGE_TOO_LARGE);
316 }
317 IMAGE_LOGE("output buffer is not enough.");
318 } else {
319 context->status = ERROR;
320 IMAGE_LOGE("Packing failed, packedSize outside size.");
321 BuildMsgOnError(context, false, "Packing failed",
322 packRes == ERR_IMAGE_INVALID_PARAMETER ? COMMON_ERR_INVALID_PARAMETER : innerEncodeErrorCode);
323 }
324 }
325
STATIC_COMPLETE_FUNC(PackingError)326 STATIC_COMPLETE_FUNC(PackingError)
327 {
328 napi_value result = nullptr;
329 napi_get_undefined(env, &result);
330 auto context = static_cast<ImagePackerAsyncContext*>(data);
331 context->status = ERROR;
332 CommonCallbackRoutine(env, context, result);
333 }
334
STATIC_COMPLETE_FUNC(Packing)335 STATIC_COMPLETE_FUNC(Packing)
336 {
337 napi_value result = nullptr;
338 napi_get_undefined(env, &result);
339 auto context = static_cast<ImagePackerAsyncContext*>(data);
340
341 if (!ImageNapiUtils::CreateArrayBuffer(env, context->resultBuffer.get(),
342 context->packedSize, &result)) {
343 context->status = ERROR;
344 IMAGE_LOGE("napi_create_arraybuffer failed!");
345 napi_get_undefined(env, &result);
346 } else {
347 context->status = SUCCESS;
348 }
349 context->resultBuffer = nullptr;
350 context->resultBufferSize = 0;
351 CommonCallbackRoutine(env, context, result);
352 }
353
CreateEnumTypeObject(napi_env env,napi_valuetype type,napi_ref * ref,std::vector<struct ImageEnum> imageEnumMap)354 static napi_value CreateEnumTypeObject(napi_env env,
355 napi_valuetype type, napi_ref* ref, std::vector<struct ImageEnum> imageEnumMap)
356 {
357 napi_value result = nullptr;
358 napi_status status;
359 int32_t refCount = 1;
360 std::string propName;
361 status = napi_create_object(env, &result);
362 if (status == napi_ok) {
363 for (auto imgEnum : imageEnumMap) {
364 napi_value enumNapiValue = nullptr;
365 if (type == napi_string) {
366 status = napi_create_string_utf8(env, imgEnum.strVal.c_str(),
367 NAPI_AUTO_LENGTH, &enumNapiValue);
368 } else if (type == napi_number) {
369 status = napi_create_int32(env, imgEnum.numVal, &enumNapiValue);
370 } else {
371 IMAGE_LOGE("Unsupported type %{public}d!", type);
372 }
373 if (status == napi_ok && enumNapiValue != nullptr) {
374 status = napi_set_named_property(env, result, imgEnum.name.c_str(), enumNapiValue);
375 }
376 if (status != napi_ok) {
377 IMAGE_LOGE("Failed to add named prop!");
378 break;
379 }
380 }
381
382 if (status == napi_ok) {
383 status = napi_create_reference(env, result, refCount, ref);
384 if (status == napi_ok) {
385 return result;
386 }
387 }
388 }
389 IMAGE_LOGE("CreateEnumTypeObject is Failed!");
390 napi_get_undefined(env, &result);
391 return result;
392 }
393
Init(napi_env env,napi_value exports)394 napi_value ImagePackerNapi::Init(napi_env env, napi_value exports)
395 {
396 napi_property_descriptor props[] = {
397 DECLARE_NAPI_FUNCTION("packing", Packing),
398 DECLARE_NAPI_FUNCTION("packToData", PackToData),
399 DECLARE_NAPI_FUNCTION("packToFile", PackToFile),
400 DECLARE_NAPI_FUNCTION("packingFromPixelMap", Packing),
401 DECLARE_NAPI_FUNCTION("release", Release),
402 DECLARE_NAPI_GETTER("supportedFormats", GetSupportedFormats),
403 };
404 napi_property_descriptor static_prop[] = {
405 DECLARE_NAPI_STATIC_FUNCTION("createImagePacker", CreateImagePacker),
406 DECLARE_NAPI_PROPERTY("PackingDynamicRange",
407 CreateEnumTypeObject(env, napi_number, &packingDynamicRangeRef_, sPackingDynamicRangeMap)),
408 };
409
410 napi_value constructor = nullptr;
411
412 IMG_NAPI_CHECK_RET_D(IMG_IS_OK(
413 napi_define_class(env, CLASS_NAME_IMAGEPACKER.c_str(), NAPI_AUTO_LENGTH, Constructor,
414 nullptr, IMG_ARRAY_SIZE(props), props, &constructor)), nullptr,
415 IMAGE_LOGE("define class fail")
416 );
417
418 IMG_NAPI_CHECK_RET_D(IMG_IS_OK(
419 napi_create_reference(env, constructor, 1, &sConstructor_)),
420 nullptr,
421 IMAGE_LOGE("create reference fail")
422 );
423
424 IMG_NAPI_CHECK_RET_D(IMG_IS_OK(
425 napi_set_named_property(env, exports, CLASS_NAME_IMAGEPACKER.c_str(), constructor)),
426 nullptr,
427 IMAGE_LOGE("set named property fail")
428 );
429 IMG_NAPI_CHECK_RET_D(IMG_IS_OK(
430 napi_define_properties(env, exports, IMG_ARRAY_SIZE(static_prop), static_prop)),
431 nullptr,
432 IMAGE_LOGE("define properties fail")
433 );
434
435 IMAGE_LOGD("Init success");
436 return exports;
437 }
438
Constructor(napi_env env,napi_callback_info info)439 napi_value ImagePackerNapi::Constructor(napi_env env, napi_callback_info info)
440 {
441 napi_value undefineVar = nullptr;
442 napi_get_undefined(env, &undefineVar);
443
444 napi_status status;
445 napi_value thisVar = nullptr;
446
447 status = napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
448 if (status == napi_ok && thisVar != nullptr) {
449 std::unique_ptr<ImagePackerNapi> pImgPackerNapi = std::make_unique<ImagePackerNapi>();
450 if (pImgPackerNapi != nullptr) {
451 pImgPackerNapi->env_ = env;
452 pImgPackerNapi->nativeImgPck = sImgPck_;
453 sImgPck_ = nullptr;
454 status = napi_wrap(env, thisVar, reinterpret_cast<void *>(pImgPackerNapi.get()),
455 ImagePackerNapi::Destructor, nullptr, nullptr);
456 if (status == napi_ok) {
457 pImgPackerNapi.release();
458 return thisVar;
459 } else {
460 IMAGE_LOGE("Failure wrapping js to native napi");
461 }
462 }
463 }
464
465 return undefineVar;
466 }
467
CreateImagePacker(napi_env env,napi_callback_info info)468 napi_value ImagePackerNapi::CreateImagePacker(napi_env env, napi_callback_info info)
469 {
470 ImageTrace imageTrace("ImagePackerNapi::CreateImagePacker");
471 napi_value constructor = nullptr;
472 napi_value result = nullptr;
473 napi_status status;
474
475 std::shared_ptr<ImagePacker> imagePacker = std::make_shared<ImagePacker>();
476 status = napi_get_reference_value(env, sConstructor_, &constructor);
477 if (IMG_IS_OK(status)) {
478 sImgPck_ = imagePacker;
479 status = napi_new_instance(env, constructor, 0, nullptr, &result);
480 if (status == napi_ok) {
481 return result;
482 } else {
483 IMAGE_LOGE("New instance could not be obtained");
484 }
485 }
486 return result;
487 }
488
Destructor(napi_env env,void * nativeObject,void * finalize)489 void ImagePackerNapi::Destructor(napi_env env, void *nativeObject, void *finalize)
490 {
491 }
492
parseDynamicRange(napi_env env,napi_value root)493 static EncodeDynamicRange parseDynamicRange(napi_env env, napi_value root)
494 {
495 uint32_t tmpNumber = 0;
496 if (!GET_UINT32_BY_NAME(root, "desiredDynamicRange", tmpNumber)) {
497 return EncodeDynamicRange::SDR;
498 }
499 if (tmpNumber <= static_cast<uint32_t>(EncodeDynamicRange::SDR)) {
500 return EncodeDynamicRange(tmpNumber);
501 }
502 return EncodeDynamicRange::SDR;
503 }
504
parseNeedsPackProperties(napi_env env,napi_value root)505 static bool parseNeedsPackProperties(napi_env env, napi_value root)
506 {
507 bool tmpNeedsPackProperties = false;
508 if (!GET_BOOL_BY_NAME(root, "needsPackProperties", tmpNeedsPackProperties)) {
509 IMAGE_LOGD("No needsPackProperties in pack option");
510 }
511 return tmpNeedsPackProperties;
512 }
513
parseBufferSize(napi_env env,napi_value root,ImagePackerAsyncContext * context=nullptr)514 static int64_t parseBufferSize(napi_env env, napi_value root, ImagePackerAsyncContext *context = nullptr)
515 {
516 napi_value tempValue = nullptr;
517 int64_t defaultSize = getDefaultBufferSize(context);
518 int64_t tmpNumber = defaultSize;
519 if (napi_get_named_property(env, root, "bufferSize", &tempValue) != napi_ok) {
520 IMAGE_LOGI("No bufferSize, Using default");
521 return tmpNumber;
522 }
523 napi_get_value_int64(env, tempValue, &tmpNumber);
524 IMAGE_LOGD("BufferSize is %{public}" PRId64, tmpNumber);
525 if (tmpNumber < 0) {
526 return defaultSize;
527 }
528 return tmpNumber;
529 }
530
parsePackOptionOfQuality(napi_env env,napi_value root,PackOption * opts)531 static bool parsePackOptionOfQuality(napi_env env, napi_value root, PackOption* opts)
532 {
533 uint32_t tmpNumber = 0;
534 if (!GET_UINT32_BY_NAME(root, "quality", tmpNumber)) {
535 IMAGE_LOGE("No quality in pack option");
536 return false;
537 }
538 if (tmpNumber > SIZE) {
539 IMAGE_LOGE("Invalid quality");
540 opts->quality = BYTE_FULL;
541 } else {
542 opts->quality = static_cast<uint8_t>(tmpNumber & 0xff);
543 }
544 return true;
545 }
546
parsePackOptions(napi_env env,napi_value root,PackOption * opts)547 static bool parsePackOptions(napi_env env, napi_value root, PackOption* opts)
548 {
549 napi_value tmpValue = nullptr;
550
551 if (!GET_NODE_BY_NAME(root, "format", tmpValue)) {
552 IMAGE_LOGE("No format in pack option");
553 return false;
554 }
555
556 bool isFormatArray = false;
557 napi_is_array(env, tmpValue, &isFormatArray);
558 auto formatType = ImageNapiUtils::getType(env, tmpValue);
559
560 IMAGE_LOGD("parsePackOptions format type %{public}d, is array %{public}d",
561 formatType, isFormatArray);
562
563 char buffer[SIZE] = {0};
564 size_t res = 0;
565 if (napi_string == formatType) {
566 if (napi_get_value_string_utf8(env, tmpValue, buffer, SIZE, &res) != napi_ok) {
567 IMAGE_LOGE("Parse pack option format failed");
568 return false;
569 }
570 opts->format = std::string(buffer);
571 } else if (isFormatArray) {
572 uint32_t len = 0;
573 if (napi_get_array_length(env, tmpValue, &len) != napi_ok) {
574 IMAGE_LOGE("Parse pack napi_get_array_length failed");
575 return false;
576 }
577 IMAGE_LOGD("Parse pack array_length=%{public}u", len);
578 for (size_t i = 0; i < len; i++) {
579 napi_value item;
580 napi_get_element(env, tmpValue, i, &item);
581 if (napi_get_value_string_utf8(env, item, buffer, SIZE, &res) != napi_ok) {
582 IMAGE_LOGE("Parse format in item failed %{public}zu", i);
583 continue;
584 }
585 opts->format = std::string(buffer);
586 IMAGE_LOGD("format is %{public}s.", opts->format.c_str());
587 }
588 } else {
589 IMAGE_LOGE("Invalid pack option format type");
590 return false;
591 }
592 opts->desiredDynamicRange = parseDynamicRange(env, root);
593 IMAGE_LOGD("parsePackOptions format:[%{public}s]", opts->format.c_str());
594 opts->needsPackProperties = parseNeedsPackProperties(env, root);
595 return parsePackOptionOfQuality(env, root, opts);
596 }
597
ParserPackingArgumentType(napi_env env,napi_value argv)598 static int32_t ParserPackingArgumentType(napi_env env, napi_value argv)
599 {
600 napi_value constructor = nullptr;
601 napi_value global = nullptr;
602 bool isInstance = false;
603 napi_status ret = napi_invalid_arg;
604
605 napi_get_global(env, &global);
606
607 ret = napi_get_named_property(env, global, "ImageSource", &constructor);
608 if (ret != napi_ok) {
609 IMAGE_LOGE("Get ImageSourceNapi property failed!");
610 }
611
612 ret = napi_instanceof(env, argv, constructor, &isInstance);
613 if (ret == napi_ok && isInstance) {
614 IMAGE_LOGD("This is ImageSourceNapi type!");
615 return TYPE_IMAGE_SOURCE;
616 }
617
618 ret = napi_get_named_property(env, global, "PixelMap", &constructor);
619 if (ret != napi_ok) {
620 IMAGE_LOGE("Get PixelMapNapi property failed!");
621 }
622
623 ret = napi_instanceof(env, argv, constructor, &isInstance);
624 if (ret == napi_ok && isInstance) {
625 IMAGE_LOGD("This is PixelMapNapi type!");
626 return TYPE_PIXEL_MAP;
627 }
628
629 #if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
630 ret = napi_get_named_property(env, global, "Picture", &constructor);
631 if (ret != napi_ok) {
632 IMAGE_LOGE("Get PictureNapi property failed!");
633 }
634
635 ret = napi_instanceof(env, argv, constructor, &isInstance);
636 if (ret == napi_ok && isInstance) {
637 IMAGE_LOGD("This is PictureNapi type!");
638 return TYPE_PICTURE;
639 }
640 #endif
641
642 IMAGE_LOGE("Invalid type!");
643 return TYPE_IMAGE_SOURCE;
644 }
645
GetImageSourceFromNapi(napi_env env,napi_value value)646 static std::shared_ptr<ImageSource> GetImageSourceFromNapi(napi_env env, napi_value value)
647 {
648 if (env == nullptr || value == nullptr) {
649 IMAGE_LOGE("GetImageSourceFromNapi input is null");
650 return nullptr;
651 }
652 std::unique_ptr<ImageSourceNapi> imageSourceNapi = std::make_unique<ImageSourceNapi>();
653 napi_status status = napi_unwrap(env, value, reinterpret_cast<void**>(&imageSourceNapi));
654 if (!IMG_IS_OK(status)) {
655 IMAGE_LOGE("GetImageSourceFromNapi napi unwrap failed");
656 return nullptr;
657 }
658 if (imageSourceNapi == nullptr) {
659 IMAGE_LOGE("GetImageSourceFromNapi imageSourceNapi is nullptr");
660 return nullptr;
661 }
662 return imageSourceNapi.release()->nativeImgSrc;
663 }
664
ParserPackingArguments(napi_env env,napi_value * argv,size_t argc,ImagePackerAsyncContext * context)665 static void ParserPackingArguments(napi_env env,
666 napi_value* argv, size_t argc, ImagePackerAsyncContext* context)
667 {
668 int32_t refCount = 1;
669 if (argc < PARAM1 || argc > PARAM3) {
670 BuildMsgOnError(context, false, "Arguments Count error", COMMON_ERR_INVALID_PARAMETER);
671 }
672 context->packType = ParserPackingArgumentType(env, argv[PARAM0]);
673 if (context->packType == TYPE_PICTURE || context->packType == TYPE_ARRAY) {
674 context->needReturnErrorCode = true;
675 }
676 if (context->packType == TYPE_IMAGE_SOURCE) {
677 context->rImageSource = GetImageSourceFromNapi(env, argv[PARAM0]);
678 BuildMsgOnError(context, context->rImageSource != nullptr, "ImageSource mismatch",
679 COMMON_ERR_INVALID_PARAMETER);
680 } else if (context->packType == TYPE_PIXEL_MAP) {
681 context->rPixelMap = PixelMapNapi::GetPixelMap(env, argv[PARAM0]);
682 BuildMsgOnError(context, context->rPixelMap != nullptr, "PixelMap mismatch",
683 COMMON_ERR_INVALID_PARAMETER);
684 #if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
685 } else if (context->packType == TYPE_PICTURE) {
686 context->rPicture = PictureNapi::GetPicture(env, argv[PARAM0]);
687 BuildMsgOnError(context, context->rPicture != nullptr, "Picture mismatch",
688 COMMON_ERR_INVALID_PARAMETER);
689 #endif
690 }
691 if (argc > PARAM1 && ImageNapiUtils::getType(env, argv[PARAM1]) == napi_object) {
692 BuildMsgOnError(context, parsePackOptions(env, argv[PARAM1], &(context->packOption)),
693 "PackOptions mismatch", COMMON_ERR_INVALID_PARAMETER);
694 context->resultBufferSize = parseBufferSize(env, argv[PARAM1], context);
695 }
696 if (argc > PARAM2 && ImageNapiUtils::getType(env, argv[PARAM2]) == napi_function) {
697 napi_create_reference(env, argv[PARAM2], refCount, &(context->callbackRef));
698 }
699 }
700
Packing(napi_env env,napi_callback_info info,bool needReturnError)701 napi_value ImagePackerNapi::Packing(napi_env env, napi_callback_info info, bool needReturnError)
702 {
703 ImageTrace imageTrace("ImagePackerNapi::Packing");
704 napi_status status;
705 napi_value result = nullptr;
706 size_t argc = ARGS_THREE;
707 napi_value argv[ARGS_THREE] = {0};
708 napi_value thisVar = nullptr;
709
710 napi_get_undefined(env, &result);
711
712 IMG_JS_ARGS(env, info, status, argc, argv, thisVar);
713 NAPI_ASSERT(env, IMG_IS_OK(status), "fail to napi_get_cb_info");
714
715 std::unique_ptr<ImagePackerAsyncContext> asyncContext = std::make_unique<ImagePackerAsyncContext>();
716 status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&asyncContext->constructor_));
717 NAPI_ASSERT(env, IMG_IS_READY(status, asyncContext->constructor_), "fail to unwrap constructor_");
718 asyncContext->needReturnErrorCode = needReturnError;
719
720 asyncContext->rImagePacker = asyncContext->constructor_->nativeImgPck;
721 ParserPackingArguments(env, argv, argc, asyncContext.get());
722 if (asyncContext->callbackRef == nullptr) {
723 napi_create_promise(env, &(asyncContext->deferred), &result);
724 }
725
726 ImageNapiUtils::HicheckerReport();
727
728 if (IsImagePackerErrorOccur(asyncContext.get())) {
729 IMG_CREATE_CREATE_ASYNC_WORK(env, status, "PackingError",
730 [](napi_env env, void *data) {}, PackingErrorComplete, asyncContext, asyncContext->work);
731 } else {
732 IMG_CREATE_CREATE_ASYNC_WORK_WITH_QOS(env, status, "Packing",
733 PackingExec, PackingComplete, asyncContext, asyncContext->work, napi_qos_user_initiated);
734 }
735
736 IMG_NAPI_CHECK_RET_D(IMG_IS_OK(status),
737 nullptr, IMAGE_LOGE("fail to create async work"));
738 return result;
739 }
740
Packing(napi_env env,napi_callback_info info)741 napi_value ImagePackerNapi::Packing(napi_env env, napi_callback_info info)
742 {
743 return Packing(env, info, false);
744 }
745
PackToData(napi_env env,napi_callback_info info)746 napi_value ImagePackerNapi::PackToData(napi_env env, napi_callback_info info)
747 {
748 return Packing(env, info, true);
749 }
750
GetSupportedFormats(napi_env env,napi_callback_info info)751 napi_value ImagePackerNapi::GetSupportedFormats(napi_env env, napi_callback_info info)
752 {
753 napi_value result = nullptr;
754 napi_get_undefined(env, &result);
755
756 napi_status status;
757 napi_value thisVar = nullptr;
758 size_t argCount = 0;
759
760 IMG_JS_ARGS(env, info, status, argCount, nullptr, thisVar);
761
762 IMG_NAPI_CHECK_RET_D(IMG_IS_OK(status), result, IMAGE_LOGE("fail to napi_get_cb_info"));
763
764 std::unique_ptr<ImagePackerAsyncContext> context = std::make_unique<ImagePackerAsyncContext>();
765 status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&context->constructor_));
766
767 IMG_NAPI_CHECK_RET_D(IMG_IS_READY(status, context->constructor_),
768 nullptr, IMAGE_LOGE("fail to unwrap context"));
769 std::set<std::string> formats;
770 uint32_t ret = context->constructor_->nativeImgPck->GetSupportedFormats(formats);
771
772 IMG_NAPI_CHECK_RET_D((ret == SUCCESS),
773 nullptr, IMAGE_LOGE("fail to get supported formats"));
774
775 napi_create_array(env, &result);
776 size_t i = 0;
777 for (const std::string& formatStr: formats) {
778 napi_value format = nullptr;
779 napi_create_string_latin1(env, formatStr.c_str(), formatStr.length(), &format);
780 napi_set_element(env, result, i, format);
781 i++;
782 }
783 return result;
784 }
785
ReleaseComplete(napi_env env,napi_status status,void * data)786 static void ReleaseComplete(napi_env env, napi_status status, void *data)
787 {
788 napi_value result = nullptr;
789 napi_get_undefined(env, &result);
790
791 auto context = static_cast<ImagePackerAsyncContext*>(data);
792 if (context != nullptr && context->constructor_ != nullptr) {
793 delete context->constructor_;
794 context->constructor_ = nullptr;
795 }
796 CommonCallbackRoutine(env, context, result);
797 }
798
Release(napi_env env,napi_callback_info info)799 napi_value ImagePackerNapi::Release(napi_env env, napi_callback_info info)
800 {
801 ImageTrace imageTrace("ImagePackerNapi::Release");
802 napi_value result = nullptr;
803 napi_get_undefined(env, &result);
804
805 int32_t refCount = 1;
806 napi_status status;
807 napi_value thisVar = nullptr;
808 napi_value argValue[NUM_1] = {0};
809 size_t argCount = 1;
810
811 IMG_JS_ARGS(env, info, status, argCount, argValue, thisVar);
812 IMAGE_LOGD("Release argCount is [%{public}zu]", argCount);
813 IMG_NAPI_CHECK_RET_D(IMG_IS_OK(status), result, IMAGE_LOGE("fail to napi_get_cb_info"));
814
815 std::unique_ptr<ImagePackerAsyncContext> context = std::make_unique<ImagePackerAsyncContext>();
816 status = napi_remove_wrap(env, thisVar, reinterpret_cast<void**>(&context->constructor_));
817
818 IMG_NAPI_CHECK_RET_D(IMG_IS_READY(status, context->constructor_), result,
819 IMAGE_LOGE("fail to unwrap context"));
820 IMAGE_LOGD("Release argCount is [%{public}zu]", argCount);
821 if (argCount == 1 && ImageNapiUtils::getType(env, argValue[NUM_0]) == napi_function) {
822 napi_create_reference(env, argValue[NUM_0], refCount, &context->callbackRef);
823 }
824
825 if (context->callbackRef == nullptr) {
826 napi_create_promise(env, &(context->deferred), &result);
827 }
828
829 IMG_CREATE_CREATE_ASYNC_WORK(env, status, "Release",
830 [](napi_env env, void *data) {}, ReleaseComplete, context, context->work);
831 return result;
832 }
833
ParserPackToFileArguments(napi_env env,napi_value * argv,size_t argc,ImagePackerAsyncContext * context)834 static void ParserPackToFileArguments(napi_env env,
835 napi_value* argv, size_t argc, ImagePackerAsyncContext* context)
836 {
837 int32_t refCount = 1;
838 if (argc < PARAM1 || argc > PARAM4) {
839 BuildMsgOnError(context, (argc < PARAM1 || argc > PARAM4),
840 "Arguments Count error", ERR_IMAGE_INVALID_PARAMETER);
841 }
842 context->packType = ParserPackingArgumentType(env, argv[PARAM0]);
843 if (context->packType == TYPE_IMAGE_SOURCE) {
844 context->rImageSource = GetImageSourceFromNapi(env, argv[PARAM0]);
845 BuildMsgOnError(context, context->rImageSource != nullptr,
846 "ImageSource mismatch", ERR_IMAGE_INVALID_PARAMETER);
847 } else if (context->packType == TYPE_PIXEL_MAP) {
848 context->rPixelMap = PixelMapNapi::GetPixelMap(env, argv[PARAM0]);
849 BuildMsgOnError(context, context->rPixelMap != nullptr,
850 "PixelMap mismatch", ERR_IMAGE_INVALID_PARAMETER);
851 #if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
852 } else if (context->packType == TYPE_PICTURE) {
853 context->rPicture = PictureNapi::GetPicture(env, argv[PARAM0]);
854 BuildMsgOnError(context, context->rPicture != nullptr,
855 "Picture mismatch", ERR_IMAGE_INVALID_PARAMETER);
856 #endif
857 }
858 if (argc > PARAM1 && ImageNapiUtils::getType(env, argv[PARAM1]) == napi_number) {
859 BuildMsgOnError(context, (napi_get_value_int32(env, argv[PARAM1], &(context->fd)) == napi_ok &&
860 context->fd > INVALID_FD), "fd mismatch", ERR_IMAGE_INVALID_PARAMETER);
861 }
862 if (argc > PARAM2 && ImageNapiUtils::getType(env, argv[PARAM2]) == napi_object) {
863 BuildMsgOnError(context,
864 parsePackOptions(env, argv[PARAM2], &(context->packOption)),
865 "PackOptions mismatch", ERR_IMAGE_INVALID_PARAMETER);
866 }
867 if (argc > PARAM3 && ImageNapiUtils::getType(env, argv[PARAM3]) == napi_function) {
868 napi_create_reference(env, argv[PARAM3], refCount, &(context->callbackRef));
869 }
870 }
871
872 #if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
SetPicture(ImagePackerAsyncContext * context)873 bool SetPicture(ImagePackerAsyncContext *context)
874 {
875 IMAGE_LOGD("ImagePacker set picture");
876 if (context->rPicture == nullptr) {
877 BuildMsgOnError(context, context->rImageSource == nullptr,
878 "Picture is nullptr", IMAGE_BAD_PARAMETER);
879 return false;
880 }
881 context->rImagePacker->AddPicture(*(context->rPicture));
882 return true;
883 }
884 #endif
885
FinalizePacking(ImagePackerAsyncContext * context)886 static void FinalizePacking(ImagePackerAsyncContext* context)
887 {
888 int64_t packedSize = 0;
889 auto packRes = context->rImagePacker->FinalizePacking(packedSize);
890 IMAGE_LOGD("packRes=%{public}d packedSize=%{public}" PRId64, packRes, packedSize);
891 if (packRes == SUCCESS && packedSize > 0) {
892 context->packedSize = packedSize;
893 context->status = SUCCESS;
894 } else {
895 context->status = ERROR;
896 BuildMsgOnError(context, packRes == SUCCESS, "PackedSize outside size", packRes);
897 IMAGE_LOGE("Packing failed, packedSize outside size.");
898 if (context->packType == TYPE_PICTURE) {
899 BuildMsgOnError(context, packRes == SUCCESS, "PackToFile picture failed",
900 packRes == ERR_IMAGE_INVALID_PARAMETER ? IMAGE_BAD_PARAMETER : IMAGE_ENCODE_FAILED);
901 }
902 }
903 }
904
STATIC_EXEC_FUNC(PackToFile)905 STATIC_EXEC_FUNC(PackToFile)
906 {
907 auto context = static_cast<ImagePackerAsyncContext*>(data);
908 if (context->fd <= INVALID_FD) {
909 BuildMsgOnError(context, context->fd <= INVALID_FD, "ImagePacker invalid fd", ERR_IMAGE_INVALID_PARAMETER);
910 return;
911 }
912
913 auto startRes = context->rImagePacker->StartPacking(context->fd, context->packOption);
914 if (startRes != SUCCESS) {
915 context->status = ERROR;
916 BuildMsgOnError(context, startRes == SUCCESS, "Start packing failed", startRes);
917 return;
918 }
919 if (context->packType == TYPE_IMAGE_SOURCE) {
920 IMAGE_LOGD("ImagePacker set image source");
921 if (context->rImageSource == nullptr) {
922 BuildMsgOnError(context, context->rImageSource == nullptr,
923 "ImageSource is nullptr", ERR_IMAGE_INVALID_PARAMETER);
924 return;
925 }
926 context->rImagePacker->AddImage(*(context->rImageSource));
927 } else if (context->packType == TYPE_PIXEL_MAP) {
928 IMAGE_LOGD("ImagePacker set pixelmap");
929 if (context->rPixelMap == nullptr) {
930 BuildMsgOnError(context, context->rImageSource == nullptr,
931 "Pixelmap is nullptr", ERR_IMAGE_INVALID_PARAMETER);
932 return;
933 }
934 context->rImagePacker->AddImage(*(context->rPixelMap));
935 #if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
936 } else if (context->packType == TYPE_PICTURE) {
937 if (!SetPicture(context)) {
938 return;
939 }
940 #endif
941 }
942 FinalizePacking(context);
943 }
944
STATIC_COMPLETE_FUNC(PackToFile)945 STATIC_COMPLETE_FUNC(PackToFile)
946 {
947 napi_value result = nullptr;
948 napi_get_undefined(env, &result);
949 auto context = static_cast<ImagePackerAsyncContext*>(data);
950 CommonCallbackRoutine(env, context, result);
951 }
952
PackToFile(napi_env env,napi_callback_info info)953 napi_value ImagePackerNapi::PackToFile(napi_env env, napi_callback_info info)
954 {
955 ImageTrace imageTrace("ImagePackerNapi::PackToFile");
956 napi_status status;
957 napi_value result = nullptr;
958 size_t argc = ARGS_FOUR;
959 napi_value argv[ARGS_FOUR] = {0};
960 napi_value thisVar = nullptr;
961
962 napi_get_undefined(env, &result);
963
964 IMG_JS_ARGS(env, info, status, argc, argv, thisVar);
965 NAPI_ASSERT(env, IMG_IS_OK(status), "fail to napi_get_cb_info");
966
967 std::unique_ptr<ImagePackerAsyncContext> asyncContext = std::make_unique<ImagePackerAsyncContext>();
968 status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&asyncContext->constructor_));
969 NAPI_ASSERT(env, IMG_IS_READY(status, asyncContext->constructor_), "fail to unwrap constructor_");
970
971 asyncContext->rImagePacker = asyncContext->constructor_->nativeImgPck;
972 ParserPackToFileArguments(env, argv, argc, asyncContext.get());
973 if (asyncContext->callbackRef == nullptr) {
974 napi_create_promise(env, &(asyncContext->deferred), &result);
975 }
976
977 ImageNapiUtils::HicheckerReport();
978
979 if (IsImagePackerErrorOccur(asyncContext.get())) {
980 IMG_CREATE_CREATE_ASYNC_WORK(env, status, "PackingError",
981 [](napi_env env, void *data) {}, PackingErrorComplete, asyncContext, asyncContext->work);
982 } else {
983 IMG_CREATE_CREATE_ASYNC_WORK_WITH_QOS(env, status, "PackToFile",
984 PackToFileExec, PackToFileComplete, asyncContext, asyncContext->work, napi_qos_user_initiated);
985 }
986
987 IMG_NAPI_CHECK_RET_D(IMG_IS_OK(status),
988 nullptr, IMAGE_LOGE("fail to create async work"));
989 return result;
990 }
release()991 void ImagePackerNapi::release()
992 {
993 if (!isRelease) {
994 nativeImgPck = nullptr;
995 isRelease = true;
996 }
997 }
GetNative(ImagePackerNapi * napi)998 std::shared_ptr<ImagePacker> ImagePackerNapi::GetNative(ImagePackerNapi* napi)
999 {
1000 if (napi != nullptr) {
1001 return napi->nativeImgPck;
1002 }
1003 return nullptr;
1004 }
1005 } // namespace Media
1006 } // namespace OHOS
1007