From 6b0fcacf4fcaba7e9a25639e60e8df3a3437934e Mon Sep 17 00:00:00 2001 From: timlyeee Date: Tue, 16 Aug 2022 19:05:34 +0800 Subject: [PATCH 01/14] save image data --- cocos/native-binding/impl.ts | 1 + cocos/native-binding/index.ts | 6 + native/cocos/bindings/manual/jsb_global.cpp | 63 +++++ native/cocos/platform/Image.cpp | 251 ++++++++++++++++++++ native/cocos/platform/Image.h | 12 +- 5 files changed, 332 insertions(+), 1 deletion(-) diff --git a/cocos/native-binding/impl.ts b/cocos/native-binding/impl.ts index 1b95baf61d9..0b27e5d284e 100644 --- a/cocos/native-binding/impl.ts +++ b/cocos/native-binding/impl.ts @@ -140,4 +140,5 @@ export const native = { AssetsManager: globalJsb.AssetsManager, EventAssetsManager: globalJsb.EventAssetsManager, Manifest: globalJsb.Manifest, + saveImageData: globalJsb.saveImageData; }; diff --git a/cocos/native-binding/index.ts b/cocos/native-binding/index.ts index 1daf1590250..9f4b4a7f9b9 100644 --- a/cocos/native-binding/index.ts +++ b/cocos/native-binding/index.ts @@ -1316,4 +1316,10 @@ export declare namespace native { */ export function removeAllListeners(); } + /** + * @en Save the image to the path indicated. + * @zh 保存图片到指定路径。 + */ + export function saveImageData(data: Uint8Array, width: number, height: number, filePath: string, + callback: (ret: boolean, error: string) => void): void; } diff --git a/native/cocos/bindings/manual/jsb_global.cpp b/native/cocos/bindings/manual/jsb_global.cpp index 1de97edfa2e..acf8761bcf9 100644 --- a/native/cocos/bindings/manual/jsb_global.cpp +++ b/native/cocos/bindings/manual/jsb_global.cpp @@ -24,7 +24,9 @@ ****************************************************************************/ #include "jsb_global.h" +#include "Value.h" #include "application/ApplicationManager.h" +#include "base/Data.h" #include "base/DeferredReleasePool.h" #include "base/Scheduler.h" #include "base/ThreadPool.h" @@ -659,7 +661,67 @@ static bool js_loadImage(se::State &s) { // NOLINT return false; } SE_BIND_FUNC(js_loadImage) +//pixels(RGBA), width, height, fullFilePath(*.png/*.jpg) +static bool js_saveImageData(se::State& s) // NOLINT +{ + const auto& args = s.args(); + size_t argc = args.size(); + CC_UNUSED bool ok = true; + if (argc == 4 || argc == 5) { + auto *uint8ArrayObj = args[0].toObject(); + uint8_t *uint8ArrayData = nullptr; + size_t length = 0; + bool ok = uint8ArrayObj->getTypedArrayData(&uint8ArrayData, &length); + + uint32_t width; + uint32_t height; + ok &= sevalue_to_native(args[1], &width); + ok &= sevalue_to_native(args[2], &height); + + std::string filePath; + ok &= sevalue_to_native(args[3], &filePath); + SE_PRECONDITION2(ok, false, "js_saveImageData : Error processing arguments"); + + se::Value callbackVal = argc == 5 ? args[4] : se::Value::Null; + if (!callbackVal.isNull()) { + CC_ASSERT(callbackVal.isObject()); + CC_ASSERT(callbackVal.toObject()->isFunction()); + } + std::shared_ptr callbackPtr = std::make_shared(callbackVal); + gThreadPool->pushTask([=](int /*tid*/) { + auto* img = ccnew Image(); + img->initWithRawData(uint8ArrayData, length, width, height, 8); + // isToRGB = false, to keep alpha channel + bool isSuccess = img->saveToFile(filePath, false/*isToRGB*/); + //s.rval().setBoolean(saveRes); + + img->release(); + + CC_CURRENT_ENGINE()->getScheduler()->performFunctionInCocosThread([=]() { + se::AutoHandleScope hs; + se::ValueArray seArgs; + + se::Value visSuccess; + nativevalue_to_se(isSuccess, visSuccess); + se::HandleObject retObj(visSuccess.toObject()); + seArgs.push_back(se::Value(retObj)); + + se::Value error; + nativevalue_to_se(error, verror); + se::HandleObject retObj(isSuccess.toObject()); + seArgs.push_back(se::Value(retObj)); + + callbackPtr->toObject()->call(seArgs, nullptr); + delete img; + }); + }); + + } + SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 2); + return false; +} +SE_BIND_FUNC(js_saveImageData) static bool js_destroyImage(se::State &s) { // NOLINT const auto &args = s.args(); size_t argc = args.size(); @@ -1313,6 +1375,7 @@ bool jsb_register_global_variables(se::Object *global) { // NOLINT __jsbObj->defineFunction("dumpNativePtrToSeObjectMap", _SE(jsc_dumpNativePtrToSeObjectMap)); __jsbObj->defineFunction("loadImage", _SE(js_loadImage)); + __jsbObj->defineFunction("saveImageData", _SE(js_saveImageData)); __jsbObj->defineFunction("openURL", _SE(JSB_openURL)); __jsbObj->defineFunction("copyTextToClipboard", _SE(JSB_copyTextToClipboard)); __jsbObj->defineFunction("setPreferredFramesPerSecond", _SE(JSB_setPreferredFramesPerSecond)); diff --git a/native/cocos/platform/Image.cpp b/native/cocos/platform/Image.cpp index 31948993203..9cfbd16ef96 100644 --- a/native/cocos/platform/Image.cpp +++ b/native/cocos/platform/Image.cpp @@ -30,6 +30,7 @@ #include #include "base/Config.h" // CC_USE_JPEG, CC_USE_WEBP #include "base/std/container/string.h" +#include "gfx-base/GFXDef-common.h" #if CC_USE_JPEG #include "jpeg/jpeglib.h" @@ -944,4 +945,254 @@ bool Image::initWithRawData(const unsigned char *data, uint32_t /*dataLen*/, int return ret; } +#if (CC_PLATFORM != CC_PLATFORM_IOS) +bool Image::saveToFile(const std::string& filename, bool isToRGB) +{ + //only support for Image::PixelFormat::RGB888 or Image::PixelFormat::RGBA8888 uncompressed data + if (isCompressed() || (_renderFormat != gfx::Format::RGB8 && _renderFormat != gfx::Format::RGBA8)) + { + CC_LOG_DEBUG("saveToFile: Image: saveToFile is only support for gfx::Format::RGB8 or gfx::Format::RGBA8 uncompressed data for now"); + return false; + } + + std::string fileExtension = FileUtils::getInstance()->getFileExtension(filename); + + if (fileExtension == ".png") + { + return saveImageToPNG(filename, isToRGB); + } + if (fileExtension == ".jpg") + { + return saveImageToJPG(filename); + } + CC_LOG_DEBUG("saveToFile: Image: saveToFile no support file extension(only .png or .jpg) for file: %s", filename.c_str()); + return false; +} +#endif // (CC_TARGET_PLATFORM != CC_PLATFORM_IOS) + +bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) +{ + bool ret = false; + do + { + FILE *fp; + png_structp png_ptr; + png_infop info_ptr; + png_colorp palette; + png_bytep *row_pointers; + + fp = fopen(FileUtils::getInstance()->getSuitableFOpen(filePath).c_str(), "wb"); + CC_BREAK_IF(nullptr == fp); + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + bool hasAlpha = gfx::GFX_FORMAT_INFOS[static_cast(_renderFormat)].hasAlpha; + + if (nullptr == png_ptr) + { + fclose(fp); + break; + } + + info_ptr = png_create_info_struct(png_ptr); + if (nullptr == info_ptr) + { + fclose(fp); + png_destroy_write_struct(&png_ptr, nullptr); + break; + } + if (setjmp(png_jmpbuf(png_ptr))) + { + fclose(fp); + png_destroy_write_struct(&png_ptr, &info_ptr); + break; + } + png_init_io(png_ptr, fp); + if (!isToRGB && hasAlpha) + { + png_set_IHDR(png_ptr, info_ptr, _width, _height, 8, PNG_COLOR_TYPE_RGB_ALPHA, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + } + else + { + png_set_IHDR(png_ptr, info_ptr, _width, _height, 8, PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + } + + palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof (png_color)); + png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH); + + png_write_info(png_ptr, info_ptr); + + png_set_packing(png_ptr); + + row_pointers = (png_bytep *)malloc(_height * sizeof(png_bytep)); + if(row_pointers == nullptr) + { + fclose(fp); + png_destroy_write_struct(&png_ptr, &info_ptr); + break; + } + + if (!hasAlpha) + { + for (int i = 0; i < (int)_height; i++) + { + row_pointers[i] = (png_bytep)_data + i * _width * 3; + } + + png_write_image(png_ptr, row_pointers); + + free(row_pointers); + row_pointers = nullptr; + } + else + { + if (isToRGB) + { + unsigned char *tempData = static_cast(malloc(_width * _height * 3 * sizeof(unsigned char))); + if (nullptr == tempData) + { + fclose(fp); + png_destroy_write_struct(&png_ptr, &info_ptr); + + free(row_pointers); + row_pointers = nullptr; + break; + } + + for (int i = 0; i < _height; ++i) + { + for (int j = 0; j < _width; ++j) + { + tempData[(i * _width + j) * 3] = _data[(i * _width + j) * 4]; + tempData[(i * _width + j) * 3 + 1] = _data[(i * _width + j) * 4 + 1]; + tempData[(i * _width + j) * 3 + 2] = _data[(i * _width + j) * 4 + 2]; + } + } + + for (int i = 0; i < (int)_height; i++) + { + row_pointers[i] = (png_bytep)tempData + i * _width * 3; + } + + png_write_image(png_ptr, row_pointers); + + free(row_pointers); + row_pointers = nullptr; + + if (tempData != nullptr) + { + free(tempData); + } + } + else + { + for (int i = 0; i < (int)_height; i++) + { + row_pointers[i] = (png_bytep)_data + i * _width * 4; + } + + png_write_image(png_ptr, row_pointers); + + free(row_pointers); + row_pointers = nullptr; + } + } + + png_write_end(png_ptr, info_ptr); + + png_free(png_ptr, palette); + palette = nullptr; + + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(fp); + + ret = true; + } while (0); + return ret; +} + +bool Image::saveImageToJPG(const std::string& filePath) +{ + bool ret = false; + do + { + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + FILE * outfile; /* target file */ + JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ + int row_stride; /* physical row width in image buffer */ + + cinfo.err = jpeg_std_error(&jerr); + /* Now we can initialize the JPEG compression object. */ + jpeg_create_compress(&cinfo); + + CC_BREAK_IF((outfile = fopen(FileUtils::getInstance()->getSuitableFOpen(filePath).c_str(), "wb")) == nullptr); + + jpeg_stdio_dest(&cinfo, outfile); + + cinfo.image_width = _width; /* image width and height, in pixels */ + cinfo.image_height = _height; + cinfo.input_components = 3; /* # of color components per pixel */ + cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, 90, TRUE); + + jpeg_start_compress(&cinfo, TRUE); + + row_stride = _width * 3; /* JSAMPLEs per row in image_buffer */ + bool hasAlpha = gfx::GFX_FORMAT_INFOS[static_cast(_renderFormat)].hasAlpha; + + if (hasAlpha) + { + unsigned char *tempData = static_cast(malloc(_width * _height * 3 * sizeof(unsigned char))); + if (nullptr == tempData) + { + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + fclose(outfile); + break; + } + + for (int i = 0; i < _height; ++i) + { + for (int j = 0; j < _width; ++j) + + { + tempData[(i * _width + j) * 3] = _data[(i * _width + j) * 4]; + tempData[(i * _width + j) * 3 + 1] = _data[(i * _width + j) * 4 + 1]; + tempData[(i * _width + j) * 3 + 2] = _data[(i * _width + j) * 4 + 2]; + } + } + + while (cinfo.next_scanline < cinfo.image_height) + { + row_pointer[0] = & tempData[cinfo.next_scanline * row_stride]; + (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + + if (tempData != nullptr) + { + free(tempData); + } + } + else + { + while (cinfo.next_scanline < cinfo.image_height) { + row_pointer[0] = & _data[cinfo.next_scanline * row_stride]; + (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + } + + jpeg_finish_compress(&cinfo); + fclose(outfile); + jpeg_destroy_compress(&cinfo); + + ret = true; + } while (0); + return ret; +} + } // namespace cc diff --git a/native/cocos/platform/Image.h b/native/cocos/platform/Image.h index 497f5f6f558..8b0605e0e45 100644 --- a/native/cocos/platform/Image.h +++ b/native/cocos/platform/Image.h @@ -80,9 +80,16 @@ class Image : public RefCounted { inline int getWidth() const { return _width; } inline int getHeight() const { return _height; } inline ccstd::string getFilePath() const { return _filePath; } - inline bool isCompressed() const { return _isCompressed; } + /** + @brief Save Image data to the specified file, with specified format. + @param filename the file's absolute path, including file suffix. + @param isToRGB whether the image is saved as RGB format. + */ + bool saveToFile(const std::string &filename, bool isToRGB = true); + + protected: bool initWithJpgData(const unsigned char *data, uint32_t dataLen); bool initWithPngData(const unsigned char *data, uint32_t dataLen); @@ -96,6 +103,9 @@ class Image : public RefCounted { bool initWithETC2Data(const unsigned char *data, uint32_t dataLen); bool initWithASTCData(const unsigned char *data, uint32_t dataLen); + bool saveImageToPNG(const std::string& filePath, bool isToRGB = true); + bool saveImageToJPG(const std::string& filePath); + unsigned char *_data = nullptr; uint32_t _dataLen = 0; int _width = 0; From 8184d11d10108d63e81220aa1369c25be5204221 Mon Sep 17 00:00:00 2001 From: timlyeee Date: Wed, 17 Aug 2022 19:06:29 +0800 Subject: [PATCH 02/14] daily update --- cocos/native-binding/impl.ts | 2 +- cocos/native-binding/index.ts | 2 +- native/cocos/bindings/manual/jsb_global.cpp | 40 ++++++++++----------- native/cocos/platform/Image.cpp | 37 ++++++++++++------- 4 files changed, 45 insertions(+), 36 deletions(-) diff --git a/cocos/native-binding/impl.ts b/cocos/native-binding/impl.ts index 0b27e5d284e..ce31c8f26be 100644 --- a/cocos/native-binding/impl.ts +++ b/cocos/native-binding/impl.ts @@ -140,5 +140,5 @@ export const native = { AssetsManager: globalJsb.AssetsManager, EventAssetsManager: globalJsb.EventAssetsManager, Manifest: globalJsb.Manifest, - saveImageData: globalJsb.saveImageData; + saveImageData: globalJsb.saveImageData, }; diff --git a/cocos/native-binding/index.ts b/cocos/native-binding/index.ts index 9f4b4a7f9b9..9f668b6c075 100644 --- a/cocos/native-binding/index.ts +++ b/cocos/native-binding/index.ts @@ -1321,5 +1321,5 @@ export declare namespace native { * @zh 保存图片到指定路径。 */ export function saveImageData(data: Uint8Array, width: number, height: number, filePath: string, - callback: (ret: boolean, error: string) => void): void; + callback: (isSuccess: boolean) => void): void; } diff --git a/native/cocos/bindings/manual/jsb_global.cpp b/native/cocos/bindings/manual/jsb_global.cpp index acf8761bcf9..fa4353fe03f 100644 --- a/native/cocos/bindings/manual/jsb_global.cpp +++ b/native/cocos/bindings/manual/jsb_global.cpp @@ -24,6 +24,7 @@ ****************************************************************************/ #include "jsb_global.h" +#include #include "Value.h" #include "application/ApplicationManager.h" #include "base/Data.h" @@ -692,33 +693,30 @@ static bool js_saveImageData(se::State& s) // NOLINT auto* img = ccnew Image(); img->initWithRawData(uint8ArrayData, length, width, height, 8); // isToRGB = false, to keep alpha channel + bool isSuccess = img->saveToFile(filePath, false/*isToRGB*/); - //s.rval().setBoolean(saveRes); img->release(); - - CC_CURRENT_ENGINE()->getScheduler()->performFunctionInCocosThread([=]() { - se::AutoHandleScope hs; - se::ValueArray seArgs; - - se::Value visSuccess; - nativevalue_to_se(isSuccess, visSuccess); - se::HandleObject retObj(visSuccess.toObject()); - seArgs.push_back(se::Value(retObj)); - - se::Value error; - nativevalue_to_se(error, verror); - se::HandleObject retObj(isSuccess.toObject()); - seArgs.push_back(se::Value(retObj)); - - callbackPtr->toObject()->call(seArgs, nullptr); - delete img; - }); + if (!callbackPtr->isNull()) { + CC_CURRENT_ENGINE()->getScheduler()->performFunctionInCocosThread([=]() { + se::AutoHandleScope hs; + se::ValueArray seArgs; + + se::Value visSuccess; + nativevalue_to_se(isSuccess, visSuccess); + se::HandleObject retObj(visSuccess.toObject()); + + seArgs.push_back(se::Value(retObj)); + callbackPtr->toObject()->call(seArgs, nullptr); + delete img; + }); + } + }); - + return true; } - SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 2); + SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d or %d", (int)argc, 4, 5); return false; } SE_BIND_FUNC(js_saveImageData) diff --git a/native/cocos/platform/Image.cpp b/native/cocos/platform/Image.cpp index 9cfbd16ef96..f9e455c9d4e 100644 --- a/native/cocos/platform/Image.cpp +++ b/native/cocos/platform/Image.cpp @@ -978,8 +978,8 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) FILE *fp; png_structp png_ptr; png_infop info_ptr; - png_colorp palette; - png_bytep *row_pointers; + png_colorp palette{nullptr}; + png_bytep *row_pointers{nullptr}; fp = fopen(FileUtils::getInstance()->getSuitableFOpen(filePath).c_str(), "wb"); CC_BREAK_IF(nullptr == fp); @@ -996,16 +996,22 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) info_ptr = png_create_info_struct(png_ptr); if (nullptr == info_ptr) { - fclose(fp); png_destroy_write_struct(&png_ptr, nullptr); - break; - } - if (setjmp(png_jmpbuf(png_ptr))) - { fclose(fp); - png_destroy_write_struct(&png_ptr, &info_ptr); break; } + //if (setjmp(png_jmpbuf(png_ptr))) + //{ + // /*png_destroy_write_struct(&png_ptr, &info_ptr); + // fclose(fp); + // if (palette) { + // free(palette); + // } + // if (row_pointers) { + // free(row_pointers); + // }*/ + // break; + //} png_init_io(png_ptr, fp); if (!isToRGB && hasAlpha) { @@ -1025,7 +1031,7 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) png_set_packing(png_ptr); - row_pointers = (png_bytep *)malloc(_height * sizeof(png_bytep)); + row_pointers = (png_bytep *)png_malloc(png_ptr, _height * sizeof(png_bytep)); if(row_pointers == nullptr) { fclose(fp); @@ -1089,11 +1095,16 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) { for (int i = 0; i < (int)_height; i++) { - row_pointers[i] = (png_bytep)_data + i * _width * 4; + // row_pointers[i] = (png_bytep)(_data + i * _width) /*Bytes per pixel*/; + row_pointers[i] = (png_bytep)_data + i * _width * 4 /*Bytes per pixel*/; } - - png_write_image(png_ptr, row_pointers); - + try { + png_write_image(png_ptr, row_pointers); + //png_ptr->writ + } catch (std::exception& e){ + CC_LOG_DEBUG("exception, %s", e.what()); + } + free(row_pointers); row_pointers = nullptr; } From 2e00606d14e98212c940968b141086312b4deaa3 Mon Sep 17 00:00:00 2001 From: timlyeee Date: Fri, 19 Aug 2022 18:26:39 +0800 Subject: [PATCH 03/14] save image data --- native/cocos/bindings/manual/jsb_global.cpp | 48 ++++---- native/cocos/platform/Image.cpp | 130 +++++++------------- 2 files changed, 72 insertions(+), 106 deletions(-) diff --git a/native/cocos/bindings/manual/jsb_global.cpp b/native/cocos/bindings/manual/jsb_global.cpp index fa4353fe03f..f2138bf9394 100644 --- a/native/cocos/bindings/manual/jsb_global.cpp +++ b/native/cocos/bindings/manual/jsb_global.cpp @@ -670,6 +670,7 @@ static bool js_saveImageData(se::State& s) // NOLINT CC_UNUSED bool ok = true; if (argc == 4 || argc == 5) { auto *uint8ArrayObj = args[0].toObject(); + uint8ArrayObj->root(); uint8_t *uint8ArrayData = nullptr; size_t length = 0; bool ok = uint8ArrayObj->getTypedArrayData(&uint8ArrayData, &length); @@ -684,35 +685,38 @@ static bool js_saveImageData(se::State& s) // NOLINT SE_PRECONDITION2(ok, false, "js_saveImageData : Error processing arguments"); se::Value callbackVal = argc == 5 ? args[4] : se::Value::Null; + se::Object *callbackObj; if (!callbackVal.isNull()) { CC_ASSERT(callbackVal.isObject()); CC_ASSERT(callbackVal.toObject()->isFunction()); + callbackObj = callbackVal.toObject(); + s.thisObject()->attachObject(callbackObj); + callbackObj->root(); + callbackObj->incRef(); } - std::shared_ptr callbackPtr = std::make_shared(callbackVal); + //std::shared_ptr callbackPtr = std::make_shared(callbackVal); + gThreadPool->pushTask([=](int /*tid*/) { - auto* img = ccnew Image(); - img->initWithRawData(uint8ArrayData, length, width, height, 8); // isToRGB = false, to keep alpha channel - + auto *img = ccnew Image(); + img->initWithRawData(uint8ArrayData, length, width, height, 32 /*Unused*/); bool isSuccess = img->saveToFile(filePath, false/*isToRGB*/); - - img->release(); - - if (!callbackPtr->isNull()) { - CC_CURRENT_ENGINE()->getScheduler()->performFunctionInCocosThread([=]() { - se::AutoHandleScope hs; - se::ValueArray seArgs; - - se::Value visSuccess; - nativevalue_to_se(isSuccess, visSuccess); - se::HandleObject retObj(visSuccess.toObject()); - - seArgs.push_back(se::Value(retObj)); - callbackPtr->toObject()->call(seArgs, nullptr); - delete img; - }); - } - + CC_CURRENT_ENGINE()->getScheduler()->performFunctionInCocosThread([=]() { + se::AutoHandleScope hs; + se::ValueArray seArgs; + + se::Value visSuccess; + nativevalue_to_se(isSuccess, visSuccess); + + seArgs.push_back(visSuccess); + if (callbackObj) { + callbackObj->call(seArgs, nullptr); + callbackObj->unroot(); + callbackObj->decRef(); + } + delete img; + uint8ArrayObj->unroot(); + }); }); return true; } diff --git a/native/cocos/platform/Image.cpp b/native/cocos/platform/Image.cpp index f9e455c9d4e..8c77b74caf2 100644 --- a/native/cocos/platform/Image.cpp +++ b/native/cocos/platform/Image.cpp @@ -937,6 +937,10 @@ bool Image::initWithRawData(const unsigned char *data, uint32_t /*dataLen*/, int _dataLen = height * width * bytesPerComponent; _data = static_cast(malloc(_dataLen * sizeof(unsigned char))); CC_BREAK_IF(!_data); + // for (int i = 0; i < _dataLen; i+=4) { + // //memset(_data+i, 0xFF008080, 4); + // *reinterpret_cast(_data + i) = 0xFFff00FF; + // } memcpy(_data, data, _dataLen); ret = true; @@ -973,71 +977,42 @@ bool Image::saveToFile(const std::string& filename, bool isToRGB) bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) { bool ret = false; - do - { - FILE *fp; - png_structp png_ptr; - png_infop info_ptr; - png_colorp palette{nullptr}; - png_bytep *row_pointers{nullptr}; - - fp = fopen(FileUtils::getInstance()->getSuitableFOpen(filePath).c_str(), "wb"); - CC_BREAK_IF(nullptr == fp); - + + FILE *fp{nullptr}; + png_structp png_ptr{nullptr}; + png_infop info_ptr{nullptr}; + png_colorp palette{nullptr}; + png_bytep *row_pointers{nullptr}; + bool hasAlpha = gfx::GFX_FORMAT_INFOS[static_cast(_renderFormat)].hasAlpha; + do { + // Init png structure and png ptr png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); - bool hasAlpha = gfx::GFX_FORMAT_INFOS[static_cast(_renderFormat)].hasAlpha; - - if (nullptr == png_ptr) + CC_BREAK_IF(!png_ptr); + if (setjmp(png_jmpbuf(png_ptr))) { - fclose(fp); break; } - info_ptr = png_create_info_struct(png_ptr); - if (nullptr == info_ptr) - { - png_destroy_write_struct(&png_ptr, nullptr); - fclose(fp); - break; - } - //if (setjmp(png_jmpbuf(png_ptr))) - //{ - // /*png_destroy_write_struct(&png_ptr, &info_ptr); - // fclose(fp); - // if (palette) { - // free(palette); - // } - // if (row_pointers) { - // free(row_pointers); - // }*/ - // break; - //} + CC_BREAK_IF(!info_ptr); + // Start open file + fp = fopen(FileUtils::getInstance()->getSuitableFOpen(filePath).c_str(), "wb"); + CC_BREAK_IF(!fp); png_init_io(png_ptr, fp); - if (!isToRGB && hasAlpha) - { - png_set_IHDR(png_ptr, info_ptr, _width, _height, 8, PNG_COLOR_TYPE_RGB_ALPHA, - PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - } - else - { - png_set_IHDR(png_ptr, info_ptr, _width, _height, 8, PNG_COLOR_TYPE_RGB, - PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - } - + auto mask = (!isToRGB && hasAlpha) ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB; + png_set_IHDR(png_ptr, info_ptr, _width, _height, 8, mask, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof (png_color)); + CC_BREAK_IF(!palette); png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH); png_write_info(png_ptr, info_ptr); png_set_packing(png_ptr); - row_pointers = (png_bytep *)png_malloc(png_ptr, _height * sizeof(png_bytep)); - if(row_pointers == nullptr) - { - fclose(fp); - png_destroy_write_struct(&png_ptr, &info_ptr); - break; - } + //row_pointers = (png_bytep *)png_malloc(png_ptr, _height * sizeof(png_bytep)); + row_pointers = (png_bytep *)malloc(_height * sizeof(png_bytep)); + CC_BREAK_IF(!row_pointers); if (!hasAlpha) { @@ -1045,11 +1020,7 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) { row_pointers[i] = (png_bytep)_data + i * _width * 3; } - png_write_image(png_ptr, row_pointers); - - free(row_pointers); - row_pointers = nullptr; } else { @@ -1058,11 +1029,6 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) unsigned char *tempData = static_cast(malloc(_width * _height * 3 * sizeof(unsigned char))); if (nullptr == tempData) { - fclose(fp); - png_destroy_write_struct(&png_ptr, &info_ptr); - - free(row_pointers); - row_pointers = nullptr; break; } @@ -1080,16 +1046,10 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) { row_pointers[i] = (png_bytep)tempData + i * _width * 3; } - - png_write_image(png_ptr, row_pointers); - - free(row_pointers); - row_pointers = nullptr; - - if (tempData != nullptr) - { + if (tempData != nullptr) { free(tempData); } + png_write_image(png_ptr, row_pointers); } else { @@ -1098,29 +1058,31 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) // row_pointers[i] = (png_bytep)(_data + i * _width) /*Bytes per pixel*/; row_pointers[i] = (png_bytep)_data + i * _width * 4 /*Bytes per pixel*/; } - try { - png_write_image(png_ptr, row_pointers); - //png_ptr->writ - } catch (std::exception& e){ - CC_LOG_DEBUG("exception, %s", e.what()); - } - - free(row_pointers); - row_pointers = nullptr; + png_write_image(png_ptr, row_pointers); } } png_write_end(png_ptr, info_ptr); + ret = true; + } while (0); + /*Later free for all functions*/ + if (row_pointers) { + free(row_pointers); + row_pointers = nullptr; + } + if (palette) { png_free(png_ptr, palette); - palette = nullptr; - + } + if (info_ptr) { png_destroy_write_struct(&png_ptr, &info_ptr); - + } + if (fp) { fclose(fp); - - ret = true; - } while (0); + } + if (png_ptr) { + png_destroy_write_struct(&png_ptr, nullptr); + } return ret; } From b2b861527c97afa625a8395e8df930d64b633760 Mon Sep 17 00:00:00 2001 From: timlyeee Date: Mon, 22 Aug 2022 16:33:29 +0800 Subject: [PATCH 04/14] Fix ref count error --- native/cocos/bindings/manual/jsb_global.cpp | 9 ++++++--- native/cocos/platform/Image.cpp | 3 +-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/native/cocos/bindings/manual/jsb_global.cpp b/native/cocos/bindings/manual/jsb_global.cpp index f2138bf9394..18ce2090c1f 100644 --- a/native/cocos/bindings/manual/jsb_global.cpp +++ b/native/cocos/bindings/manual/jsb_global.cpp @@ -671,10 +671,12 @@ static bool js_saveImageData(se::State& s) // NOLINT if (argc == 4 || argc == 5) { auto *uint8ArrayObj = args[0].toObject(); uint8ArrayObj->root(); + uint8ArrayObj->incRef(); + CC_LOG_DEBUG("address %p rooted with ref count %d and root count %d", uint8ArrayObj, uint8ArrayObj->getRefCount(), uint8ArrayObj->_rootCount); uint8_t *uint8ArrayData = nullptr; size_t length = 0; - bool ok = uint8ArrayObj->getTypedArrayData(&uint8ArrayData, &length); - + + uint8ArrayObj->getTypedArrayData(&uint8ArrayData, &length); uint32_t width; uint32_t height; ok &= sevalue_to_native(args[1], &width); @@ -694,7 +696,6 @@ static bool js_saveImageData(se::State& s) // NOLINT callbackObj->root(); callbackObj->incRef(); } - //std::shared_ptr callbackPtr = std::make_shared(callbackVal); gThreadPool->pushTask([=](int /*tid*/) { // isToRGB = false, to keep alpha channel @@ -715,7 +716,9 @@ static bool js_saveImageData(se::State& s) // NOLINT callbackObj->decRef(); } delete img; + CC_LOG_DEBUG("address %p in unrooting with ref count %d and root count %d", uint8ArrayObj, uint8ArrayObj->getRefCount(), uint8ArrayObj->_rootCount); uint8ArrayObj->unroot(); + uint8ArrayObj->decRef(); }); }); return true; diff --git a/native/cocos/platform/Image.cpp b/native/cocos/platform/Image.cpp index 8c77b74caf2..1bf43efdebf 100644 --- a/native/cocos/platform/Image.cpp +++ b/native/cocos/platform/Image.cpp @@ -949,7 +949,6 @@ bool Image::initWithRawData(const unsigned char *data, uint32_t /*dataLen*/, int return ret; } -#if (CC_PLATFORM != CC_PLATFORM_IOS) bool Image::saveToFile(const std::string& filename, bool isToRGB) { //only support for Image::PixelFormat::RGB888 or Image::PixelFormat::RGBA8888 uncompressed data @@ -972,7 +971,7 @@ bool Image::saveToFile(const std::string& filename, bool isToRGB) CC_LOG_DEBUG("saveToFile: Image: saveToFile no support file extension(only .png or .jpg) for file: %s", filename.c_str()); return false; } -#endif // (CC_TARGET_PLATFORM != CC_PLATFORM_IOS) + bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) { From a17fbdf8b6fa2031431a48c387b183294f38ef01 Mon Sep 17 00:00:00 2001 From: timlyeee Date: Mon, 22 Aug 2022 17:03:17 +0800 Subject: [PATCH 05/14] update comment description --- cocos/native-binding/index.ts | 28 +++++++++++++++++++++ native/cocos/bindings/manual/jsb_global.cpp | 2 -- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/cocos/native-binding/index.ts b/cocos/native-binding/index.ts index 9f668b6c075..218a61af85e 100644 --- a/cocos/native-binding/index.ts +++ b/cocos/native-binding/index.ts @@ -1319,6 +1319,34 @@ export declare namespace native { /** * @en Save the image to the path indicated. * @zh 保存图片到指定路径。 + * @param data : @en the image data, should be raw data array with uint8 @zh 图片数据, 应为原始数据数组,uint8 格式。 + * @param path : @en the path to save @zh 保存路径 + * @param width : @en the width of the image @zh 图片宽度 + * @param height : @en the height of the image @zh 图片高度 + * @param filePath : @en the file path of the image @zh 图片文件路径 + * @param callback : @en the callback function @zh 回调函数 + * @example + * ```ts + let renderTexture = new RenderTexture(); + let renderWindowInfo = { + width: this._width, + height: this._height + }; + renderTexture.reset(renderWindowInfo); + cameras.forEach((camera: any) => { + camera.targetTexture = renderTexture; + }); + await this.waitForNextFrame(); + cameras.forEach((camera: any) => { + camera.targetTexture = null; + }); + let pixelData = renderTexture.readPixels(); + jsb.saveImageData(pixelData, path, width, height, filePath, (isSuccess) => { + if (isSuccess) { + console.log('save image success'); + } else { + console.log('save image failed'); + })); */ export function saveImageData(data: Uint8Array, width: number, height: number, filePath: string, callback: (isSuccess: boolean) => void): void; diff --git a/native/cocos/bindings/manual/jsb_global.cpp b/native/cocos/bindings/manual/jsb_global.cpp index 18ce2090c1f..2b4c20e33fb 100644 --- a/native/cocos/bindings/manual/jsb_global.cpp +++ b/native/cocos/bindings/manual/jsb_global.cpp @@ -672,7 +672,6 @@ static bool js_saveImageData(se::State& s) // NOLINT auto *uint8ArrayObj = args[0].toObject(); uint8ArrayObj->root(); uint8ArrayObj->incRef(); - CC_LOG_DEBUG("address %p rooted with ref count %d and root count %d", uint8ArrayObj, uint8ArrayObj->getRefCount(), uint8ArrayObj->_rootCount); uint8_t *uint8ArrayData = nullptr; size_t length = 0; @@ -716,7 +715,6 @@ static bool js_saveImageData(se::State& s) // NOLINT callbackObj->decRef(); } delete img; - CC_LOG_DEBUG("address %p in unrooting with ref count %d and root count %d", uint8ArrayObj, uint8ArrayObj->getRefCount(), uint8ArrayObj->_rootCount); uint8ArrayObj->unroot(); uint8ArrayObj->decRef(); }); From 450e51d9432574373dc3c06caca049a61eede328 Mon Sep 17 00:00:00 2001 From: timlyeee Date: Mon, 22 Aug 2022 18:35:07 +0800 Subject: [PATCH 06/14] remove unused comment, fix address sanilizer error --- native/cocos/platform/Image.cpp | 4 ---- native/cocos/platform/win32/modules/System.cpp | 6 +++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/native/cocos/platform/Image.cpp b/native/cocos/platform/Image.cpp index 1bf43efdebf..ec57e3262a5 100644 --- a/native/cocos/platform/Image.cpp +++ b/native/cocos/platform/Image.cpp @@ -937,10 +937,6 @@ bool Image::initWithRawData(const unsigned char *data, uint32_t /*dataLen*/, int _dataLen = height * width * bytesPerComponent; _data = static_cast(malloc(_dataLen * sizeof(unsigned char))); CC_BREAK_IF(!_data); - // for (int i = 0; i < _dataLen; i+=4) { - // //memset(_data+i, 0xFF008080, 4); - // *reinterpret_cast(_data + i) = 0xFFff00FF; - // } memcpy(_data, data, _dataLen); ret = true; diff --git a/native/cocos/platform/win32/modules/System.cpp b/native/cocos/platform/win32/modules/System.cpp index e82bb498889..7b6ac0febfa 100644 --- a/native/cocos/platform/win32/modules/System.cpp +++ b/native/cocos/platform/win32/modules/System.cpp @@ -112,10 +112,10 @@ ccstd::string System::getCurrentLanguageCode() const { const LCID locale_id = MAKELCID(lid, SORT_DEFAULT); int length = GetLocaleInfoA(locale_id, LOCALE_SISO639LANGNAME, nullptr, 0); - char *tempCode = ccnew char[length]; + char *tempCode = reinterpret_cast(malloc(length)); GetLocaleInfoA(locale_id, LOCALE_SISO639LANGNAME, tempCode, length); - ccstd::string code = tempCode; - delete tempCode; + ccstd::string code(tempCode); + free(tempCode); return code; } From 5f7c963c36150a6ee0a70f56400ac33ce738131f Mon Sep 17 00:00:00 2001 From: timlyeee Date: Tue, 23 Aug 2022 14:31:50 +0800 Subject: [PATCH 07/14] Fix lint --- native/cocos/bindings/manual/jsb_global.cpp | 16 ++-- native/cocos/platform/Image.cpp | 81 ++++++++++----------- 2 files changed, 47 insertions(+), 50 deletions(-) diff --git a/native/cocos/bindings/manual/jsb_global.cpp b/native/cocos/bindings/manual/jsb_global.cpp index 2b4c20e33fb..8f30ea390b9 100644 --- a/native/cocos/bindings/manual/jsb_global.cpp +++ b/native/cocos/bindings/manual/jsb_global.cpp @@ -667,14 +667,13 @@ static bool js_saveImageData(se::State& s) // NOLINT { const auto& args = s.args(); size_t argc = args.size(); - CC_UNUSED bool ok = true; + bool ok = true; if (argc == 4 || argc == 5) { auto *uint8ArrayObj = args[0].toObject(); + uint8_t *uint8ArrayData {nullptr}; + size_t length = 0; uint8ArrayObj->root(); uint8ArrayObj->incRef(); - uint8_t *uint8ArrayData = nullptr; - size_t length = 0; - uint8ArrayObj->getTypedArrayData(&uint8ArrayData, &length); uint32_t width; uint32_t height; @@ -684,14 +683,13 @@ static bool js_saveImageData(se::State& s) // NOLINT std::string filePath; ok &= sevalue_to_native(args[3], &filePath); SE_PRECONDITION2(ok, false, "js_saveImageData : Error processing arguments"); - + se::Value callbackVal = argc == 5 ? args[4] : se::Value::Null; - se::Object *callbackObj; + se::Object *callbackObj {nullptr}; if (!callbackVal.isNull()) { CC_ASSERT(callbackVal.isObject()); CC_ASSERT(callbackVal.toObject()->isFunction()); callbackObj = callbackVal.toObject(); - s.thisObject()->attachObject(callbackObj); callbackObj->root(); callbackObj->incRef(); } @@ -704,7 +702,7 @@ static bool js_saveImageData(se::State& s) // NOLINT CC_CURRENT_ENGINE()->getScheduler()->performFunctionInCocosThread([=]() { se::AutoHandleScope hs; se::ValueArray seArgs; - + se::Value visSuccess; nativevalue_to_se(isSuccess, visSuccess); @@ -714,9 +712,9 @@ static bool js_saveImageData(se::State& s) // NOLINT callbackObj->unroot(); callbackObj->decRef(); } - delete img; uint8ArrayObj->unroot(); uint8ArrayObj->decRef(); + delete img; }); }); return true; diff --git a/native/cocos/platform/Image.cpp b/native/cocos/platform/Image.cpp index ec57e3262a5..744b0e1ff9d 100644 --- a/native/cocos/platform/Image.cpp +++ b/native/cocos/platform/Image.cpp @@ -972,56 +972,56 @@ bool Image::saveToFile(const std::string& filename, bool isToRGB) bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) { bool ret = false; - + FILE *fp{nullptr}; - png_structp png_ptr{nullptr}; - png_infop info_ptr{nullptr}; + png_structp pngPtr{nullptr}; + png_infop infoPtr{nullptr}; png_colorp palette{nullptr}; - png_bytep *row_pointers{nullptr}; + png_bytep *rowPointers{nullptr}; bool hasAlpha = gfx::GFX_FORMAT_INFOS[static_cast(_renderFormat)].hasAlpha; do { // Init png structure and png ptr - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); - CC_BREAK_IF(!png_ptr); - if (setjmp(png_jmpbuf(png_ptr))) + pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + CC_BREAK_IF(!pngPtr); + if (setjmp(png_jmpbuf(pngPtr))) { break; } - info_ptr = png_create_info_struct(png_ptr); - CC_BREAK_IF(!info_ptr); + infoPtr = png_create_info_struct(pngPtr); + CC_BREAK_IF(!infoPtr); // Start open file fp = fopen(FileUtils::getInstance()->getSuitableFOpen(filePath).c_str(), "wb"); CC_BREAK_IF(!fp); - png_init_io(png_ptr, fp); + png_init_io(pngPtr, fp); auto mask = (!isToRGB && hasAlpha) ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB; - png_set_IHDR(png_ptr, info_ptr, _width, _height, 8, mask, + png_set_IHDR(pngPtr, infoPtr, _width, _height, 8, mask, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - - palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof (png_color)); - CC_BREAK_IF(!palette); - png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH); - png_write_info(png_ptr, info_ptr); + palette = static_cast(png_malloc(pngPtr, PNG_MAX_PALETTE_LENGTH * sizeof (png_color))); + CC_BREAK_IF(!palette); + png_set_PLTE(pngPtr, infoPtr, palette, PNG_MAX_PALETTE_LENGTH); + + png_write_info(pngPtr, infoPtr); - png_set_packing(png_ptr); + png_set_packing(pngPtr); //row_pointers = (png_bytep *)png_malloc(png_ptr, _height * sizeof(png_bytep)); - row_pointers = (png_bytep *)malloc(_height * sizeof(png_bytep)); - CC_BREAK_IF(!row_pointers); + rowPointers = static_cast(CC_MALLOC(_height * sizeof(png_bytep))); + CC_BREAK_IF(!rowPointers); if (!hasAlpha) { - for (int i = 0; i < (int)_height; i++) + for (int i = 0; i < _height; i++) { - row_pointers[i] = (png_bytep)_data + i * _width * 3; + rowPointers[i] = static_cast(_data) + i * _width * 3; } - png_write_image(png_ptr, row_pointers); + png_write_image(pngPtr, rowPointers); } else { if (isToRGB) { - unsigned char *tempData = static_cast(malloc(_width * _height * 3 * sizeof(unsigned char))); + auto *tempData = static_cast(CC_MALLOC(_width * _height * 3 * sizeof(unsigned char))); if (nullptr == tempData) { break; @@ -1037,46 +1037,45 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) } } - for (int i = 0; i < (int)_height; i++) + for (int i = 0; i < _height; i++) { - row_pointers[i] = (png_bytep)tempData + i * _width * 3; + rowPointers[i] = static_cast(tempData) + i * _width * 3; } if (tempData != nullptr) { free(tempData); } - png_write_image(png_ptr, row_pointers); + png_write_image(pngPtr, rowPointers); } else { - for (int i = 0; i < (int)_height; i++) + for (int i = 0; i < _height; i++) { - // row_pointers[i] = (png_bytep)(_data + i * _width) /*Bytes per pixel*/; - row_pointers[i] = (png_bytep)_data + i * _width * 4 /*Bytes per pixel*/; + rowPointers[i] = static_cast(_data) + i * _width * 4 /*Bytes per pixel*/; } - png_write_image(png_ptr, row_pointers); + png_write_image(pngPtr, rowPointers); } } - png_write_end(png_ptr, info_ptr); + png_write_end(pngPtr, infoPtr); ret = true; - } while (0); + } while (false); /*Later free for all functions*/ - if (row_pointers) { - free(row_pointers); - row_pointers = nullptr; + if (rowPointers) { + free(rowPointers); + rowPointers = nullptr; } if (palette) { - png_free(png_ptr, palette); + png_free(pngPtr, palette); } - if (info_ptr) { - png_destroy_write_struct(&png_ptr, &info_ptr); + if (infoPtr) { + png_destroy_write_struct(&pngPtr, &infoPtr); } if (fp) { fclose(fp); } - if (png_ptr) { - png_destroy_write_struct(&png_ptr, nullptr); + if (pngPtr) { + png_destroy_write_struct(&pngPtr, nullptr); } return ret; } @@ -1112,7 +1111,7 @@ bool Image::saveImageToJPG(const std::string& filePath) row_stride = _width * 3; /* JSAMPLEs per row in image_buffer */ bool hasAlpha = gfx::GFX_FORMAT_INFOS[static_cast(_renderFormat)].hasAlpha; - + if (hasAlpha) { unsigned char *tempData = static_cast(malloc(_width * _height * 3 * sizeof(unsigned char))); From 275f5d84dc850b3a58d345887c2a1efe5e02689b Mon Sep 17 00:00:00 2001 From: timlyeee Date: Tue, 23 Aug 2022 16:51:48 +0800 Subject: [PATCH 08/14] Make save image data a promise return --- cocos/native-binding/impl.ts | 14 ++++++++++++++ cocos/native-binding/index.ts | 2 +- native/cocos/bindings/manual/jsb_global.cpp | 8 ++++---- native/cocos/platform/Image.cpp | 3 +-- native/cocos/platform/win32/modules/System.cpp | 4 ++-- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/cocos/native-binding/impl.ts b/cocos/native-binding/impl.ts index ce31c8f26be..7d91a967ac0 100644 --- a/cocos/native-binding/impl.ts +++ b/cocos/native-binding/impl.ts @@ -124,6 +124,20 @@ if( NATIVE ){ globalJsb.__JsbBridgeWrapper = value; }, }); + const originSaveImageData = globalJsb.saveImageData; + globalJsb.saveImageData = (data: Uint8Array, width: number, height: number, filePath: string, + callback: (isSuccess: boolean) => void) => { + return new Promise((resolve, reject) => { + originSaveImageData(data, width, height, filePath, (isSuccess) => { + callback(isSuccess); + if (isSuccess) { + resolve(); + } else { + reject(); + } + }) + }); + } } export const native = { diff --git a/cocos/native-binding/index.ts b/cocos/native-binding/index.ts index 218a61af85e..b779f1b346d 100644 --- a/cocos/native-binding/index.ts +++ b/cocos/native-binding/index.ts @@ -1349,5 +1349,5 @@ export declare namespace native { })); */ export function saveImageData(data: Uint8Array, width: number, height: number, filePath: string, - callback: (isSuccess: boolean) => void): void; + callback: (isSuccess: boolean) => void): Promise; } diff --git a/native/cocos/bindings/manual/jsb_global.cpp b/native/cocos/bindings/manual/jsb_global.cpp index 8f30ea390b9..6b96951c658 100644 --- a/native/cocos/bindings/manual/jsb_global.cpp +++ b/native/cocos/bindings/manual/jsb_global.cpp @@ -667,7 +667,7 @@ static bool js_saveImageData(se::State& s) // NOLINT { const auto& args = s.args(); size_t argc = args.size(); - bool ok = true; + bool ok = true;// NOLINT(readability-identifier-length) if (argc == 4 || argc == 5) { auto *uint8ArrayObj = args[0].toObject(); uint8_t *uint8ArrayData {nullptr}; @@ -703,10 +703,10 @@ static bool js_saveImageData(se::State& s) // NOLINT se::AutoHandleScope hs; se::ValueArray seArgs; - se::Value visSuccess; - nativevalue_to_se(isSuccess, visSuccess); + se::Value isSuccessVal; + nativevalue_to_se(isSuccess, isSuccessVal); - seArgs.push_back(visSuccess); + seArgs.push_back(isSuccessVal); if (callbackObj) { callbackObj->call(seArgs, nullptr); callbackObj->unroot(); diff --git a/native/cocos/platform/Image.cpp b/native/cocos/platform/Image.cpp index 744b0e1ff9d..5eb66d8fb93 100644 --- a/native/cocos/platform/Image.cpp +++ b/native/cocos/platform/Image.cpp @@ -1083,8 +1083,7 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) bool Image::saveImageToJPG(const std::string& filePath) { bool ret = false; - do - { + do { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; FILE * outfile; /* target file */ diff --git a/native/cocos/platform/win32/modules/System.cpp b/native/cocos/platform/win32/modules/System.cpp index 7b6ac0febfa..ed9f8036383 100644 --- a/native/cocos/platform/win32/modules/System.cpp +++ b/native/cocos/platform/win32/modules/System.cpp @@ -112,10 +112,10 @@ ccstd::string System::getCurrentLanguageCode() const { const LCID locale_id = MAKELCID(lid, SORT_DEFAULT); int length = GetLocaleInfoA(locale_id, LOCALE_SISO639LANGNAME, nullptr, 0); - char *tempCode = reinterpret_cast(malloc(length)); + char *tempCode = reinterpret_cast(CC_MALLOC(length)); GetLocaleInfoA(locale_id, LOCALE_SISO639LANGNAME, tempCode, length); ccstd::string code(tempCode); - free(tempCode); + CC_FREE(tempCode); return code; } From 497290517aee16789fd74f941c9299fd2ed508d8 Mon Sep 17 00:00:00 2001 From: timlyeee Date: Tue, 23 Aug 2022 18:21:46 +0800 Subject: [PATCH 09/14] Optimize --- cocos/native-binding/impl.ts | 1 - cocos/native-binding/index.ts | 3 +-- native/cocos/platform/Image.cpp | 39 ++++++++++++++------------------- 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/cocos/native-binding/impl.ts b/cocos/native-binding/impl.ts index 7d91a967ac0..9bd37fcf569 100644 --- a/cocos/native-binding/impl.ts +++ b/cocos/native-binding/impl.ts @@ -129,7 +129,6 @@ if( NATIVE ){ callback: (isSuccess: boolean) => void) => { return new Promise((resolve, reject) => { originSaveImageData(data, width, height, filePath, (isSuccess) => { - callback(isSuccess); if (isSuccess) { resolve(); } else { diff --git a/cocos/native-binding/index.ts b/cocos/native-binding/index.ts index b779f1b346d..e162d4a357d 100644 --- a/cocos/native-binding/index.ts +++ b/cocos/native-binding/index.ts @@ -1348,6 +1348,5 @@ export declare namespace native { console.log('save image failed'); })); */ - export function saveImageData(data: Uint8Array, width: number, height: number, filePath: string, - callback: (isSuccess: boolean) => void): Promise; + export function saveImageData(data: Uint8Array, width: number, height: number, filePath: string): Promise; } diff --git a/native/cocos/platform/Image.cpp b/native/cocos/platform/Image.cpp index 5eb66d8fb93..1f4b9be3bb5 100644 --- a/native/cocos/platform/Image.cpp +++ b/native/cocos/platform/Image.cpp @@ -1026,15 +1026,12 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) { break; } - - for (int i = 0; i < _height; ++i) - { - for (int j = 0; j < _width; ++j) - { - tempData[(i * _width + j) * 3] = _data[(i * _width + j) * 4]; - tempData[(i * _width + j) * 3 + 1] = _data[(i * _width + j) * 4 + 1]; - tempData[(i * _width + j) * 3 + 2] = _data[(i * _width + j) * 4 + 2]; - } + auto *dst = tempData; + auto *src = _data; + for (int t = 0; t < _width * _height; t++) { + memcpy(dst, src, 3); + dst += 3; + src += 4; } for (int i = 0; i < _height; i++) @@ -1042,7 +1039,7 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) rowPointers[i] = static_cast(tempData) + i * _width * 3; } if (tempData != nullptr) { - free(tempData); + CC_FREE(tempData); } png_write_image(pngPtr, rowPointers); } @@ -1050,7 +1047,7 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) { for (int i = 0; i < _height; i++) { - rowPointers[i] = static_cast(_data) + i * _width * 4 /*Bytes per pixel*/; + rowPointers[i] = static_cast(_data) + i * _width * 4 /*Bytes per pixel*/; } png_write_image(pngPtr, rowPointers); } @@ -1113,7 +1110,7 @@ bool Image::saveImageToJPG(const std::string& filePath) if (hasAlpha) { - unsigned char *tempData = static_cast(malloc(_width * _height * 3 * sizeof(unsigned char))); + unsigned char *tempData = static_cast(CC_MALLOC(_width * _height * 3 * sizeof(unsigned char))); if (nullptr == tempData) { jpeg_finish_compress(&cinfo); @@ -1122,17 +1119,15 @@ bool Image::saveImageToJPG(const std::string& filePath) break; } - for (int i = 0; i < _height; ++i) - { - for (int j = 0; j < _width; ++j) - - { - tempData[(i * _width + j) * 3] = _data[(i * _width + j) * 4]; - tempData[(i * _width + j) * 3 + 1] = _data[(i * _width + j) * 4 + 1]; - tempData[(i * _width + j) * 3 + 2] = _data[(i * _width + j) * 4 + 2]; - } + auto *dst = tempData; + auto *src = _data; + for (int t = 0; t < _width * _height; t++) { + memcpy(dst, src, 3); + dst += 3; + src += 4; } + while (cinfo.next_scanline < cinfo.image_height) { row_pointer[0] = & tempData[cinfo.next_scanline * row_stride]; @@ -1141,7 +1136,7 @@ bool Image::saveImageToJPG(const std::string& filePath) if (tempData != nullptr) { - free(tempData); + CC_FREE(tempData); } } else From cfd76f8655aa73dc494d176a4547613555cd327b Mon Sep 17 00:00:00 2001 From: timlyeee Date: Tue, 23 Aug 2022 18:25:21 +0800 Subject: [PATCH 10/14] fix lint --- native/cocos/platform/Image.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/native/cocos/platform/Image.cpp b/native/cocos/platform/Image.cpp index 1f4b9be3bb5..5478591818c 100644 --- a/native/cocos/platform/Image.cpp +++ b/native/cocos/platform/Image.cpp @@ -1084,8 +1084,8 @@ bool Image::saveImageToJPG(const std::string& filePath) struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; FILE * outfile; /* target file */ - JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ - int row_stride; /* physical row width in image buffer */ + JSAMPROW rowPointer[1]; /* pointer to JSAMPLE row[s] */ + int rowStride; /* physical row width in image buffer */ cinfo.err = jpeg_std_error(&jerr); /* Now we can initialize the JPEG compression object. */ @@ -1105,12 +1105,12 @@ bool Image::saveImageToJPG(const std::string& filePath) jpeg_start_compress(&cinfo, TRUE); - row_stride = _width * 3; /* JSAMPLEs per row in image_buffer */ + rowStride = _width * 3; /* JSAMPLEs per row in image_buffer */ bool hasAlpha = gfx::GFX_FORMAT_INFOS[static_cast(_renderFormat)].hasAlpha; if (hasAlpha) { - unsigned char *tempData = static_cast(CC_MALLOC(_width * _height * 3 * sizeof(unsigned char))); + auto *tempData = static_cast(CC_MALLOC(_width * _height * 3 * sizeof(unsigned char))); if (nullptr == tempData) { jpeg_finish_compress(&cinfo); @@ -1130,8 +1130,8 @@ bool Image::saveImageToJPG(const std::string& filePath) while (cinfo.next_scanline < cinfo.image_height) { - row_pointer[0] = & tempData[cinfo.next_scanline * row_stride]; - (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + rowPointer[0] = & tempData[cinfo.next_scanline * rowStride]; + (void) jpeg_write_scanlines(&cinfo, rowPointer, 1); } if (tempData != nullptr) @@ -1142,8 +1142,8 @@ bool Image::saveImageToJPG(const std::string& filePath) else { while (cinfo.next_scanline < cinfo.image_height) { - row_pointer[0] = & _data[cinfo.next_scanline * row_stride]; - (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + rowPointer[0] = & _data[cinfo.next_scanline * rowStride]; + (void) jpeg_write_scanlines(&cinfo, rowPointer, 1); } } From cc46503e468ac1c5a21fcb634b3f6bd632639a38 Mon Sep 17 00:00:00 2001 From: timlyeee Date: Tue, 23 Aug 2022 19:05:32 +0800 Subject: [PATCH 11/14] fix lint and resolve comment --- native/cocos/bindings/manual/jsb_global.cpp | 2 -- native/cocos/platform/Image.cpp | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/native/cocos/bindings/manual/jsb_global.cpp b/native/cocos/bindings/manual/jsb_global.cpp index 6b96951c658..96ba71a5603 100644 --- a/native/cocos/bindings/manual/jsb_global.cpp +++ b/native/cocos/bindings/manual/jsb_global.cpp @@ -24,8 +24,6 @@ ****************************************************************************/ #include "jsb_global.h" -#include -#include "Value.h" #include "application/ApplicationManager.h" #include "base/Data.h" #include "base/DeferredReleasePool.h" diff --git a/native/cocos/platform/Image.cpp b/native/cocos/platform/Image.cpp index 5478591818c..2bd34baf4f4 100644 --- a/native/cocos/platform/Image.cpp +++ b/native/cocos/platform/Image.cpp @@ -1152,7 +1152,7 @@ bool Image::saveImageToJPG(const std::string& filePath) jpeg_destroy_compress(&cinfo); ret = true; - } while (0); + } while (false); return ret; } From bee43fc7eeb6a67549d94dba5dbc113b79f52a9f Mon Sep 17 00:00:00 2001 From: timlyeee Date: Wed, 24 Aug 2022 10:42:01 +0800 Subject: [PATCH 12/14] static cast size_t to uint32_t --- native/cocos/bindings/manual/jsb_global.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/native/cocos/bindings/manual/jsb_global.cpp b/native/cocos/bindings/manual/jsb_global.cpp index 96ba71a5603..414d1a2dcb8 100644 --- a/native/cocos/bindings/manual/jsb_global.cpp +++ b/native/cocos/bindings/manual/jsb_global.cpp @@ -695,7 +695,8 @@ static bool js_saveImageData(se::State& s) // NOLINT gThreadPool->pushTask([=](int /*tid*/) { // isToRGB = false, to keep alpha channel auto *img = ccnew Image(); - img->initWithRawData(uint8ArrayData, length, width, height, 32 /*Unused*/); + // A conversion from size_t to uint32_t might lose integer precision + img->initWithRawData(uint8ArrayData, static_cast(length), width, height, 32 /*Unused*/); bool isSuccess = img->saveToFile(filePath, false/*isToRGB*/); CC_CURRENT_ENGINE()->getScheduler()->performFunctionInCocosThread([=]() { se::AutoHandleScope hs; From 16d8271bd6400322f9c09f7d2ee86df499edc102 Mon Sep 17 00:00:00 2001 From: timlyeee Date: Wed, 24 Aug 2022 10:42:51 +0800 Subject: [PATCH 13/14] fix declaration --- cocos/native-binding/impl.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cocos/native-binding/impl.ts b/cocos/native-binding/impl.ts index 9bd37fcf569..290976ec130 100644 --- a/cocos/native-binding/impl.ts +++ b/cocos/native-binding/impl.ts @@ -125,8 +125,7 @@ if( NATIVE ){ }, }); const originSaveImageData = globalJsb.saveImageData; - globalJsb.saveImageData = (data: Uint8Array, width: number, height: number, filePath: string, - callback: (isSuccess: boolean) => void) => { + globalJsb.saveImageData = (data: Uint8Array, width: number, height: number, filePath: string) => { return new Promise((resolve, reject) => { originSaveImageData(data, width, height, filePath, (isSuccess) => { if (isSuccess) { From b7e65946e70d12a0ddde76beb8c7fac6524ad830 Mon Sep 17 00:00:00 2001 From: timlyeee Date: Wed, 24 Aug 2022 16:33:46 +0800 Subject: [PATCH 14/14] Code format --- native/cocos/bindings/manual/jsb_global.cpp | 3 +- native/cocos/platform/Image.cpp | 68 ++++++--------------- 2 files changed, 21 insertions(+), 50 deletions(-) diff --git a/native/cocos/bindings/manual/jsb_global.cpp b/native/cocos/bindings/manual/jsb_global.cpp index 414d1a2dcb8..8f57a533c96 100644 --- a/native/cocos/bindings/manual/jsb_global.cpp +++ b/native/cocos/bindings/manual/jsb_global.cpp @@ -661,8 +661,7 @@ static bool js_loadImage(se::State &s) { // NOLINT } SE_BIND_FUNC(js_loadImage) //pixels(RGBA), width, height, fullFilePath(*.png/*.jpg) -static bool js_saveImageData(se::State& s) // NOLINT -{ +static bool js_saveImageData(se::State& s) { // NOLINT const auto& args = s.args(); size_t argc = args.size(); bool ok = true;// NOLINT(readability-identifier-length) diff --git a/native/cocos/platform/Image.cpp b/native/cocos/platform/Image.cpp index 2bd34baf4f4..b33c9a5c36d 100644 --- a/native/cocos/platform/Image.cpp +++ b/native/cocos/platform/Image.cpp @@ -289,7 +289,6 @@ bool Image::initWithImageFile(const ccstd::string &path) { bool Image::initWithImageData(const unsigned char *data, uint32_t dataLen) { bool ret = false; - do { CC_BREAK_IF(!data || dataLen <= 0); @@ -945,23 +944,19 @@ bool Image::initWithRawData(const unsigned char *data, uint32_t /*dataLen*/, int return ret; } -bool Image::saveToFile(const std::string& filename, bool isToRGB) -{ +bool Image::saveToFile(const std::string& filename, bool isToRGB) { //only support for Image::PixelFormat::RGB888 or Image::PixelFormat::RGBA8888 uncompressed data - if (isCompressed() || (_renderFormat != gfx::Format::RGB8 && _renderFormat != gfx::Format::RGBA8)) - { + if (isCompressed() || (_renderFormat != gfx::Format::RGB8 && _renderFormat != gfx::Format::RGBA8)) { CC_LOG_DEBUG("saveToFile: Image: saveToFile is only support for gfx::Format::RGB8 or gfx::Format::RGBA8 uncompressed data for now"); return false; } std::string fileExtension = FileUtils::getInstance()->getFileExtension(filename); - if (fileExtension == ".png") - { + if (fileExtension == ".png") { return saveImageToPNG(filename, isToRGB); } - if (fileExtension == ".jpg") - { + if (fileExtension == ".jpg") { return saveImageToJPG(filename); } CC_LOG_DEBUG("saveToFile: Image: saveToFile no support file extension(only .png or .jpg) for file: %s", filename.c_str()); @@ -969,8 +964,7 @@ bool Image::saveToFile(const std::string& filename, bool isToRGB) } -bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) -{ +bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) { bool ret = false; FILE *fp{nullptr}; @@ -983,8 +977,7 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) // Init png structure and png ptr pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); CC_BREAK_IF(!pngPtr); - if (setjmp(png_jmpbuf(pngPtr))) - { + if (setjmp(png_jmpbuf(pngPtr))) { break; } infoPtr = png_create_info_struct(pngPtr); @@ -1009,21 +1002,15 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) rowPointers = static_cast(CC_MALLOC(_height * sizeof(png_bytep))); CC_BREAK_IF(!rowPointers); - if (!hasAlpha) - { - for (int i = 0; i < _height; i++) - { + if (!hasAlpha) { + for (int i = 0; i < _height; i++) { rowPointers[i] = static_cast(_data) + i * _width * 3; } png_write_image(pngPtr, rowPointers); - } - else - { - if (isToRGB) - { + } else { + if (isToRGB) { auto *tempData = static_cast(CC_MALLOC(_width * _height * 3 * sizeof(unsigned char))); - if (nullptr == tempData) - { + if (nullptr == tempData) { break; } auto *dst = tempData; @@ -1034,19 +1021,15 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) src += 4; } - for (int i = 0; i < _height; i++) - { + for (int i = 0; i < _height; i++) { rowPointers[i] = static_cast(tempData) + i * _width * 3; } if (tempData != nullptr) { CC_FREE(tempData); } png_write_image(pngPtr, rowPointers); - } - else - { - for (int i = 0; i < _height; i++) - { + } else { + for (int i = 0; i < _height; i++) { rowPointers[i] = static_cast(_data) + i * _width * 4 /*Bytes per pixel*/; } png_write_image(pngPtr, rowPointers); @@ -1077,8 +1060,7 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB) return ret; } -bool Image::saveImageToJPG(const std::string& filePath) -{ +bool Image::saveImageToJPG(const std::string& filePath) { bool ret = false; do { struct jpeg_compress_struct cinfo; @@ -1108,17 +1090,14 @@ bool Image::saveImageToJPG(const std::string& filePath) rowStride = _width * 3; /* JSAMPLEs per row in image_buffer */ bool hasAlpha = gfx::GFX_FORMAT_INFOS[static_cast(_renderFormat)].hasAlpha; - if (hasAlpha) - { + if (hasAlpha) { auto *tempData = static_cast(CC_MALLOC(_width * _height * 3 * sizeof(unsigned char))); - if (nullptr == tempData) - { + if (nullptr == tempData) { jpeg_finish_compress(&cinfo); jpeg_destroy_compress(&cinfo); fclose(outfile); break; } - auto *dst = tempData; auto *src = _data; for (int t = 0; t < _width * _height; t++) { @@ -1126,21 +1105,14 @@ bool Image::saveImageToJPG(const std::string& filePath) dst += 3; src += 4; } - - - while (cinfo.next_scanline < cinfo.image_height) - { + while (cinfo.next_scanline < cinfo.image_height) { rowPointer[0] = & tempData[cinfo.next_scanline * rowStride]; (void) jpeg_write_scanlines(&cinfo, rowPointer, 1); } - - if (tempData != nullptr) - { + if (tempData != nullptr) { CC_FREE(tempData); } - } - else - { + } else { while (cinfo.next_scanline < cinfo.image_height) { rowPointer[0] = & _data[cinfo.next_scanline * rowStride]; (void) jpeg_write_scanlines(&cinfo, rowPointer, 1);