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