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