diff --git a/app/build.gradle b/app/build.gradle index ffb698ef..54d6e114 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,7 +5,7 @@ plugins { // 10,00,000 major-minor-build def AppMajorVersion = 1 def AppMinorVersion = 1 -def AppBuildNumber = 1 +def AppBuildNumber = 5 def AppVersionName = AppMajorVersion + "." + AppMinorVersion + "." + AppBuildNumber def AppVersionCode = AppMajorVersion * 100000 + AppMinorVersion * 1000 + AppBuildNumber @@ -36,10 +36,10 @@ android { externalNativeBuild { cmake { // cppFlags '-std=c++17 -frtti -fexceptions -Wno-error=format-security' - cppFlags '-std=c++17 -fexceptions -Wno-error=format-security' + cppFlags '-std=c++17 -fexceptions -Wno-error=format-security -fopenmp' // cppFlags '-std=c++17 -Wno-error=format-security' // arguments "-DANDROID_STL=c++_shared" - arguments "-DNCNN_DISABLE_EXCEPTION=OFF", "-DTERM_CORE_ROOT=" + coreroot, "-DOpenCV_DIR=" + opencvsdk + "/sdk/native/jni", "-DHDRPLUS_ROOT=" + hdrplusroot, "-DNCNN_ROOT=" + ncnnroot + arguments "-DNCNN_DISABLE_EXCEPTION=OFF", "-DTERM_CORE_ROOT=" + coreroot, "-DOpenCV_DIR=" + opencvsdk + "/sdk/native/jni", "-DHDRPLUS_ROOT=" + hdrplusroot, "-DNCNN_ROOT=" + ncnnroot, "-DHALIDE_ROOT=" + halideroot abiFilters 'arm64-v8a', 'armeabi-v7a' // setAbiFilters(['arm64-v8a']) } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c4df279f..012a5f3a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -79,6 +79,13 @@ android:enabled="true" android:exported="false" android:grantUriPermissions="true" /> + + + + + + + >>>>>> 1b0d0f421fe8db524af9afc327dce98899a13e6d # include_directories(${OpenCV_DIR}/include) # add_library( lib_opencv SHARED IMPORTED ) @@ -120,10 +132,17 @@ find_package(OpenMP REQUIRED) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/hdrplus/include ) +# include_directories(${HDRPLUS_ROOT}/${ANDROID_ABI}/include) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/hdrplus2) +include_directories(hdrplus2/${ANDROID_ABI}) +include_directories(${HALIDE_ROOT}/${ANDROID_ABI}/include) + + SET(HDRPLUS_LIBS raw exiv2 exiv2-xmp expat lcms2 OpenMP::OpenMP_CXX) -SET(HDRPLUS_SOURCES +SET(HDRPLUS2_LIBS raw raw_r lcms2 tiff tiffxx jpeg hdrplus_pipeline) +SET(HDRPLUS_SOURCES hdrplus/src/align.cpp hdrplus/src/bayer_image.cpp hdrplus/src/burst.cpp @@ -131,9 +150,15 @@ SET(HDRPLUS_SOURCES hdrplus/src/hdrplus_pipeline.cpp hdrplus/src/merge.cpp hdrplus/src/params.cpp - ) +SET(HDRPLUS2_SOURCES + hdrplus2/src/HDRPlus.cpp + hdrplus2/src/Burst.cpp + hdrplus2/src/InputSource.cpp + hdrplus2/src/LibRaw2DngConverter.cpp + hdrplus2/${ANDROID_ABI}/hdrplus_pipeline.registration.cpp) + SET(YAMC_INC_DIR ${CMAKE_SOURCE_DIR}) # SET(TERM_CORE_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../xymp/Core) @@ -323,6 +348,25 @@ add_library( ${FREETYPE_SRC_FILES} ) + +if(USING_EXEC_HDRP) + message(WARNING "HDRP Compiled") +add_executable( libhdrp.so + ${HDRPLUS_SOURCES} + hdrplus/bin/hdrplus.cpp ) +target_link_libraries( libhdrp.so PUBLIC -fopenmp -static-openmp + android z + ${OpenCV_LIBS} + # ${LIBRAW_LIBRARY} + ${HDRPLUS_LIBS} + ) +else(USING_EXEC_HDRP) + +endif() + +SET(HDRPLUS_SOURCES_EMBED ${HDRPLUS2_SOURCES} ) +SET(HDRPLUS_LIBS_EMBED ${HDRPLUS2_LIBS} ) + add_library( # Sets the name of the library. microphoto @@ -350,7 +394,7 @@ add_library( # Sets the name of the library. # camera2/OpenCVFont.cpp - ${HDRPLUS_SOURCES} + ${HDRPLUS_SOURCES_EMBED} ${CAMERA2_SOURCES} ${IMG_UTILS_SRCS} @@ -426,7 +470,7 @@ target_link_libraries( # Specifies the target library. android camera2ndk mediandk z - ncnn ${OpenCV_LIBS} sqlite3 ${HDRPLUS_LIBS} + ncnn ${OpenCV_LIBS} sqlite3 ${HDRPLUS_LIBS_EMBED} ) diff --git a/app/src/main/cpp/GPIOControl.cpp b/app/src/main/cpp/GPIOControl.cpp index 38768843..0203306f 100644 --- a/app/src/main/cpp/GPIOControl.cpp +++ b/app/src/main/cpp/GPIOControl.cpp @@ -31,20 +31,78 @@ typedef struct char str[MAX_STRING_LEN]; }IOT_PARAM; + +std::mutex GpioControl::m_locker; +std::vector> GpioControl::m_references; + void GpioControl::setInt(int cmd, int value) { - int fd = open(GPIO_NODE_MP, O_RDONLY); - IOT_PARAM param; - param.cmd = cmd; - param.value = value; - // LOGE("set_int fd=%d,cmd=%d,value=%d\r\n",fd, cmd, value); - if( fd > 0 ) + int fd = -1; + IOT_PARAM param = { cmd, value, 0 }; + // param.cmd = cmd; + // param.value = value; + int res = 0; + + uint32_t references = (value != 0) ? 1 : 0; + std::vector >::iterator it; + + if (value) { - int res = ioctl(fd, IOT_PARAM_WRITE, ¶m); - // LOGE("set_int22 cmd=%d,value=%d,result=%d\r\n",param.cmd, param.value, param.result); - close(fd); + m_locker.lock(); + fd = open(GPIO_NODE_MP, O_RDONLY); + if( fd > 0 ) + { + res = ioctl(fd, IOT_PARAM_WRITE, ¶m); +#ifdef _DEBUG + ALOGI("setInt cmd=%d,value=%d,result=%d\r\n",param.cmd, param.value, param.result); +#endif + close(fd); + // check res??? + for (it = m_references.begin(); it != m_references.end(); ++it) + { + if (it->first == cmd) + { + it->second++; + references = it->second; + break; + } + } + if (it == m_references.end()) + { + m_references.push_back(std::pair(cmd, references)); + } + } + m_locker.unlock(); + } + else + { + m_locker.lock(); + for (it = m_references.begin(); it != m_references.end(); ++it) + { + if (it->first == cmd) + { + if (it->second > 0) + { + it->second--; + } + references = it->second; + break; + } + } + + if (references == 0) + { + fd = open(GPIO_NODE_MP, O_RDONLY); + if (fd > 0) { + res = ioctl(fd, IOT_PARAM_WRITE, ¶m); +#ifdef _DEBUG + ALOGI("setInt cmd=%d,value=%d,result=%d\r\n",param.cmd, param.value, param.result); +#endif + close(fd); + } + } + m_locker.unlock(); } - return; } int GpioControl::getInt(int cmd) @@ -132,6 +190,7 @@ std::string GpioControl::getString(int cmd) return ""; } +<<<<<<< HEAD #ifdef USING_N938 #if 0 @@ -193,3 +252,5 @@ bool GpioControl::OpenSensors() } #endif +======= +>>>>>>> 1b0d0f421fe8db524af9afc327dce98899a13e6d diff --git a/app/src/main/cpp/GPIOControl.h b/app/src/main/cpp/GPIOControl.h index 908a8947..8f92e4c9 100644 --- a/app/src/main/cpp/GPIOControl.h +++ b/app/src/main/cpp/GPIOControl.h @@ -8,6 +8,15 @@ #include #include #include +#include +#include +#include + + + +#ifndef USING_N938 + +#ifndef USING_PLZ // MicroPhoto #define CMD_GET_LIGHT_ADC 101 #define CMD_SET_LIGHT_ADC 102 @@ -33,95 +42,89 @@ #define CMD_SET_SPI_MAXSPEEDHZ 125 #define CMD_SET_PWM_BEE_STATE 126 #define CMD_SET_ALM_MODE 128 -#define CMD_SET_SPI_POWER 129 +#define CMD_SET_SYSTEM_RESET 202 #define CMD_SET_485_EN_STATE 131 -#define CMD_SET_CAM_3V3_EN_STATE 132 #define CMD_SET_12V_EN_STATE 133 -#define CMD_SET_SYSTEM_RESET 202 - - -#ifdef USING_N938 - - -#define CMD_SET_485_EN_STATE 131 -#define CMD_SET_CAM_3V3_EN_STATE 132 -#define CMD_SET_12V_EN_STATE 133 -#define CMD_SET_485_STATE 121 -#define CMD_SET_SPI_MODE 123 -#define CMD_SET_SPI_BITS_PER_WORD 124 -#define CMD_SET_SPI_MAXSPEEDHZ 125 -#define CMD_SET_SPI_POWER 129 -#define CMD_SET_WTH_POWER 490 -#define CMD_SET_PULL_POWER 491 -#define CMD_SET_ANGLE_POWER 492 -#define CMD_SET_OTHER_POWER 493 -#define CMD_SET_PIC1_POWER 494 -#define CMD_SET_GPIO157_POWER 510 -#define CMD_SET_GPIO5_POWER 511 -#define CMD_SET_PWM_BEE_STATE 126 -#define CMD_SET_ALM_MODE 128 -#define CMD_SET_485_en0 301 -#define CMD_SET_485_en1 302 -#define CMD_SET_485_en2 303 -#define CMD_SET_485_en3 304 -#define CMD_SET_485_en4 305 -#define CMD_SET_OTG_STATE 107 -#define CMD_GET_OTG_STATE 108 - -#if 0 - -#define CMD_485_0_DE 156 // 485_0 DE信号 -#define CMD_485_0_PWR_EN 157 // 485_0 电源使能 -#define CMD_485_0_1_DE_EN 171 // 485_0&1DE电平转换芯片使能信号 -#define CMD_485_1_DE 172 // - -#define CMD_SET_CAM_3V3_EN_STATE 72 // 整板3V3上电使能 -#define CMD_3V3_SWITCH_EN 45 // 整板485_3V3信号电平转换电源使能 - -#define CMD_UART0_EN 73 // 预留UART0电平转换芯片使能 -#define CMD_485_1_PWR_EN 5 // 485_1 电源使能 - -#define CMD_485_3_DE 6 // 485_3 DE信号 -#define CMD_485_2_DE 7 // 485_2 DE信号 -#define CMD_485_4_DE 13 // 485_4 DE信号 -#define CMD_NETWORK_PWR_EN 94 // 100M网络电源使能 - -#define CMD_485_2_PWR_EN 92 // 485_2 电源使能 -#define CMD_485_3_PWR_EN 91 // 485_3 电源使能 -#define CMD_485_4_PWR_EN 90 // 485_4 电源使能 - -#define CMD_SEC_EN 27 // 加密芯片上电使能 - -#define CMD_485_2_3_DE_EN 26 // 485_2&3 DE电平转换芯片使能信号 - -#define CMD_5V_PWR_EN 14 // 整板5V0上电使能 -#define CMD_SD_CARD_DECT 15 // SD CARD DECT -#define CMD_PIC1_EN 16 - -#define CMD_OTHER_EN 21 -#define CMD_ANGLE_EN 22 -#define CMD_PULL_EN 23 -#define CMD_WEATHER_EN 24 - -#define CMD_LED_CTRL 46 -#define CMD_BD_EN 47 -#define CMD_ADC_EN 44 - -#define CMD_SPI_PWR_EN 43 // SPI转串口电源使能 - +#if 1 +#define CMD_SET_SPI_POWER 129 +#define CMD_SET_CAM_3V3_EN_STATE 132 #endif -#endif // USING_N938 +#else // defined(USING_PLZ) + +#define CMD_SET_485_ENABLE 512 +#define CMD_SET_3V3_PWR_ENABLE 516 +#define CMD_SET_5V_PWR_ENABLE 517 +#define CMD_SET_SENSOR_ENABLE 504 +#define CMD_SET_SENSOR_PWR_ENABLE 505 +#define CMD_SET_SENSOR2_ENABLE 506 +#define CMD_SET_SENSOR4_ENABLE 510 +#define CMD_SET_SENSOR1_PWR_ENABLE 513 +#define CMD_SET_SENSOR2_PWR_ENABLE 514 +#define CMD_SET_SENSOR3_PWR_ENABLE 509 +#define CMD_SET_SENSOR4_PWR_ENABLE 525 +#define CMD_SET_PHOTO_IN 520 +#define CMD_SET_PHOTO_OUT 515 + +#define CMD_SET_ADC_ENABLE 500 +#define CMD_SET_MIPI_SWITCH 501 +#define CMD_SET_CAM_RSTN1 502 +#define CMD_SET_CAM_RSTN0 503 +#define CMD_SET_SD_DECT 507 +#define CMD_SET_PTZ_PWR_ENABLE 508 +#define CMD_SET_RTC_ENABLE 511 +#define CMD_SET_100M_ENABLE 518 +#define CMD_SET_100M_SWITCH_PWR_ENABLE 519 +#define CMD_SET_AM_POWER_ENABLE 521 +#define CMD_SET_NRSEC_POWER_ENABLE 522 +#define CMD_SET_AMP_ENABLE 523 +#define CMD_SET_LIGHT1_RESISTOR_ENABLE 524 +#define CMD_SET_100M_RESET 526 + +#endif // USING_PLZ + +#else // defined(USING_N938) +#define CMD_SET_SYSTEM_RESET 202 +#define CMD_SET_485_EN1 302 +#define CMD_SET_CAM_3V3_EN_STATE 360 +#define CMD_SET_UART0_EN 361 +#define CMD_SET_485_EN0 301 +#define CMD_SET_NETWORK_POWER_EN 362 +#define CMD_SET_485_EN3 304 +#define CMD_SET_485_EN2 303 +#define CMD_SET_SPI_POWER 129 +#define CMD_SET_5V_EN 363 +#define CMD_SDCARD_DETECT_EN 364 +#define CMD_SET_PIC1_POWER 494 +#define CMD_SET_OTHER_POWER 493 +#define CMD_SET_ANGLE_POWER 492 +#define CMD_SET_PULL_POWER 491 +#define CMD_SET_WTH_POWER 490 +#define CMD_SET_485_EN4 305 +#define CMD_LED_CTRL 365 +#define CMD_BD_EN 366 +#define CMD_ADC_EN 367 +#define CMD_SPI2SERIAL_POWER_EN 368 +#define CMD_RS485_3V3_EN 369 + +#endif // USING_N938 + + +<<<<<<< HEAD #ifndef USING_N938 #define GPIO_NODE_N938 "/sys/devices/platform/1000b000.pinctrl/mt_gpio" #else +======= +>>>>>>> 1b0d0f421fe8db524af9afc327dce98899a13e6d #define GPIO_NODE_MP "/dev/mtkgpioctrl" -#endif // USING_N938 - class GpioControl { +private: + static std::mutex m_locker; + static std::vector> m_references; + public: static void setInt(int cmd, int value); @@ -133,12 +136,18 @@ public: static void setOtgState(bool on) { +#ifndef USING_N938 setInt(CMD_SET_OTG_STATE, on ? 1 : 0); +#endif } static bool getOtgState() { +#ifndef USING_N938 return getInt(CMD_SET_OTG_STATE) != 0; +#else + return false; +#endif } static void setCam3V3Enable(bool enabled) @@ -157,82 +166,142 @@ public: static void setLightAdc(int i) { +#ifndef USING_N938 setInt(CMD_SET_LIGHT_ADC, i); +#endif } static int getLightAdc() { +#ifndef USING_N938 return getInt(CMD_GET_LIGHT_ADC); +#else + return -1; +#endif } static int getChargingVoltage() { +#ifndef USING_N938 return getInt(CMD_GET_CHARGING_VOL_STATE); +#else + return -1; +#endif } static int getChargingShuntVoltage() { +#ifndef USING_N938 return getInt(CMD_GET_CHARGING_SHUNT_VOLTAGE_STATE); +#else + return -1; +#endif } static int getChargingBusVoltage() { +#ifndef USING_N938 return getInt(CMD_GET_CHARGING_BUS_VOLTAGE_STATE); +#else + return -1; +#endif } static int getChargingPower() { +#ifndef USING_N938 return getInt(CMD_GET_CHARGING_POWER_STATE); +#else + return -1; +#endif } static int getChargingCurrent() { +#ifndef USING_N938 return getInt(CMD_GET_CHARGING_CURRENT_STATE); +#else + return -1; +#endif } static int getBatteryVoltage() { +#ifndef USING_N938 return getInt(CMD_GET_BAT_VOL_STATE); +#else + return -1; +#endif } static int getBatteryShuntVoltage() { +#ifndef USING_N938 return getInt(CMD_GET_BAT_SHUNT_VOLTAGE_STATE); +#else + return -1; +#endif } static int getBatteryBusVoltage() { +#ifndef USING_N938 return getInt(CMD_GET_BAT_BUS_VOLTAGE_STATE); +#else + return -1; +#endif } static int getBatteryPower() { +#ifndef USING_N938 return getInt(CMD_GET_BAT_POWER_STATE); +#else + return -1; +#endif } static int getBatteryCurrent() { +#ifndef USING_N938 return getInt(CMD_GET_BAT_CURRENT_STATE); +#else + return -1; +#endif } static void set485WriteMode() { +#if 0 setInt(CMD_SET_485_STATE, 1); +#endif } static void set485ReadMode() { +#if 0 setInt(CMD_SET_485_STATE, 0); +#endif } static void setSpiMode(int i) { +#ifndef USING_N938 setInt(CMD_SET_SPI_MODE, i); +#endif } static void setSpiBitsPerWord(int i) { +#ifndef USING_N938 setInt(CMD_SET_SPI_BITS_PER_WORD, i); +#endif } static void setSpiMaxSpeedHz(long j) { +#ifndef USING_N938 setLong(CMD_SET_SPI_MAXSPEEDHZ, j); +#endif } static void setBeeOn(bool z) { +#ifndef USING_N938 setInt(CMD_SET_PWM_BEE_STATE, z ? 1 : 0); +#endif } static void setJidianqiState(bool z) { +#ifndef USING_N938 setInt(CMD_SET_ALM_MODE, z ? 1 : 0); +#endif } static void setSpiPower(bool on) { @@ -244,19 +313,17 @@ public: } static void setRS485Enable(bool z) { +#ifndef USING_N938 setInt(CMD_SET_485_EN_STATE, z ? 1 : 0); +#endif } static void set12VEnable(bool z) { +#ifndef USING_N938 setInt(CMD_SET_12V_EN_STATE, z ? 1 : 0); - } - -#ifdef USING_N938 - static bool SetN938Cmd(int cmd, int val); - static bool OpenSensors(); - static bool CloseSensors(); #endif + } }; diff --git a/app/src/main/cpp/MicroPhoto.cpp b/app/src/main/cpp/MicroPhoto.cpp index 3d5a67b6..e147d28d 100644 --- a/app/src/main/cpp/MicroPhoto.cpp +++ b/app/src/main/cpp/MicroPhoto.cpp @@ -195,7 +195,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) google_breakpad::ExceptionHandler eh(descriptor, NULL, DumpCallback, NULL, true, -1); #endif - +#if 0 { struct sigaction sig_action = {}; sig_action.sa_sigaction = posix_signal_handler; @@ -223,6 +223,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) // } env->DeleteLocalRef(clazz); +#endif return result; } @@ -280,7 +281,7 @@ Java_com_xypower_mpapp_MicroPhotoService_init( jobject pThis, jstring appPath, jstring ip, jint port, jstring cmdid, jint protocol, jint networkProtocol, jint encryptData, jlong netHandle, jint signalLevel, - jint versionCode, jlong buildTime, jstring simcard, jstring tfCardPath) { + jint versionCode, jlong buildTime, jstring simcard, jstring tfCardPath, jstring nativeLibraryDir) { /* google_breakpad::MinidumpDescriptor descriptor("."); @@ -310,6 +311,7 @@ Java_com_xypower_mpapp_MicroPhotoService_init( const char *cmdidStr = cmdid == NULL ? NULL : env->GetStringUTFChars(cmdid, 0); const char *simcardStr = simcard == NULL ? NULL : env->GetStringUTFChars(simcard, 0); const char *tfCardPathStr = tfCardPath == NULL ? NULL : env->GetStringUTFChars(tfCardPath, 0); + const char *nativeLibraryDirStr = nativeLibraryDir == NULL ? NULL : env->GetStringUTFChars(nativeLibraryDir, 0); JavaVM* vm = NULL; jint ret = env->GetJavaVM(&vm); @@ -320,7 +322,7 @@ Java_com_xypower_mpapp_MicroPhotoService_init( CTerminal* pTerminal = NewTerminal(protocol); - CPhoneDevice* device = new CPhoneDevice(vm, pThis, MakeString(appPathStr), NETID_UNSET, versionCode); + CPhoneDevice* device = new CPhoneDevice(vm, pThis, MakeString(appPathStr), NETID_UNSET, versionCode, MakeString(nativeLibraryDirStr)); device->SetListener(pTerminal); device->UpdateSignalLevel(signalLevel); device->SetBuildTime(buildTime / 1000); @@ -331,14 +333,20 @@ Java_com_xypower_mpapp_MicroPhotoService_init( // pTerminal->SetPacketSize(1 * 1024); // 1K #if defined(USING_NRSEC) && !defined(USING_NRSEC_VPN) pTerminal->InitEncryptionInfo(simcardStr, "/dev/spidev0.0", ""); +#endif +#ifdef _DEBUG + ALOGD("Call Startup"); #endif bool res = pTerminal->Startup(device); - +#ifdef _DEBUG + ALOGD("Finish Startup"); +#endif if (appPathStr != NULL) env->ReleaseStringUTFChars(appPath, appPathStr); if (ipStr != NULL) env->ReleaseStringUTFChars(ip, ipStr); if (cmdidStr != NULL) env->ReleaseStringUTFChars(cmdid, cmdidStr); if (simcardStr != NULL) env->ReleaseStringUTFChars(simcard, simcardStr); if (tfCardPathStr != NULL) env->ReleaseStringUTFChars(tfCardPath, tfCardPathStr); + if (nativeLibraryDirStr != NULL) env->ReleaseStringUTFChars(nativeLibraryDir, nativeLibraryDirStr); if (!res) { @@ -392,7 +400,7 @@ Java_com_xypower_mpapp_MicroPhotoService_takePhoto( CTerminal::LoadChannelConfig(channel, configFilePathStr, cfg); CTerminal::ConvertChannelConfigToPhotoInfo(cfg, photoOrVideo != JNI_FALSE, photoInfo); - CPhoneDevice* device = new CPhoneDevice(vm, NULL, "", NETID_UNSET, 0); + CPhoneDevice* device = new CPhoneDevice(vm, NULL, "", NETID_UNSET, 0, std::string("")); // device->SetListener(pTerminal); if (photoInfo.usbCamera) @@ -885,9 +893,11 @@ Java_com_xypower_mpapp_MicroPhotoService_burstCaptureFinished( return; } +#if 0 const char* pathsStr = env->GetStringUTFChars(pathsJoinedByTab, 0); ((CPhoneDevice *)dev)->ProcessRawCapture(result != JNI_FALSE, numberOfCaptures, MakeString(pathsStr), frontCamera != JNI_FALSE, rotation, photoId); env->ReleaseStringUTFChars(pathsJoinedByTab, pathsStr); +#endif } } @@ -940,7 +950,7 @@ Java_com_xypower_mpapp_MicroPhotoService_reloadConfigs( extern "C" JNIEXPORT jboolean JNICALL Java_com_xypower_mpapp_MicroPhotoService_sendExternalPhoto( - JNIEnv* env, jclass cls, jlong handler, jstring path) { + JNIEnv* env, jclass cls, jlong handler, jstring path, jlong photoInfo) { CTerminal* pTerminal = reinterpret_cast(handler); if (pTerminal == NULL) @@ -953,10 +963,25 @@ Java_com_xypower_mpapp_MicroPhotoService_sendExternalPhoto( return JNI_FALSE; } - const char *pathStr = env->GetStringUTFChars(path, 0); + IDevice::PHOTO_INFO* pPhotoInfo = photoInfo == 0 ? NULL : reinterpret_cast(photoInfo); + + const char *pathStr = NULL; + + if (path != NULL) + { + pathStr = env->GetStringUTFChars(path, 0); + } + bool res = pTerminal->SendExternalPhoto(pathStr); - env->ReleaseStringUTFChars(path, pathStr); + if (pathStr != NULL) + { + env->ReleaseStringUTFChars(path, pathStr); + } + if (pPhotoInfo != NULL) + { + delete pPhotoInfo; + } return res ? JNI_TRUE : JNI_FALSE; } @@ -1328,8 +1353,7 @@ Java_com_xypower_mpapp_MicroPhotoService_exportPrivateFile( #ifdef USING_NRSEC - if (env->GetStringUTFLength(outputPath) <= 0) - { + if (env->GetStringUTFLength(outputPath) <= 0) { return JNI_FALSE; } @@ -1340,8 +1364,7 @@ Java_com_xypower_mpapp_MicroPhotoService_exportPrivateFile( GpioControl::setSpiPower(true); NrsecPort nrsec; - if (!nrsec.Open(path)) - { + if (!nrsec.Open(path)) { return JNI_FALSE; } @@ -1354,9 +1377,8 @@ Java_com_xypower_mpapp_MicroPhotoService_exportPrivateFile( GpioControl::setSpiPower(false); CPhoneDevice::TurnOffCameraPower(NULL); - if (res) - { - const char* outputPathStr = env->GetStringUTFChars(outputPath, 0); + if (res) { + const char *outputPathStr = env->GetStringUTFChars(outputPath, 0); res = writeFile(outputPathStr, &data[0], len); env->ReleaseStringUTFChars(outputPath, outputPathStr); } @@ -1365,4 +1387,4 @@ Java_com_xypower_mpapp_MicroPhotoService_exportPrivateFile( #else return JNI_FALSE; #endif -} +} \ No newline at end of file diff --git a/app/src/main/cpp/PhoneDevice.cpp b/app/src/main/cpp/PhoneDevice.cpp index 27de01f1..ac8c9a13 100644 --- a/app/src/main/cpp/PhoneDevice.cpp +++ b/app/src/main/cpp/PhoneDevice.cpp @@ -27,16 +27,20 @@ #ifdef USING_HDRPLUS #include +#include #endif #include #include #include +#include namespace fs = std::filesystem; +#if 0 #define CMD_SET_485_EN_STATE 131 #define CMD_SET_CAM_3V3_EN_STATE 132 #define CMD_SET_12V_EN_STATE 133 +#endif extern bool GetJniEnv(JavaVM *vm, JNIEnv **env, bool& didAttachThread); @@ -46,6 +50,26 @@ extern bool GetJniEnv(JavaVM *vm, JNIEnv **env, bool& didAttachThread); // are normalized to eight bits. static const int kMaxChannelValue = 262143; +class ByteArraysPointer +{ +public: + ByteArraysPointer() + { + } + ~ByteArraysPointer() + { +#ifdef _DEBUG + ALOGD("ByteArray Size=%u", (uint32_t)byteArrays.size()); + for (auto it = byteArrays.cbegin(); it != byteArrays.cend(); ++it) + { + ALOGD("ByteArray Free: Size=%u", (uint32_t)((*it).size())); + } + +#endif + byteArrays.clear(); + } + std::vector > byteArrays; +}; cv::Mat convert16bit2_8bit_(cv::Mat ans){ if(ans.type()==CV_16UC3){ @@ -71,7 +95,13 @@ cv::Mat convert16bit2_8bit_(cv::Mat ans){ return ans; } - +char* MakeArgv(const std::string v) +{ + char* argv = new char[v.size() + 1]; + memset(argv, 0, v.size() + 1); + strcpy(argv, v.c_str()); + return argv; +} static long getFreeMemoryImpl(const char* const sums[], const size_t sumsLen[], size_t num) { @@ -181,11 +211,29 @@ bool CPhoneDevice::CPhoneCamera::on_image(cv::Mat& rgb) return false; } -bool CPhoneDevice::CPhoneCamera::onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, std::vector >& frames) +bool CPhoneDevice::CPhoneCamera::onOneCapture(std::shared_ptr characteristics, std::shared_ptr result, uint32_t ldr, uint32_t duration, cv::Mat rgb) +{ + if (m_dev != NULL) + { + return m_dev->onOneCapture(characteristics, result, ldr, duration, rgb); + } + return false; +} + +bool CPhoneDevice::CPhoneCamera::onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, uint32_t duration, std::vector >& frames) +{ + if (m_dev != NULL) + { + return m_dev->onBurstCapture(characteristics, results, ldr, duration, frames); + } + return false; +} + +bool CPhoneDevice::CPhoneCamera::onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, uint32_t duration, std::vector >& frames) { if (m_dev != NULL) { - return m_dev->onBurstCapture(characteristics, results, ldr, frames); + return m_dev->onBurstCapture(characteristics, results, ldr, duration, frames); } return false; } @@ -211,11 +259,29 @@ CPhoneDevice::CJpegCamera::CJpegCamera(CPhoneDevice* dev, int32_t width, int32_t { } -bool CPhoneDevice::CJpegCamera::onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, std::vector >& frames) +bool CPhoneDevice::CJpegCamera::onOneCapture(std::shared_ptr characteristics, std::shared_ptr result, uint32_t ldr, uint32_t duration, cv::Mat rgb) +{ + if (m_dev != NULL) + { + return m_dev->onOneCapture(characteristics, result, ldr, duration, rgb); + } + return false; +} + +bool CPhoneDevice::CJpegCamera::onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, uint32_t duration, std::vector >& frames) +{ + if (m_dev != NULL) + { + m_dev->onBurstCapture(characteristics, results, ldr, duration, frames); + } + return true; +} + +bool CPhoneDevice::CJpegCamera::onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, uint32_t duration, std::vector >& frames) { if (m_dev != NULL) { - m_dev->onBurstCapture(characteristics, results, ldr, frames); + m_dev->onBurstCapture(characteristics, results, ldr, duration, frames); } return true; } @@ -322,7 +388,7 @@ std::mutex CPhoneDevice::m_powerLocker; long CPhoneDevice::mCameraPowerCount = 0; long CPhoneDevice::mOtgCount = 0; -CPhoneDevice::CPhoneDevice(JavaVM* vm, jobject service, const std::string& appPath, unsigned int netId, unsigned int versionCode) : mVersionCode(versionCode) +CPhoneDevice::CPhoneDevice(JavaVM* vm, jobject service, const std::string& appPath, unsigned int netId, unsigned int versionCode, const std::string& nativeLibDir) : mVersionCode(versionCode), m_nativeLibraryDir(nativeLibDir) { mCamera = NULL; m_listener = NULL; @@ -364,10 +430,14 @@ CPhoneDevice::CPhoneDevice(JavaVM* vm, jobject service, const std::string& appPa mGetSystemInfoMid = env->GetMethodID(classService, "getSystemInfo", "()Ljava/lang/String;"); mInstallAppMid = env->GetMethodID(classService, "installApp", "(Ljava/lang/String;J)Z"); - mRebootMid = env->GetMethodID(classService, "reboot", "(IJ)V"); + mRebootMid = env->GetMethodID(classService, "reboot", "(IJLjava/lang/String;)V"); mEnableGpsMid = env->GetMethodID(classService, "enableGps", "(Z)V"); mRequestPositionMid = env->GetMethodID(classService, "requestPosition", "()Z"); + mExecHdrplusMid = env->GetMethodID(classService, "execHdrplus", "(IILjava/lang/String;Ljava/lang/String;)I"); + + mCallSysCameraMid = env->GetMethodID(classService, "callSystemCamera", "(IJ)V"); + env->DeleteLocalRef(classService); } @@ -378,6 +448,7 @@ CPhoneDevice::CPhoneDevice(JavaVM* vm, jobject service, const std::string& appPa m_timerUidFeed = time(NULL) * 1000; m_wakelockIdFeed = (unsigned long)m_timerUidFeed; + m_uniqueIdFeed = (unsigned long)m_timerUidFeed; #ifdef USING_NRSEC TurnOnCameraPower(env); @@ -439,7 +510,7 @@ void CPhoneDevice::SetRecognizationCfg(const IDevice::CFG_RECOGNIZATION* pRecogn } else { - XYLOG(XYLOG_SEVERITY_INFO, "AI Enabled"); + XYLOG(XYLOG_SEVERITY_INFO, "AI Enabled and will Init NCNN"); ncnn_init(); mAIInitialized = true; bool res = YoloV5Ncnn_Init(paramFile, binFile); @@ -549,6 +620,7 @@ bool CPhoneDevice::SelfTest(std::string& result) int32_t width = 0; int32_t height = 0; NdkCamera::CAMERA_PARAMS params = { 0 }; + params.burstCaptures = 1; if (usbCamera) { TurnOnOtg(NULL); @@ -936,7 +1008,7 @@ bool CPhoneDevice::InstallAPP(const std::string& path, unsigned int delayedTime) return true; } -bool CPhoneDevice::Reboot(int resetType) +bool CPhoneDevice::Reboot(int resetType, const std::string& reason) { if (resetType == REBOOT_TYPE_DEVICE) { @@ -951,13 +1023,13 @@ bool CPhoneDevice::Reboot(int resetType) else { long timeout = 1000; - RestartApp(resetType, timeout); + RestartApp(resetType, timeout, reason); } return true; } -void CPhoneDevice::RestartApp(int resetType, long timeout) +void CPhoneDevice::RestartApp(int resetType, long timeout, const std::string& reason) { JNIEnv* env = NULL; bool didAttachThread = false; @@ -966,7 +1038,13 @@ void CPhoneDevice::RestartApp(int resetType, long timeout) { ALOGE("Failed to get JNI Env"); } - env->CallVoidMethod(m_javaService, mRebootMid, resetType, timeout); + + jstring jreason = NULL; + if (!reason.empty()) + { + jreason = env->NewStringUTF(reason.c_str()); + } + env->CallVoidMethod(m_javaService, mRebootMid, resetType, timeout, jreason); if (didAttachThread) { m_vm->DetachCurrentThread(); @@ -1110,7 +1188,7 @@ void CPhoneDevice::handleRebootTimer(union sigval v) CPhoneDevice* pDevice = (CPhoneDevice*)(v.sival_ptr); // Reboot APP XYLOG(XYLOG_SEVERITY_ERROR, "Camera Close Thread is DEAD, will RESTART app"); - pDevice->RestartApp(0, 2000); + pDevice->RestartApp(0, 2000, "Camera Can't Close"); } // void CPhoneDevice::handleRebootTimerImpl() @@ -1368,7 +1446,7 @@ bool CPhoneDevice::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector< TurnOnCameraPower(NULL); res = true; - if (mPhotoInfo.mediaType == 0/* && mPhotoInfo.usingRawFormat == 0*/) + if (mPhotoInfo.mediaType == 0 && mPhotoInfo.usingSysCamera == 0) { mCamera = new CPhoneCamera(this, photoInfo.width, photoInfo.height, params); // mCamera = new CJpegCamera(this, photoInfo.width, photoInfo.height, mPath, params); @@ -1378,6 +1456,7 @@ bool CPhoneDevice::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector< } else { + bool hasFatalError = mCamera->HasFatalError(); XYLOG(XYLOG_SEVERITY_DEBUG, "TP: Failed to OpenCamera CH=%u PR=%X PHOTOID=%u", (unsigned int)photoInfo.channel, (unsigned int)photoInfo.preset, photoInfo.photoId); delete mCamera; mCamera = NULL; @@ -1388,6 +1467,34 @@ bool CPhoneDevice::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector< { TurnOffOtg(NULL); } + + if (hasFatalError) + { + XYLOG(XYLOG_SEVERITY_DEBUG, "TP: Fatal Error Happened, will RestartAPP in 60s CH=%u PR=%X PHOTOID=%u", (unsigned int)photoInfo.channel, (unsigned int)photoInfo.preset, photoInfo.photoId); + RestartApp(REBOOT_TYPE_APP, 60000, "FatalErrorOnCamera"); + } + } + } + else if (mPhotoInfo.usingSysCamera == 1) + { + JNIEnv* env = NULL; + bool didAttachThread = false; + res = GetJniEnv(m_vm, &env, didAttachThread); + if (!res) + { + ALOGE("Failed to get JNI Env"); + return false; + } + + IDevice::PHOTO_INFO *pPhotoInfo = new IDevice::PHOTO_INFO(mPhotoInfo); + + jboolean photoOrVideo = mPhotoInfo.mediaType == 0 ? JNI_TRUE : JNI_FALSE; + env->CallVoidMethod(m_javaService, mCallSysCameraMid, mPhotoInfo.cameraId, + reinterpret_cast(pPhotoInfo)); + + if (didAttachThread) + { + m_vm->DetachCurrentThread(); } } else @@ -1512,9 +1619,9 @@ void DrawOutlineText(cv::Ptr ft2, cv::Mat& mat, const std::st } } -bool CPhoneDevice::onBurstCapture(std::shared_ptr characteristics, - std::vector >& results, - uint32_t ldr, std::vector >& frames) +bool CPhoneDevice::onOneCapture(std::shared_ptr characteristics, + std::shared_ptr result, + uint32_t ldr, uint32_t duration, cv::Mat rgb) { time_t takingTime = time(NULL); if (mPhotoInfo.remedy != 0) @@ -1532,7 +1639,7 @@ bool CPhoneDevice::onBurstCapture(std::shared_ptr characteristi std::string path; path.swap(mPath); - std::string tmpPath = m_appPath + std::string(APP_DIR_TMP DIR_SEP_STR) + std::to_string(photoInfo.photoId); + // std::string tmpPath = m_appPath + std::string(APP_DIR_TMP DIR_SEP_STR) + std::to_string(photoInfo.photoId); acamera_metadata_enum_android_lens_facing_t facing = ACAMERA_LENS_FACING_FRONT; ACameraMetadata_const_entry e = { 0 }; @@ -1556,158 +1663,594 @@ bool CPhoneDevice::onBurstCapture(std::shared_ptr characteristi CPhoneCamera* pCamera = mCamera; mCamera = NULL; - std::thread th([=]()mutable - { - cv::Mat rgb; - std::vector > rawFiles; + media_status_t mstatus; - media_status_t mstatus; - std::string cameraInfo; - if (photoInfo.usingRawFormat != 0) + std::thread closeThread(&CPhoneDevice::CloseCamera2, this, pCamera, photoInfo.photoId, turnOffOtg); + m_threadClose.swap(closeThread); + if (closeThread.joinable()) + { + closeThread.detach(); + } + + CPhoneDevice* pThis = this; + std::thread th([pThis, characteristics, result, photoInfo, osds, path, rgb, facing, sensorOrientation, ldr, duration, takingTime]()mutable + { + std::string cameraInfo; + if (photoInfo.outputDbgInfo != 0) + { + NdkCamera::CAPTURE_RESULT captureResult = { 0 }; + NdkCamera::EnumCameraResult(result.get(), captureResult); + + char extimeunit[4] = { 0 }; + unsigned int extime = (captureResult.exposureTime >= 1000000) ? ((unsigned int)(captureResult.exposureTime / 1000000)) : ((unsigned int)(captureResult.exposureTime / 1000)); + strcpy(extimeunit, (captureResult.exposureTime >= 1000000) ? "ms" : "μs"); + char str[128] = { 0 }; + snprintf(str, sizeof(str), "AE=%u AF=%u EXPS=%u%s(%d) ISO=%d AFS=%u AES=%u AWBS=%u SCENE=%d LDR=%d(%u) %0.1fx T=%u FD=%lld", + captureResult.autoExposure, captureResult.autoFocus, + extime, extimeunit, captureResult.compensation, captureResult.sensitivity, + // isnan(captureResult.FocusDistance) ? 0 : captureResult.FocusDistance, + (unsigned int)captureResult.afState, (unsigned int)captureResult.aeState, captureResult.awbState, + captureResult.sceneMode, GpioControl::getLightAdc(), ldr, captureResult.zoomRatio, + duration, captureResult.frameDuration); + cameraInfo = str; + } + +#ifdef OUTPUT_CAMERA_DBG_INFO +#if 0 + bool shouldRetry = false; + if (ldr != ~0) { - // - for (int idx = 0; idx < frames.size(); idx++) + if (ldr < MIN_LIGHT_Y) { - std::shared_ptr spImage = frames[idx]; - std::shared_ptr result = results[idx]; - - auto it = rawFiles.insert(rawFiles.end(), std::vector()); - - int32_t width; - int32_t height; - AImage_getWidth(spImage.get(), &width); - AImage_getHeight(spImage.get(), &height); + if (photoInfo.retries < (DEFAULT_TAKE_PHOTO_RETRIES - 1)) + { + shouldRetry = true; + char presetBuf[16] = {0}; + snprintf(presetBuf, sizeof(presetBuf), "%02X", photoInfo.retries); + // replaceAll(fullPath, ".jpg", std::string("-") + std::to_string(photoInfo.retries) + ".jpg"); + replaceAll(fullPath, "_FF_", std::string("_") + presetBuf + std::string("_")); + XYLOG(XYLOG_SEVERITY_ERROR, "Photo is TOO dark or light(LDR=%u), will RETRY it", + (uint32_t) captureResult.avgY); - int planeCount; - media_status_t status = AImage_getNumberOfPlanes(spImage.get(), &planeCount); - AASSERT(status == AMEDIA_OK && planeCount == 1, "Error: getNumberOfPlanes() planeCount = %d", planeCount); + // photoInfo.usingRawFormat = 1; + } + } + else if (ldr > MAX_LIGHT_Y) + { + if (photoInfo.retries < (DEFAULT_TAKE_PHOTO_RETRIES - 1)) + { + shouldRetry = true; + char presetBuf[16] = {0}; + snprintf(presetBuf, sizeof(presetBuf), "%02X", photoInfo.retries); + // replaceAll(fullPath, ".jpg", std::string("-") + std::to_string(photoInfo.retries) + ".jpg"); + replaceAll(fullPath, "_FF_", std::string("_") + presetBuf + std::string("_")); + XYLOG(XYLOG_SEVERITY_ERROR, "Photo is TOO dark or light(LDR=%u), will RETRY it", + (uint32_t) captureResult.avgY); + } - uint8_t *planeData = NULL; - int planeDataLen = 0; - mstatus = AImage_getPlaneData(spImage.get(), 0, &planeData, &planeDataLen); - DngCreator dngCreator(characteristics.get(), result.get()); - dngCreator.writeInputBuffer(*it, planeData, planeDataLen, width, height, 0); + photoInfo.compensation = -2 * ((int16_t) ((uint16_t) captureResult.avgY)); } } - else +#endif // 0 +#endif // OUTPUT_CAMERA_DBG_INFO + + // Notify to take next photo + pThis->TakePhotoCb(1, photoInfo, "", takingTime); + + bool res = pThis->PostProcessPhoto(photoInfo, osds, path, cameraInfo, rgb); + if (res) + { + // TakePhotoCb(2, photoInfo, path, takingTime); + } + }); + + th.detach(); + + return true; +} + +bool CPhoneDevice::onBurstCapture(std::shared_ptr characteristics, + std::vector >& results, + uint32_t ldr, uint32_t duration, std::vector >& frames) +{ + time_t takingTime = time(NULL); + if (mPhotoInfo.remedy != 0) + { + if ((takingTime - mPhotoInfo.scheduleTime) > 30) + { + takingTime = mPhotoInfo.scheduleTime + mPhotoInfo.channel * 2; + } + } + mPhotoInfo.photoTime = takingTime; + + vector osds; + osds.swap(mOsds); + PHOTO_INFO photoInfo = mPhotoInfo; + std::string path; + path.swap(mPath); + + // std::string tmpPath = m_appPath + std::string(APP_DIR_TMP DIR_SEP_STR) + std::to_string(photoInfo.photoId); + std::shared_ptr pByteArrays = std::make_shared(); + pByteArrays.get()->byteArrays.swap(frames); + + bool turnOffOtg = (photoInfo.usbCamera != 0); + CPhoneCamera* pCamera = mCamera; + mCamera = NULL; + + std::thread closeThread(&CPhoneDevice::CloseCamera2, this, pCamera, photoInfo.photoId, turnOffOtg); + m_threadClose.swap(closeThread); + if (closeThread.joinable()) + { + closeThread.detach(); + } + + CPhoneDevice* pThis = this; + std::thread th([pThis, characteristics, results, photoInfo, osds, path, pByteArrays, ldr, duration, takingTime]()mutable + { + cv::Mat rgb; + std::string cameraInfo; + media_status_t mstatus; + + acamera_metadata_enum_android_lens_facing_t facing = ACAMERA_LENS_FACING_FRONT; + ACameraMetadata_const_entry e = { 0 }; + camera_status_t status = ACameraMetadata_getConstEntry(characteristics.get(), ACAMERA_LENS_FACING, &e); + if (status == ACAMERA_OK) + { + facing = (acamera_metadata_enum_android_lens_facing_t)e.data.u8[0]; + } + + int sensorOrientation = 0; + { + ACameraMetadata_const_entry e = { 0 }; + status = ACameraMetadata_getConstEntry(characteristics.get(), ACAMERA_SENSOR_ORIENTATION, &e); + if (status == ACAMERA_OK) + { + sensorOrientation = (int)e.data.i32[0]; + } + } + + if (photoInfo.outputDbgInfo != 0) + { + if (!results.empty()) + { + NdkCamera::CAPTURE_RESULT captureResult = { 0 }; + NdkCamera::EnumCameraResult(results[0].get(), captureResult); + + char extimeunit[4] = { 0 }; + unsigned int extime = (captureResult.exposureTime >= 1000000) ? ((unsigned int)(captureResult.exposureTime / 1000000)) : ((unsigned int)(captureResult.exposureTime / 1000)); + strcpy(extimeunit, (captureResult.exposureTime >= 1000000) ? "ms" : "μs"); + char str[128] = { 0 }; + snprintf(str, sizeof(str), "AE=%u AF=%u EXPS=%u%s(%d) ISO=%d AFS=%u AES=%u AWBS=%u SCENE=%d LDR=%d(%u) %0.1fx T=%u FD=%lld BURST", + captureResult.autoExposure, captureResult.autoFocus, + extime, extimeunit, captureResult.compensation, captureResult.sensitivity, + // isnan(captureResult.FocusDistance) ? 0 : captureResult.FocusDistance, + (unsigned int)captureResult.afState, (unsigned int)captureResult.aeState, captureResult.awbState, + captureResult.sceneMode, GpioControl::getLightAdc(), ldr, captureResult.zoomRatio, + duration, captureResult.frameDuration); + cameraInfo = str; + } + } + +#ifdef OUTPUT_CAMERA_DBG_INFO +#if 0 + bool shouldRetry = false; + if (ldr != ~0) { - if (results.size() == 1 && frames.size() == 1) + if (ldr < MIN_LIGHT_Y) { - std::shared_ptr result = results[0]; - std::shared_ptr frame = frames[0]; + if (photoInfo.retries < (DEFAULT_TAKE_PHOTO_RETRIES - 1)) + { + shouldRetry = true; + char presetBuf[16] = {0}; + snprintf(presetBuf, sizeof(presetBuf), "%02X", photoInfo.retries); + // replaceAll(fullPath, ".jpg", std::string("-") + std::to_string(photoInfo.retries) + ".jpg"); + replaceAll(fullPath, "_FF_", std::string("_") + presetBuf + std::string("_")); + XYLOG(XYLOG_SEVERITY_ERROR, "Photo is TOO dark or light(LDR=%u), will RETRY it", + (uint32_t) captureResult.avgY); - if (photoInfo.outputDbgInfo != 0) + // photoInfo.usingRawFormat = 1; + } + } + else if (ldr > MAX_LIGHT_Y) + { + if (photoInfo.retries < (DEFAULT_TAKE_PHOTO_RETRIES - 1)) { - NdkCamera::CAPTURE_RESULT captureResult = { 0 }; - NdkCamera::EnumCameraResult(result.get(), captureResult); - - char extimeunit[4] = { 0 }; - unsigned int extime = (captureResult.exposureTime >= 1000000) ? ((unsigned int)(captureResult.exposureTime / 1000000)) : ((unsigned int)(captureResult.exposureTime / 1000)); - strcpy(extimeunit, (captureResult.exposureTime >= 1000000) ? "ms" : "μs"); - char str[128] = { 0 }; - snprintf(str, sizeof(str), "AE=%u AF=%u EXPS=%u%s(%d) ISO=%d AFS=%u AES=%u AWBS=%u SCENE=%d LDR=%d(%u) %0.1fx T=%u FD=%lld", - captureResult.autoExposure, captureResult.autoFocus, - extime, extimeunit, captureResult.compensation, captureResult.sensitivity, - // isnan(captureResult.FocusDistance) ? 0 : captureResult.FocusDistance, - (unsigned int)captureResult.afState, (unsigned int)captureResult.aeState, captureResult.awbState, - captureResult.sceneMode, GpioControl::getLightAdc(), ldr, captureResult.zoomRatio, - (uint32_t)captureResult.duration, captureResult.frameDuration); - cameraInfo = str; + shouldRetry = true; + char presetBuf[16] = {0}; + snprintf(presetBuf, sizeof(presetBuf), "%02X", photoInfo.retries); + // replaceAll(fullPath, ".jpg", std::string("-") + std::to_string(photoInfo.retries) + ".jpg"); + replaceAll(fullPath, "_FF_", std::string("_") + presetBuf + std::string("_")); + XYLOG(XYLOG_SEVERITY_ERROR, "Photo is TOO dark or light(LDR=%u), will RETRY it", + (uint32_t) captureResult.avgY); } - int32_t format; - mstatus = AImage_getFormat(frame.get(), &format); + photoInfo.compensation = -2 * ((int16_t) ((uint16_t) captureResult.avgY)); + } + } +#endif // 0 +#endif // OUTPUT_CAMERA_DBG_INFO - if (format == AIMAGE_FORMAT_YUV_420_888) - { - int32_t width; - int32_t height; - mstatus = AImage_getWidth(frame.get(), &width); - mstatus = AImage_getHeight(frame.get(), &height); - - int32_t y_pixelStride = 0; - int32_t u_pixelStride = 0; - int32_t v_pixelStride = 0; - AImage_getPlanePixelStride(frame.get(), 0, &y_pixelStride); - AImage_getPlanePixelStride(frame.get(), 1, &u_pixelStride); - AImage_getPlanePixelStride(frame.get(), 2, &v_pixelStride); - - int32_t y_rowStride = 0; - int32_t u_rowStride = 0; - int32_t v_rowStride = 0; - AImage_getPlaneRowStride(frame.get(), 0, &y_rowStride); - AImage_getPlaneRowStride(frame.get(), 1, &u_rowStride); - AImage_getPlaneRowStride(frame.get(), 2, &v_rowStride); - - uint8_t* y_data = 0; - uint8_t* u_data = 0; - uint8_t* v_data = 0; - int y_len = 0; - int u_len = 0; - int v_len = 0; - AImage_getPlaneData(frame.get(), 0, &y_data, &y_len); - AImage_getPlaneData(frame.get(), 1, &u_data, &u_len); - AImage_getPlaneData(frame.get(), 2, &v_data, &v_len); - - if (u_data == v_data + 1 && v_data == y_data + width * height && y_pixelStride == 1 && u_pixelStride == 2 && v_pixelStride == 2 && y_rowStride == width && u_rowStride == width && v_rowStride == width) - { - // already nv21 - ConvertYUV21ToMat(y_data, width, height, photoInfo.width, photoInfo.height, sensorOrientation, facing == ACAMERA_LENS_FACING_FRONT, photoInfo.orientation, rgb); - } - else + // Notify to take next photo + pThis->TakePhotoCb(1, photoInfo, "", takingTime); + +#ifdef USING_EXEC_HDRP + if (photoInfo.usingNewHdrplus) { - // construct nv21 - uint8_t* nv21 = new uint8_t[width * height + width * height / 2]; + XYLOG(XYLOG_SEVERITY_ERROR, "Start HDR CH=%u IMGID=%u", (uint32_t)photoInfo.channel, (uint32_t)photoInfo.photoId); + std::vector > localFrames; + localFrames.swap(pByteArrays.get()->byteArrays); +#ifdef _DEBUG + std::vector& firstFrame = localFrames[0]; + writeFile("/sdcard/com.xypower.mpapp/tmp/dngs/1.dng", &firstFrame[0], firstFrame.size()); + writeFile("/sdcard/com.xypower.mpapp/tmp/dngs/2.dng", &localFrames[1][0], localFrames[1].size()); + writeFile("/sdcard/com.xypower.mpapp/tmp/dngs/3.dng", &localFrames[2][0], localFrames[2].size()); + writeFile("/sdcard/com.xypower.mpapp/tmp/dngs/4.dng", &localFrames[3][0], localFrames[3].size()); + + // readFile("/sdcard/com.xypower.mpapp/tmp/dngs/001.dng", localFrames[0]); + // readFile("/sdcard/com.xypower.mpapp/tmp/dngs/002.dng", localFrames[1]); + // readFile("/sdcard/com.xypower.mpapp/tmp/dngs/003.dng", localFrames[2]); + // readFile("/sdcard/com.xypower.mpapp/tmp/dngs/004.dng", localFrames[3]); + +#endif + doHdrPlus(localFrames, rgb); + cv::cvtColor(rgb.clone(), rgb, cv::COLOR_RGB2BGR); + + localFrames.clear(); + +#ifdef _DEBUG + std::vector params; + params.push_back(cv::IMWRITE_JPEG_QUALITY); + params.push_back(95); + cv::imwrite("/sdcard/com.xypower.mpapp/tmp/1.jpg", rgb, params); +#endif + + XYLOG(XYLOG_SEVERITY_ERROR, "Finish HDR CH=%u IMGID=%u", (uint32_t)photoInfo.channel, (uint32_t)photoInfo.photoId); + { - // Y - uint8_t* yptr = nv21; - for (int y = 0; y < height; y++) + cv::Mat tempPic = convert16bit2_8bit_(rgb); + rgb = tempPic; + } + + if (photoInfo.orientation > 0) + { + if (photoInfo.orientation == 1) { - const uint8_t* y_data_ptr = y_data + y_rowStride * y; - for (int x = 0; x < width; x++) + if (facing == ACAMERA_LENS_FACING_FRONT) { - yptr[0] = y_data_ptr[0]; - yptr++; - y_data_ptr += y_pixelStride; + cv::flip(rgb, rgb, 1); } } - - // UV - uint8_t* uvptr = nv21 + width * height; - for (int y = 0; y < height / 2; y++) + else if (photoInfo.orientation == 2) + { + cv::Mat tempPic; + cv::transpose(rgb, tempPic); + cv::flip(tempPic, rgb, 1); + } + else if (photoInfo.orientation == 3) { - const uint8_t* v_data_ptr = v_data + v_rowStride * y; - const uint8_t* u_data_ptr = u_data + u_rowStride * y; - for (int x = 0; x < width / 2; x++) + if (facing == ACAMERA_LENS_FACING_FRONT) + { + flip(rgb, rgb, 0); + } + else { - uvptr[0] = v_data_ptr[0]; - uvptr[1] = u_data_ptr[0]; - uvptr += 2; - v_data_ptr += v_pixelStride; - u_data_ptr += u_pixelStride; + cv::flip(rgb, rgb, -1); } } + else if ((photoInfo.orientation % 4) == 0) + { + cv::Mat tempPic; + cv::transpose(rgb, tempPic); + cv::flip(tempPic, rgb, 0); + } + + XYLOG(XYLOG_SEVERITY_ERROR, "Finish rotation CH=%u IMGID=%u", (uint32_t)photoInfo.channel, (uint32_t)photoInfo.photoId); } + // cv::cvtColor(rgb, rgb, cv::COLOR_RGB2BGR); + } + else + { + uint64_t uniqueId = pThis->m_uniqueIdFeed.fetch_add(1); + + std::string tmpDir = pThis->m_appPath + (APP_DIR_TMP DIR_SEP_STR) + std::to_string(uniqueId) + DIR_SEP_STR; + EnsureDirectoryPathExists(tmpDir); + + std::vector > localFrames; + localFrames.swap(pByteArrays.get()->byteArrays); + + std::string outputPath = tmpDir + "output.bmp"; + size_t numberOfFrames = localFrames.size(); + std::vector imagePaths; + for (int idx = 0; idx < localFrames.size(); idx++) + { + std::string imagePath = tmpDir + std::to_string(idx) + ".dng"; + std::vector& frame = localFrames[idx]; + if (writeFile(imagePath, &frame[0], frame.size())) + { + imagePaths.push_back(imagePath); + } + } + localFrames.clear(); + + int exitCode = pThis->CallExecv(photoInfo.orientation, facing == ACAMERA_LENS_FACING_FRONT ? 1 : 0, outputPath, imagePaths); + for (auto it = imagePaths.cbegin(); it != imagePaths.cend(); ++it) + { + std::remove((*it).c_str()); + } + + if (existsFile(outputPath)) + { + rgb = cv::imread(outputPath); + std::remove(outputPath.c_str()); + } + + std::error_code errCode; + fs::remove_all(fs::path(tmpDir), errCode); + } +#else // USING_EXEC_HDRP + XYLOG(XYLOG_SEVERITY_ERROR, "Start HDR CH=%u IMGID=%u", (uint32_t)photoInfo.channel, (uint32_t)photoInfo.photoId); + hdrplus::hdrplus_pipeline pipeline; + std::vector > localFrames; + localFrames.swap(pByteArrays.get()->byteArrays); + pipeline.run_pipeline(localFrames, 0, rgb); + localFrames.clear(); + + XYLOG(XYLOG_SEVERITY_ERROR, "Finish HDR CH=%u IMGID=%u", (uint32_t)photoInfo.channel, (uint32_t)photoInfo.photoId); + + { + cv::Mat tempPic = convert16bit2_8bit_(rgb); + rgb = tempPic; + } + + if (photoInfo.orientation > 0) + { + if (photoInfo.orientation == 1) + { + if (facing == ACAMERA_LENS_FACING_FRONT) + { + cv::flip(rgb, rgb, 1); + } + } + else if (photoInfo.orientation == 2) + { + cv::Mat tempPic; + cv::transpose(rgb, tempPic); + cv::flip(tempPic, rgb, 1); + } + else if (photoInfo.orientation == 3) + { + if (facing == ACAMERA_LENS_FACING_FRONT) + { + flip(rgb, rgb, 0); + } + else + { + cv::flip(rgb, rgb, -1); + } + } + else if ((photoInfo.orientation % 4) == 0) + { + cv::Mat tempPic; + cv::transpose(rgb, tempPic); + cv::flip(tempPic, rgb, 0); + } + + XYLOG(XYLOG_SEVERITY_ERROR, "Finish rotation CH=%u IMGID=%u", (uint32_t)photoInfo.channel, (uint32_t)photoInfo.photoId); + } + cv::cvtColor(rgb, rgb, cv::COLOR_RGB2BGR); +#endif // USING_EXEC_HDRP + bool res = pThis->PostProcessPhoto(photoInfo, osds, path, cameraInfo, rgb); + if (res) + { + // TakePhotoCb(2, photoInfo, path, takingTime); + } + }); - ConvertYUV21ToMat(nv21, width, height, photoInfo.width, photoInfo.height, sensorOrientation, facing == ACAMERA_LENS_FACING_FRONT, photoInfo.orientation, rgb); + th.detach(); - delete[] nv21; - } + return true; +} + +bool CPhoneDevice::onBurstCapture(std::shared_ptr characteristics, + std::vector >& results, + uint32_t ldr, uint32_t duration, std::vector >& frames) +{ + time_t takingTime = time(NULL); + if (mPhotoInfo.remedy != 0) + { + if ((takingTime - mPhotoInfo.scheduleTime) > 30) + { + takingTime = mPhotoInfo.scheduleTime + mPhotoInfo.channel * 2; + } + } + mPhotoInfo.photoTime = takingTime; + + vector osds; + osds.swap(mOsds); + PHOTO_INFO photoInfo = mPhotoInfo; + std::string path; + path.swap(mPath); + + // std::string tmpPath = m_appPath + std::string(APP_DIR_TMP DIR_SEP_STR) + std::to_string(photoInfo.photoId); + + acamera_metadata_enum_android_lens_facing_t facing = ACAMERA_LENS_FACING_FRONT; + ACameraMetadata_const_entry e = { 0 }; + camera_status_t status = ACameraMetadata_getConstEntry(characteristics.get(), ACAMERA_LENS_FACING, &e); + if (status == ACAMERA_OK) + { + facing = (acamera_metadata_enum_android_lens_facing_t)e.data.u8[0]; + } + + int sensorOrientation = 0; + { + ACameraMetadata_const_entry e = { 0 }; + status = ACameraMetadata_getConstEntry(characteristics.get(), ACAMERA_SENSOR_ORIENTATION, &e); + if (status == ACAMERA_OK) + { + sensorOrientation = (int)e.data.i32[0]; + } + } + + bool turnOffOtg = (photoInfo.usbCamera != 0); + CPhoneCamera* pCamera = mCamera; + mCamera = NULL; + + cv::Mat rgb; + media_status_t mstatus; + + std::vector > rawFiles; + + if (photoInfo.usingRawFormat != 0) + { + for (int idx = 0; idx < frames.size(); idx++) + { + std::shared_ptr spImage = frames[idx]; + std::shared_ptr spResult = results[idx]; + + hdrplus::MemFile* rawImage = new hdrplus::MemFile(); + rawFiles.push_back(std::shared_ptr(rawImage)); + // rawImage->FromAImage(spImage.get(), characteristics.get(), spResult.get()); - if (photoInfo.outputDbgInfo != 0) + int32_t width = 0; + int32_t height = 0; + mstatus = AImage_getWidth(spImage.get(), &width); + mstatus = AImage_getHeight(spImage.get(), &height); + + int32_t planeCount = 0; + mstatus = AImage_getNumberOfPlanes(spImage.get(), &planeCount); + AASSERT(status == AMEDIA_OK && planeCount == 1, "Error: getNumberOfPlanes() planeCount = %d", planeCount); + + uint8_t *planeData = NULL; + int planeDataLen = 0; + mstatus = AImage_getPlaneData(spImage.get(), 0, &planeData, &planeDataLen); + ALOGD("Start Converting Dng"); + DngCreator dngCreator(characteristics.get(), spResult.get()); + dngCreator.writeInputBuffer(rawImage->content, planeData, planeDataLen, width, height, 0); + ALOGD("End Converting Dng"); + } + } + else + { + if (results.size() == 1 && frames.size() == 1) + { + std::shared_ptr result = results[0]; + std::shared_ptr frame = frames[0]; + + int32_t format; + mstatus = AImage_getFormat(frame.get(), &format); + + if (format == AIMAGE_FORMAT_YUV_420_888) + { + int32_t width; + int32_t height; + mstatus = AImage_getWidth(frame.get(), &width); + mstatus = AImage_getHeight(frame.get(), &height); + + int32_t y_pixelStride = 0; + int32_t u_pixelStride = 0; + int32_t v_pixelStride = 0; + AImage_getPlanePixelStride(frame.get(), 0, &y_pixelStride); + AImage_getPlanePixelStride(frame.get(), 1, &u_pixelStride); + AImage_getPlanePixelStride(frame.get(), 2, &v_pixelStride); + + int32_t y_rowStride = 0; + int32_t u_rowStride = 0; + int32_t v_rowStride = 0; + AImage_getPlaneRowStride(frame.get(), 0, &y_rowStride); + AImage_getPlaneRowStride(frame.get(), 1, &u_rowStride); + AImage_getPlaneRowStride(frame.get(), 2, &v_rowStride); + + uint8_t* y_data = 0; + uint8_t* u_data = 0; + uint8_t* v_data = 0; + int y_len = 0; + int u_len = 0; + int v_len = 0; + AImage_getPlaneData(frame.get(), 0, &y_data, &y_len); + AImage_getPlaneData(frame.get(), 1, &u_data, &u_len); + AImage_getPlaneData(frame.get(), 2, &v_data, &v_len); + + if (u_data == v_data + 1 && v_data == y_data + width * height && y_pixelStride == 1 && u_pixelStride == 2 && v_pixelStride == 2 && y_rowStride == width && u_rowStride == width && v_rowStride == width) + { + // already nv21 + ConvertYUV21ToMat(y_data, width, height, photoInfo.width, photoInfo.height, sensorOrientation, facing == ACAMERA_LENS_FACING_FRONT, photoInfo.orientation, rgb); + } + else + { + // construct nv21 + uint8_t* nv21 = new uint8_t[width * height + width * height / 2]; + { + // Y + uint8_t* yptr = nv21; + for (int y = 0; y < height; y++) { + const uint8_t* y_data_ptr = y_data + y_rowStride * y; + for (int x = 0; x < width; x++) + { + yptr[0] = y_data_ptr[0]; + yptr++; + y_data_ptr += y_pixelStride; + } + } + // UV + uint8_t* uvptr = nv21 + width * height; + for (int y = 0; y < height / 2; y++) + { + const uint8_t* v_data_ptr = v_data + v_rowStride * y; + const uint8_t* u_data_ptr = u_data + u_rowStride * y; + for (int x = 0; x < width / 2; x++) + { + uvptr[0] = v_data_ptr[0]; + uvptr[1] = u_data_ptr[0]; + uvptr += 2; + v_data_ptr += v_pixelStride; + u_data_ptr += u_pixelStride; + } } } + + ConvertYUV21ToMat(nv21, width, height, photoInfo.width, photoInfo.height, sensorOrientation, facing == ACAMERA_LENS_FACING_FRONT, photoInfo.orientation, rgb); + + delete[] nv21; } } + } + } - frames.clear(); - std::thread closeThread(&CPhoneDevice::CloseCamera2, this, pCamera, photoInfo.photoId, turnOffOtg); - m_threadClose.swap(closeThread); - if (closeThread.joinable()) + frames.clear(); + + std::thread closeThread(&CPhoneDevice::CloseCamera2, this, pCamera, photoInfo.photoId, turnOffOtg); + m_threadClose.swap(closeThread); + if (closeThread.joinable()) + { + closeThread.detach(); + } + + CPhoneDevice* pThis = this; + std::thread th([pThis, characteristics, results, photoInfo, osds, path, rgb, rawFiles, facing, sensorOrientation, ldr, duration, takingTime]()mutable + { + std::string cameraInfo; + if (photoInfo.outputDbgInfo != 0) { - closeThread.detach(); + if (!results.empty()) + { + NdkCamera::CAPTURE_RESULT captureResult = { 0 }; + NdkCamera::EnumCameraResult(results[0].get(), captureResult); + + char extimeunit[4] = { 0 }; + unsigned int extime = (captureResult.exposureTime >= 1000000) ? ((unsigned int)(captureResult.exposureTime / 1000000)) : ((unsigned int)(captureResult.exposureTime / 1000)); + strcpy(extimeunit, (captureResult.exposureTime >= 1000000) ? "ms" : "μs"); + char str[128] = { 0 }; + snprintf(str, sizeof(str), "AE=%u AF=%u EXPS=%u%s(%d) ISO=%d AFS=%u AES=%u AWBS=%u SCENE=%d LDR=%d(%u) %0.1fx T=%u FD=%lld", + captureResult.autoExposure, captureResult.autoFocus, + extime, extimeunit, captureResult.compensation, captureResult.sensitivity, + // isnan(captureResult.FocusDistance) ? 0 : captureResult.FocusDistance, + (unsigned int)captureResult.afState, (unsigned int)captureResult.aeState, captureResult.awbState, + captureResult.sceneMode, GpioControl::getLightAdc(), ldr, captureResult.zoomRatio, + duration, captureResult.frameDuration); + cameraInfo = str; + } } #ifdef OUTPUT_CAMERA_DBG_INFO @@ -1750,21 +2293,19 @@ bool CPhoneDevice::onBurstCapture(std::shared_ptr characteristi #endif // OUTPUT_CAMERA_DBG_INFO // Notify to take next photo - TakePhotoCb(1, photoInfo, "", takingTime); + pThis->TakePhotoCb(1, photoInfo, "", takingTime); if (photoInfo.usingRawFormat != 0) { - XYLOG(XYLOG_SEVERITY_ERROR, "Start HDR CH=%u IMGID=%u", (uint32_t)mPhotoInfo.channel, (uint32_t)mPhotoInfo.photoId); +#ifndef USING_EXEC_HDRP + XYLOG(XYLOG_SEVERITY_ERROR, "Start HDR CH=%u IMGID=%u", (uint32_t)photoInfo.channel, (uint32_t)photoInfo.photoId); hdrplus::hdrplus_pipeline pipeline; pipeline.run_pipeline(rawFiles, 0, rgb); - XYLOG(XYLOG_SEVERITY_ERROR, "Finish HDR CH=%u IMGID=%u", (uint32_t)mPhotoInfo.channel, (uint32_t)mPhotoInfo.photoId); - -#ifdef NDEBUG - for (auto it = rawFilePaths.cbegin(); it != rawFilePaths.cend(); ++it) - { - std::remove((*it).c_str()); - } + rawFiles.clear(); #endif + + XYLOG(XYLOG_SEVERITY_ERROR, "Finish HDR CH=%u IMGID=%u", (uint32_t)photoInfo.channel, (uint32_t)photoInfo.photoId); + { cv::Mat tempPic = convert16bit2_8bit_(rgb); rgb = tempPic; @@ -1807,7 +2348,7 @@ bool CPhoneDevice::onBurstCapture(std::shared_ptr characteristi cv::cvtColor(rgb, rgb, cv::COLOR_RGB2BGR); } - bool res = PostProcessPhoto(photoInfo, osds, path, cameraInfo, rgb); + bool res = pThis->PostProcessPhoto(photoInfo, osds, path, cameraInfo, rgb); if (res) { // TakePhotoCb(2, photoInfo, path, takingTime); @@ -1819,6 +2360,38 @@ bool CPhoneDevice::onBurstCapture(std::shared_ptr characteristi return true; } +int CPhoneDevice::CallExecv(int rotation, int frontCamera, const std::string& outputPath, const std::vector& images) +{ + JNIEnv* env = NULL; + bool didAttachThread = false; + bool res = GetJniEnv(m_vm, &env, didAttachThread); + if (!res) + { + ALOGE("Failed to get JNI Env"); + } + + std::string pathsWithSpace; + for (auto it = images.cbegin(); it != images.cend(); ++it) + { + pathsWithSpace.append(*it); + pathsWithSpace.append(" "); + } + pathsWithSpace.pop_back(); + + jstring joutputPath = env->NewStringUTF(outputPath.c_str()); + jstring jpathWithSpace = env->NewStringUTF(pathsWithSpace.c_str()); + jint exitCode = env->CallIntMethod(m_javaService, mExecHdrplusMid, rotation, frontCamera, joutputPath, jpathWithSpace); + env->DeleteLocalRef(jpathWithSpace); + env->DeleteLocalRef(joutputPath); + + if (didAttachThread) + { + m_vm->DetachCurrentThread(); + } + + return exitCode; +} + bool CPhoneDevice::OnImageReady(cv::Mat& mat) { time_t takingTime = time(NULL); @@ -2406,6 +2979,20 @@ bool CPhoneDevice::OnVideoReady(bool photoOrVideo, bool result, const char* path { if (photoOrVideo) { + mPhotoInfo.photoTime = time(NULL); + CPhoneCamera* pCamera = NULL; + + std::vector objs; + std::string fullPath = mPath + CTerminal::BuildPhotoFileName(mPhotoInfo); + if (result) + { + std::rename(path, fullPath.c_str()); + } + TakePhotoCb(result ? 3 : 0, mPhotoInfo, fullPath, time(NULL), objs); + + bool turnOffOtg = (mPhotoInfo.usbCamera != 0); + std::thread closeThread(&CPhoneDevice::CloseCamera2, this, pCamera, mPhotoInfo.photoId, turnOffOtg); + m_threadClose.swap(closeThread); } else { @@ -2509,6 +3096,7 @@ void CPhoneDevice::TurnOnCameraPower(JNIEnv* env) void CPhoneDevice::TurnOffCameraPower(JNIEnv* env) { + bool turnedOff = false; m_powerLocker.lock(); if (mCameraPowerCount > 0) { @@ -2516,9 +3104,15 @@ void CPhoneDevice::TurnOffCameraPower(JNIEnv* env) if (mCameraPowerCount == 0) { GpioControl::setCam3V3Enable(false); + turnedOff = true; } } m_powerLocker.unlock(); + + if (turnedOff) + { + XYLOG(XYLOG_SEVERITY_INFO, "CAM PWR Turned Off"); + } } void CPhoneDevice::TurnOnOtg(JNIEnv* env) @@ -2535,17 +3129,24 @@ void CPhoneDevice::TurnOnOtg(JNIEnv* env) void CPhoneDevice::TurnOffOtg(JNIEnv* env) { + bool turnedOff = false; m_powerLocker.lock(); if (mOtgCount > 0) { mOtgCount--; if (mOtgCount == 0) { - ALOGD("setOtgState 0"); + // ALOGD("setOtgState 0"); GpioControl::setOtgState(false); + turnedOff = true; } } m_powerLocker.unlock(); + + if (turnedOff) + { + XYLOG(XYLOG_SEVERITY_INFO, "OTG PWR Turned Off"); + } } void CPhoneDevice::UpdateSignalLevel(int signalLevel) @@ -2559,68 +3160,6 @@ void CPhoneDevice::UpdateSimcard(const std::string& simcard) m_simcard = simcard; } -bool CPhoneDevice::ProcessRawCapture(bool result, int numberOfCaptures, const std::string& pathsJoinedByTab, bool frontCamera, int rotation, long photoId) -{ - std::vector paths = split(pathsJoinedByTab, "\t"); - - if (paths.empty()) - { - cv::Mat mat; - OnCaptureReady(true, false, mat, (unsigned long)photoId); - return false; - } - - XYLOG(XYLOG_SEVERITY_ERROR, "Start Processing Raw Capture CH=%u IMGID=%u", (uint32_t)mPhotoInfo.channel, (uint32_t)mPhotoInfo.photoId); - - hdrplus::hdrplus_pipeline pipeline; - cv::Mat mat; - pipeline.run_pipeline(paths, 0, mat); - XYLOG(XYLOG_SEVERITY_ERROR, "Finish HDR CH=%u IMGID=%u", (uint32_t)mPhotoInfo.channel, (uint32_t)mPhotoInfo.photoId); - - mat = convert16bit2_8bit_(mat.clone()); - - if (rotation >= 0) - { - if (rotation == 90) - { - cv::Mat tempPic; - cv::transpose(mat, tempPic); - cv::flip(tempPic, mat, 1); - } - else if (rotation == 180) - { - if (frontCamera) - { - flip(mat, mat, 0); - - } - else - { - cv::flip(mat, mat, -1); - } - } - else if (rotation == 270) - { - cv::Mat tempPic; - cv::transpose(mat, tempPic); - cv::flip(tempPic, mat, 0); - } - - XYLOG(XYLOG_SEVERITY_ERROR, "Finish rotation CH=%u IMGID=%u", (uint32_t)mPhotoInfo.channel, (uint32_t)mPhotoInfo.photoId); - } - cv::cvtColor(mat, mat, cv::COLOR_RGB2BGR); - - XYLOG(XYLOG_SEVERITY_ERROR, "Finish Processing Raw Capture CH=%u IMGID=%u", (uint32_t)mPhotoInfo.channel, (uint32_t)mPhotoInfo.photoId); - -#ifdef _DEBUG - // cv::cvtColor(outputImg, outputImg, cv::COLOR_RGB2BGR); - cv::imwrite("/sdcard/com.xypower.mpapp/tmp/final.jpg", mat); -#endif - - OnCaptureReady(true, result != JNI_FALSE, mat, (unsigned long)photoId); - return true; -} - int CPhoneDevice::GetIceData(IDevice::ICE_INFO *iceInfo, IDevice::ICE_TAIL *iceTail, SENSOR_PARAM *sensorParam) { Collect_sensor_data(); //15s @@ -2670,9 +3209,6 @@ int CPhoneDevice::GetWData(IDevice::WEATHER_INFO *weatherInfo) Data_DEF airt; GetWeatherData(&airt, 0); weatherInfo->air_temperature = airt.EuValue; - - if (airt.AiState == -1) return false; - GetWeatherData(&airt, 1); weatherInfo->humidity = airt.EuValue; GetWeatherData(&airt, 2); @@ -2682,91 +3218,106 @@ int CPhoneDevice::GetWData(IDevice::WEATHER_INFO *weatherInfo) GetWeatherData(&airt, 3); weatherInfo->avg_winddirection_10min = airt.EuValue; GetWeatherData(&airt, 4); - weatherInfo->precipitation = airt.EuValue; + if(airt.AiState == 2) + weatherInfo->precipitation = airt.EuValue; GetWeatherData(&airt, 5); - weatherInfo->air_pressure = airt.EuValue; + if(airt.AiState == 2) + weatherInfo->air_pressure = airt.EuValue; GetWeatherData(&airt, 6); - weatherInfo->radiation_intensity = airt.EuValue; + if(airt.AiState == 2) + weatherInfo->radiation_intensity = airt.EuValue; return true; } -#ifdef USING_N938 -bool CPhoneDevice::OpenSensors() -{ - GpioControl::setInt(CMD_SET_CAM_3V3_EN_STATE, true ? 1 : 0); - GpioControl::setInt(CMD_SET_485_EN_STATE, true ? 1 : 0); - int igpio; - GpioControl::setInt(CMD_SET_WTH_POWER, 1); - GpioControl::setInt(CMD_SET_PULL_POWER, 1); - GpioControl::setInt(CMD_SET_ANGLE_POWER, 1); - GpioControl::setInt(CMD_SET_OTHER_POWER, 1); - GpioControl::setInt(CMD_SET_PIC1_POWER, 1); - - igpio = GpioControl::getInt(CMD_SET_WTH_POWER); - igpio = GpioControl::getInt(CMD_SET_PULL_POWER); - igpio = GpioControl::getInt(CMD_SET_ANGLE_POWER); - igpio = GpioControl::getInt(CMD_SET_OTHER_POWER); - igpio = GpioControl::getInt(CMD_SET_PIC1_POWER); - - GpioControl::setInt(CMD_SET_SPI_POWER, 1); - GpioControl::setInt(CMD_SET_485_en0, 1); - GpioControl::setInt(CMD_SET_485_en1, 1); - GpioControl::setInt(CMD_SET_485_en2, 1); - GpioControl::setInt(CMD_SET_485_en3, 1); - GpioControl::setInt(CMD_SET_485_en4, 1); - - igpio = GpioControl::getInt(CMD_SET_SPI_POWER); - igpio = GpioControl::getInt(CMD_SET_485_en0); - igpio = GpioControl::getInt(CMD_SET_485_en1); - igpio = GpioControl::getInt(CMD_SET_485_en2); - igpio = GpioControl::getInt(CMD_SET_485_en3); - igpio = GpioControl::getInt(CMD_SET_485_en4); - return 0; - -} -bool CPhoneDevice::CloseSensors() -{ - GpioControl::setInt(CMD_SET_12V_EN_STATE, false ? 1 : 0); - GpioControl::setInt(CMD_SET_CAM_3V3_EN_STATE, false ? 1 : 0); - GpioControl::setInt(CMD_SET_485_EN_STATE, false ? 1 : 0); - int igpio; - GpioControl::setInt(CMD_SET_WTH_POWER, 0); - GpioControl::setInt(CMD_SET_PULL_POWER, 0); - GpioControl::setInt(CMD_SET_ANGLE_POWER, 0); - GpioControl::setInt(CMD_SET_OTHER_POWER, 0); - GpioControl::setInt(CMD_SET_PIC1_POWER, 0); - - igpio = GpioControl::getInt(CMD_SET_WTH_POWER); - igpio = GpioControl::getInt(CMD_SET_PULL_POWER); - igpio = GpioControl::getInt(CMD_SET_ANGLE_POWER); - igpio = GpioControl::getInt(CMD_SET_OTHER_POWER); - igpio = GpioControl::getInt(CMD_SET_PIC1_POWER); - - GpioControl::setInt(CMD_SET_SPI_POWER, 0); - GpioControl::setInt(CMD_SET_485_en0, 0); - GpioControl::setInt(CMD_SET_485_en1, 0); - GpioControl::setInt(CMD_SET_485_en2, 0); - GpioControl::setInt(CMD_SET_485_en3, 0); - GpioControl::setInt(CMD_SET_485_en4, 0); - - //sleep(3); - igpio = GpioControl::getInt(CMD_SET_SPI_POWER); - igpio = GpioControl::getInt(CMD_SET_485_en0); - igpio = GpioControl::getInt(CMD_SET_485_en1); - igpio = GpioControl::getInt(CMD_SET_485_en2); - igpio = GpioControl::getInt(CMD_SET_485_en3); - igpio = GpioControl::getInt(CMD_SET_485_en4); - return 0; -} -#else -bool CPhoneDevice::OpenSensors() +bool CPhoneDevice::OpenSensors(int sensortype) { - return false; + if(sensortype == MAIN_POWER_OPEN) { + GpioControl::set12VEnable(true); + GpioControl::setCam3V3Enable(true); + GpioControl::setRS485Enable(true); +// GpioControl::setInt(CMD_SET_485_EN_STATE, 1); // 打开RS485电源 +#ifndef USING_N938 + GpioControl::setInt(CMD_SET_485_ENABLE, 1); +#else + GpioControl::setInt(CMD_SPI2SERIAL_POWER_EN, 1); + GpioControl::setInt(CMD_RS485_3V3_EN, 1); +#endif + GpioControl::setInt(CMD_SET_SPI_POWER, 1); + } + if(sensortype == CAMERA_SENSOR_OPEN) + { + GpioControl::setInt(CMD_SET_PIC1_POWER, 1); + GpioControl::setInt(CMD_SET_485_EN4, 1); +// GpioControl::setInt(CMD_SET_CAM_3V3_EN_STATE, 1); // 打开3.3V电压 + // GpioControl::setInt(CMD_SET_3V3_PWR_ENABLE, 1); +#ifndef USING_N938 + GpioControl::setInt(CMD_SET_PTZ_PWR_ENABLE, 1); +#endif + } + if(sensortype == WEATHER_SENSOR_OPEN || sensortype == ICETHICK_SENSOR_OPEN) + { + GpioControl::setInt(CMD_SET_WTH_POWER, 1); + GpioControl::setInt(CMD_SET_485_EN3, 1); + } + if(sensortype == ICETHICK_SENSOR_OPEN) + { + GpioControl::setInt(CMD_SET_PULL_POWER, 1); + GpioControl::setInt(CMD_SET_ANGLE_POWER, 1); + GpioControl::setInt(CMD_SET_485_EN1, 1); + GpioControl::setInt(CMD_SET_485_EN0, 1); + } + if(sensortype == OTHER_SENSOR) + { + GpioControl::setInt(CMD_SET_OTHER_POWER, 1); + GpioControl::setInt(CMD_SET_485_EN2, 1); + } + return 0; } -bool CPhoneDevice::CloseSensors() +bool CPhoneDevice::CloseSensors(int sensortype) { - return false; -} + if(sensortype == MAIN_POWER_OPEN) + { + GpioControl::setInt(CMD_SET_SPI_POWER, 0); + GpioControl::set12VEnable(false); + GpioControl::setCam3V3Enable(false); + GpioControl::setRS485Enable(false); +// GpioControl::setInt(CMD_SET_485_EN_STATE, 0); +#ifndef USING_N938 + GpioControl::setInt(CMD_SET_485_ENABLE, 0); +#else + GpioControl::setInt(CMD_SPI2SERIAL_POWER_EN, 0); + GpioControl::setInt(CMD_RS485_3V3_EN, 0); #endif + + } + if(sensortype == CAMERA_SENSOR_OPEN) + { + GpioControl::setInt(CMD_SET_PIC1_POWER, 0); + GpioControl::setInt(CMD_SET_485_EN4, 0); +// GpioControl::setInt(CMD_SET_CAM_3V3_EN_STATE, 0); +#ifndef USING_N938 + GpioControl::setInt(CMD_SET_3V3_PWR_ENABLE, 0); + GpioControl::setInt(CMD_SET_PTZ_PWR_ENABLE, 0); +#endif + } + if(sensortype == WEATHER_SENSOR_OPEN || sensortype == ICETHICK_SENSOR_OPEN) + { + GpioControl::setInt(CMD_SET_WTH_POWER, 0); + GpioControl::setInt(CMD_SET_485_EN3, 0); + } + if(sensortype == ICETHICK_SENSOR_OPEN) + { + GpioControl::setInt(CMD_SET_PULL_POWER, 0); + GpioControl::setInt(CMD_SET_ANGLE_POWER, 0); + GpioControl::setInt(CMD_SET_485_EN1, 0); + GpioControl::setInt(CMD_SET_485_EN0, 0); + } + if(sensortype == OTHER_SENSOR) + { + GpioControl::setInt(CMD_SET_OTHER_POWER, 0); + GpioControl::setInt(CMD_SET_485_EN2, 0); + } + return 0; +} diff --git a/app/src/main/cpp/PhoneDevice.h b/app/src/main/cpp/PhoneDevice.h index bfa6e16a..c0ad2ee1 100644 --- a/app/src/main/cpp/PhoneDevice.h +++ b/app/src/main/cpp/PhoneDevice.h @@ -161,7 +161,9 @@ public: virtual bool on_image(cv::Mat& rgb); virtual void on_error(const std::string& msg); virtual void onDisconnected(ACameraDevice* device); - virtual bool onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, std::vector >& frames); + virtual bool onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, uint32_t duration, std::vector >& frames); + virtual bool onOneCapture(std::shared_ptr characteristics, std::shared_ptr results, uint32_t ldr, uint32_t duration, cv::Mat rgb); + virtual bool onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, uint32_t duration, std::vector >& frames); protected: CPhoneDevice* m_dev; @@ -174,7 +176,9 @@ public: virtual void onImageAvailable(AImageReader* reader); virtual int32_t getOutputFormat() const; - virtual bool onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, std::vector >& frames); + virtual bool onOneCapture(std::shared_ptr characteristics, std::shared_ptr results, uint32_t ldr, uint32_t duration, cv::Mat rgb); + virtual bool onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, uint32_t duration, std::vector >& frames); + virtual bool onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, uint32_t duration, std::vector >& frames); protected: std::string m_path; @@ -190,7 +194,7 @@ public: unsigned long uid; }; - CPhoneDevice(JavaVM* vm, jobject service, const std::string& appPath, unsigned int netId, unsigned int versionCode); + CPhoneDevice(JavaVM* vm, jobject service, const std::string& appPath, unsigned int netId, unsigned int versionCode, const std::string& nativeLibDir); virtual ~CPhoneDevice(); virtual void SetListener(IListener* listener); @@ -201,7 +205,7 @@ public: virtual bool UpdateSchedules(); virtual bool QuerySystemProperties(map& properties); virtual bool InstallAPP(const std::string& path, unsigned int delayedTime); - virtual bool Reboot(int resetType); + virtual bool Reboot(int resetType, const std::string& reason); virtual bool EnableGPS(bool enabled); virtual float QueryBattaryVoltage(int timesForAvg, bool* isCharging); virtual bool RequestPosition(); @@ -215,15 +219,14 @@ public: virtual int GetWData(WEATHER_INFO *weatherInfo); virtual int GetIceData(ICE_INFO *iceInfo, ICE_TAIL *icetail, SENSOR_PARAM *sensorParam); - virtual bool OpenSensors(); - virtual bool CloseSensors(); + virtual bool OpenSensors(int sensortype); + virtual bool CloseSensors(int sensortype); bool GetNextScheduleItem(uint32_t tsBasedZero, uint32_t scheduleTime, vector& items); void UpdatePosition(double lon, double lat, double radius, time_t ts); bool OnVideoReady(bool photoOrVideo, bool result, const char* path, unsigned int photoId); bool OnCaptureReady(bool photoOrVideo, bool result, cv::Mat& mat, unsigned int photoId); - bool ProcessRawCapture(bool result, int numberOfCaptures, const std::string& pathsJoinedByTab, bool frontCamera, int rotation, long photoId); void UpdateSignalLevel(int signalLevel); void UpdateTfCardPath(const std::string& tfCardPath) @@ -275,7 +278,9 @@ protected: std::string QueryCpuTemperature(); bool OnImageReady(cv::Mat& mat); - bool onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, std::vector >& frames); + bool onOneCapture(std::shared_ptr characteristics, std::shared_ptr results, uint32_t ldr, uint32_t duration, cv::Mat rgb); + bool onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, uint32_t duration, std::vector >& frames); + bool onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, uint32_t duration, std::vector >& frames); void onError(const std::string& msg); void onDisconnected(ACameraDevice* device); @@ -287,10 +292,12 @@ protected: void handleTimerImpl(TIMER_CONTEXT* context); void static handleRebootTimer(union sigval v); // void handleRebootTimerImpl(); - void RestartApp(int rebootType, long timeout); + void RestartApp(int rebootType, long timeout, const std::string& reason); int QueryBatteryVoltage(int retries); + int CallExecv(int rotation, int frontCamera, const std::string& outputPath, const std::vector& images); + protected: std::mutex m_devLocker; @@ -299,6 +306,7 @@ protected: jobject m_javaService; std::string m_appPath; std::string m_tfCardPath; + std::string m_nativeLibraryDir; jmethodID mRegisterHeartbeatMid; jmethodID mUpdateCaptureScheduleMid; @@ -314,6 +322,9 @@ protected: jmethodID mInstallAppMid; jmethodID mEnableGpsMid; jmethodID mRequestPositionMid; + jmethodID mExecHdrplusMid; + + jmethodID mCallSysCameraMid; std::string mPath; IDevice::PHOTO_INFO mPhotoInfo; @@ -327,6 +338,7 @@ protected: atomic_ulong m_timerUidFeed; atomic_ulong m_wakelockIdFeed; + atomic_ulong m_uniqueIdFeed; std::map mTimers; mutable CPhoneCamera* mCamera; diff --git a/app/src/main/cpp/SensorsProtocol.cpp b/app/src/main/cpp/SensorsProtocol.cpp index b6b8e094..6508fcbe 100644 --- a/app/src/main/cpp/SensorsProtocol.cpp +++ b/app/src/main/cpp/SensorsProtocol.cpp @@ -70,18 +70,26 @@ int getInt(int cmd) } static void setRS485Enable(bool z) { +#ifndef USING_N938 setInt(CMD_SET_485_EN_STATE, z ? 1 : 0); +#endif } static void set485WriteMode() { +#ifndef USING_N938 setInt(CMD_SET_485_STATE, 1); +#endif } static void set485ReadMode() { +#ifndef USING_N938 setInt(CMD_SET_485_STATE, 0); +#endif } static void set12VEnable(bool z) { +#ifndef USING_N938 setInt(CMD_SET_12V_EN_STATE, z ? 1 : 0); +#endif } static void setCam3V3Enable(bool enabled) @@ -595,25 +603,25 @@ void Gm_CloseSensorsPower() #endif #if 1 setInt(CMD_SET_SPI_POWER, 1); - setInt(CMD_SET_485_en0, 1); - setInt(CMD_SET_485_en1, 1); - setInt(CMD_SET_485_en2, 1); - setInt(CMD_SET_485_en3, 1); - setInt(CMD_SET_485_en4, 1); + setInt(CMD_SET_485_EN0, 1); + setInt(CMD_SET_485_EN1, 1); + setInt(CMD_SET_485_EN2, 1); + setInt(CMD_SET_485_EN3, 1); + setInt(CMD_SET_485_EN4, 1); #else setInt(CMD_SET_SPI_POWER, 0); - setInt(CMD_SET_485_en0, 0); - setInt(CMD_SET_485_en1, 0); - setInt(CMD_SET_485_en2, 0); - setInt(CMD_SET_485_en3, 0); - setInt(CMD_SET_485_en4, 0); + setInt(CMD_SET_485_EN0, 0); + setInt(CMD_SET_485_EN1, 0); + setInt(CMD_SET_485_EN2, 0); + setInt(CMD_SET_485_EN3, 0); + setInt(CMD_SET_485_EN4, 0); sleep(3); igpio = getInt(CMD_SET_SPI_POWER); - igpio = getInt(CMD_SET_485_en0); - igpio = getInt(CMD_SET_485_en1); - igpio = getInt(CMD_SET_485_en2); - igpio = getInt(CMD_SET_485_en3); - igpio = getInt(CMD_SET_485_en4); + igpio = getInt(CMD_SET_485_EN0); + igpio = getInt(CMD_SET_485_EN1); + igpio = getInt(CMD_SET_485_EN2); + igpio = getInt(CMD_SET_485_EN3); + igpio = getInt(CMD_SET_485_EN4); #endif */ } @@ -655,26 +663,26 @@ void Gm_OpenSensorsPower() #endif #if 1 setInt(CMD_SET_SPI_POWER, 1); - setInt(CMD_SET_485_en0, 1); - setInt(CMD_SET_485_en1, 1); - setInt(CMD_SET_485_en2, 1); - setInt(CMD_SET_485_en3, 1); - setInt(CMD_SET_485_en4, 1); + setInt(CMD_SET_485_EN0, 1); + setInt(CMD_SET_485_EN1, 1); + setInt(CMD_SET_485_EN2, 1); + setInt(CMD_SET_485_EN3, 1); + setInt(CMD_SET_485_EN4, 1); //sleep(3); igpio = getInt(CMD_SET_SPI_POWER); - igpio = getInt(CMD_SET_485_en0); - igpio = getInt(CMD_SET_485_en1); - igpio = getInt(CMD_SET_485_en2); - igpio = getInt(CMD_SET_485_en3); - igpio = getInt(CMD_SET_485_en4); + igpio = getInt(CMD_SET_485_EN0); + igpio = getInt(CMD_SET_485_EN1); + igpio = getInt(CMD_SET_485_EN2); + igpio = getInt(CMD_SET_485_EN3); + igpio = getInt(CMD_SET_485_EN4); #else - setInt(CMD_SET_485_en0, 0); - setInt(CMD_SET_485_en1, 0); - setInt(CMD_SET_485_en2, 0); - setInt(CMD_SET_485_en3, 0); - setInt(CMD_SET_485_en4, 0); + setInt(CMD_SET_485_EN0, 0); + setInt(CMD_SET_485_EN1, 0); + setInt(CMD_SET_485_EN2, 0); + setInt(CMD_SET_485_EN3, 0); + setInt(CMD_SET_485_EN4, 0); #endif // 打开电源 @@ -1282,6 +1290,7 @@ void GM_StartSerialComm() { if (i == srdt.camerauseserial) continue; + serialport[i].Retry = 0; serialport[i].RetryTime = 800; serialport[i].WaitTime = 20; diff --git a/app/src/main/cpp/SerialComm.cpp b/app/src/main/cpp/SerialComm.cpp index 4222349b..8a06ee94 100644 --- a/app/src/main/cpp/SerialComm.cpp +++ b/app/src/main/cpp/SerialComm.cpp @@ -51,16 +51,16 @@ static void set_parity (struct termios *opt, char parity) { switch (parity) { - case'N':/* 无校验 */ + case 'N':/* 无校验 */ case 'n': opt->c_cflag &= ~PARENB; break; - case'E':/*偶校验*/ + case 'E':/*偶校验*/ case 'e': opt->c_cflag |= PARENB; opt->c_cflag &= ~PARODD; break; - case'O':/* 奇校验 */ + case 'O':/* 奇校验 */ case 'o': opt->c_cflag |= PARENB; opt->c_cflag |= ~PARODD; diff --git a/app/src/main/cpp/camera2/ndkcamera.cpp b/app/src/main/cpp/camera2/ndkcamera.cpp index e644d18c..5386fe90 100644 --- a/app/src/main/cpp/camera2/ndkcamera.cpp +++ b/app/src/main/cpp/camera2/ndkcamera.cpp @@ -28,6 +28,16 @@ #include #include "DngCreator.h" + +#ifdef _DEBUG +void Auto_AImage_delete(AImage* image) +{ + AImage_delete(image); +} +#else +#define Auto_AImage_delete AImage_delete +#endif + static void onAvailabilityCallback(void* context, const char* cameraId) { ((NdkCamera*)context)->onAvailabilityCallback(cameraId); @@ -100,6 +110,48 @@ void onCaptureCompleted(void* context, ACameraCaptureSession* session, ACaptureR ((NdkCamera*)context)->onCaptureCompleted(session, request, result); } +inline uint8_t GetCaptureIntent(ACameraDevice_request_template templateId) +{ + /* + ACAMERA_CONTROL_CAPTURE_INTENT_CUSTOM = 0, + ACAMERA_CONTROL_CAPTURE_INTENT_PREVIEW = 1, + ACAMERA_CONTROL_CAPTURE_INTENT_STILL_CAPTURE = 2, + ACAMERA_CONTROL_CAPTURE_INTENT_VIDEO_RECORD = 3, + ACAMERA_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT = 4, + ACAMERA_CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG = 5, + ACAMERA_CONTROL_CAPTURE_INTENT_MANUAL = 6, + ACAMERA_CONTROL_CAPTURE_INTENT_MOTION_TRACKING = 7, + */ + + uint8_t captureIntent = ACAMERA_CONTROL_CAPTURE_INTENT_STILL_CAPTURE; + switch (templateId) + { + case TEMPLATE_PREVIEW: // = 1, + captureIntent = ACAMERA_CONTROL_CAPTURE_INTENT_PREVIEW; + break; + case TEMPLATE_STILL_CAPTURE: // = 2, + captureIntent = ACAMERA_CONTROL_CAPTURE_INTENT_STILL_CAPTURE; + break; + case TEMPLATE_RECORD: // = 3, + captureIntent = ACAMERA_CONTROL_CAPTURE_INTENT_VIDEO_RECORD; + break; + case TEMPLATE_VIDEO_SNAPSHOT: // = 4, + captureIntent = ACAMERA_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT; + break; + case TEMPLATE_ZERO_SHUTTER_LAG: // = 5, + captureIntent = ACAMERA_CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG; + break; + case TEMPLATE_MANUAL: // = 6, + captureIntent = ACAMERA_CONTROL_CAPTURE_INTENT_MANUAL; + break; + default: + captureIntent = ACAMERA_CONTROL_CAPTURE_INTENT_STILL_CAPTURE; + break; + } + + return captureIntent; +} + NdkCamera::NdkCamera(int32_t width, int32_t height, const NdkCamera::CAMERA_PARAMS& params) { camera_facing = 0; @@ -118,6 +170,7 @@ NdkCamera::NdkCamera(int32_t width, int32_t height, const NdkCamera::CAMERA_PARA awbMode = ACAMERA_CONTROL_AWB_MODE_AUTO; aeLockAvailable = false; awbLockAvailable = false; + m_fatalError = false; sceneModeSupported = false; @@ -138,10 +191,17 @@ NdkCamera::NdkCamera(int32_t width, int32_t height, const NdkCamera::CAMERA_PARA mPreviewImageReader = NULL; mPreviewImageWindow = NULL; mPreviewOutputTarget = NULL; + mPreviewSessionOutput = NULL; mImageReader = NULL; mImageWindow = NULL; mOutputTarget = NULL; + mSessionOutput = NULL; + + mImageReader2 = NULL; + mImageWindow2 = NULL; + mOutputTarget2 = NULL; + mSessionOutput2 = NULL; camera_device = 0; @@ -152,6 +212,13 @@ NdkCamera::NdkCamera(int32_t width, int32_t height, const NdkCamera::CAMERA_PARA mResult = { 0 }; mLdr = ~0; + mFinalLdr = 0; + mFinalBurstCaptures = m_params.burstRawCapture == 0 ? 1 : m_params.burstCaptures; + if (mFinalBurstCaptures == 0) + { + mFinalBurstCaptures = 1; + } + mFinalOutputFormat = (m_params.burstRawCapture == 0) ? AIMAGE_FORMAT_YUV_420_888 : AIMAGE_FORMAT_RAW16; } NdkCamera::~NdkCamera() @@ -223,6 +290,8 @@ int NdkCamera::selfTest(const std::string& cameraId, int32_t& maxResolutionX, in } } + ACameraMetadata_free(camera_metadata); + return 0; } @@ -239,6 +308,8 @@ int NdkCamera::open(const std::string& cameraId) { DisplayDimension disp(mWidth, mHeight); DisplayDimension foundRes = disp; camera_status_t status = ACAMERA_OK; + int32_t previewWidth = 0; + int32_t previewHeight = 0; ALOGD("Start ACameraManager_getCameraIdList"); { @@ -296,24 +367,52 @@ int NdkCamera::open(const std::string& cameraId) { if (input) continue; int32_t format = e.data.i32[i + 0]; - if (format == AIMAGE_FORMAT_YUV_420_888/* || format == AIMAGE_FORMAT_JPEG*/) + if (format == AIMAGE_FORMAT_RAW16) { - DisplayDimension res(e.data.i32[i + 1], e.data.i32[i + 2]); - // XYLOG(XYLOG_SEVERITY_DEBUG, "CameraId=%s CX=%d CY=%d", cameraId.c_str(), res.width(), res.height()); - if (!disp.IsSameRatio(res)) + if (mFinalOutputFormat == AIMAGE_FORMAT_RAW16) { - - if (res.width() >= mWidth && res.height() >= mHeight) + DisplayDimension res(e.data.i32[i + 1], e.data.i32[i + 2]); + if (!disp.IsSameRatio(res)) { - temp = res; + if (res.width() >= mWidth && res.height() >= mHeight) + { + temp = res; + } + continue; } - continue; + if (res > disp) + { + foundIt = true; + foundRes = res; + } + } + } + else if (format == AIMAGE_FORMAT_YUV_420_888/* || format == AIMAGE_FORMAT_JPEG*/) + { + if (previewWidth == 0 || previewHeight == 0) + { + previewWidth = e.data.i32[i + 1]; + previewHeight = e.data.i32[i + 2]; } - if (/*format == AIMAGE_FORMAT_YUV_420_888 && */res > disp) + if (mFinalOutputFormat == AIMAGE_FORMAT_YUV_420_888) { - foundIt = true; - foundRes = res; + DisplayDimension res(e.data.i32[i + 1], e.data.i32[i + 2]); + // XYLOG(XYLOG_SEVERITY_DEBUG, "CameraId=%s CX=%d CY=%d", cameraId.c_str(), res.width(), res.height()); + if (!disp.IsSameRatio(res)) + { + if (res.width() >= mWidth && res.height() >= mHeight) + { + temp = res; + } + continue; + } + + if (/*format == AIMAGE_FORMAT_YUV_420_888 && */res > disp) + { + foundIt = true; + foundRes = res; + } } } } @@ -556,6 +655,10 @@ int NdkCamera::open(const std::string& cameraId) { status = ACameraManager_openCamera(camera_manager, cameraId.c_str(), &camera_device_state_callbacks, &camera_device); if (status != ACAMERA_OK) { + if (status == ACAMERA_ERROR_MAX_CAMERA_IN_USE) + { + m_fatalError = true; + } XYLOG(XYLOG_SEVERITY_ERROR, "Failed to open camera %s res=%d", cameraId.c_str(), status); return 1; } @@ -572,7 +675,8 @@ int NdkCamera::open(const std::string& cameraId) { } // setup imagereader and its surface - media_status_t mstatus = AImageReader_new(foundRes.org_width(), foundRes.org_height(), AIMAGE_FORMAT_YUV_420_888, 5, &mPreviewImageReader); + media_status_t mstatus = AImageReader_new(previewWidth, previewHeight, AIMAGE_FORMAT_YUV_420_888, 4, &mPreviewImageReader); + AASSERT(status == ACAMERA_OK, "Failed to call AImageReader_new preview, status=%d", status); if (mstatus == AMEDIA_OK) { AImageReader_ImageListener listener; @@ -587,7 +691,8 @@ int NdkCamera::open(const std::string& cameraId) { status = ACaptureSessionOutput_create(mPreviewImageWindow, &mPreviewSessionOutput); status = ACaptureSessionOutputContainer_add(capture_session_output_container, mPreviewSessionOutput); - mstatus = AImageReader_new(foundRes.org_width(), foundRes.org_height(), getOutputFormat(), burstCaptures + 2, &mImageReader); + mstatus = AImageReader_new(foundRes.org_width(), foundRes.org_height(), getOutputFormat(), burstCaptures + 1, &mImageReader); + AASSERT(status == ACAMERA_OK, "Failed to call AImageReader_new, status=%d", status); if (mstatus == AMEDIA_OK) { AImageReader_ImageListener listener; @@ -598,11 +703,33 @@ int NdkCamera::open(const std::string& cameraId) { ANativeWindow_acquire(mImageWindow); } status = ACameraOutputTarget_create(mImageWindow, &mOutputTarget); + AASSERT(status == ACAMERA_OK, "Failed to call ACameraOutputTarget_create, status=%d", status); status = ACaptureSessionOutput_create(mImageWindow, &mSessionOutput); + AASSERT(status == ACAMERA_OK, "Failed to call ACaptureSessionOutput_create, status=%d", status); + status = ACaptureSessionOutputContainer_add(capture_session_output_container, mSessionOutput); + if (m_params.burstRawCapture == 1) // Auto + { + mstatus = AImageReader_new(foundRes.org_width(), foundRes.org_height(), AIMAGE_FORMAT_YUV_420_888, burstCaptures, &mImageReader2); + if (mstatus == AMEDIA_OK) + { + AImageReader_ImageListener listener; + listener.context = this; + listener.onImageAvailable = ::onImageAvailable; + mstatus = AImageReader_setImageListener(mImageReader2, &listener); + mstatus = AImageReader_getWindow(mImageReader2, &mImageWindow2); + ANativeWindow_acquire(mImageWindow2); + } + status = ACameraOutputTarget_create(mImageWindow2, &mOutputTarget2); + + status = ACaptureSessionOutput_create(mImageWindow2, &mSessionOutput2); + status = ACaptureSessionOutputContainer_add(capture_session_output_container, mSessionOutput2); + } + + CaptureRequest *request = CreateRequest(true); mCaptureRequests.push_back(request); #if 0 @@ -627,7 +754,7 @@ int NdkCamera::open(const std::string& cameraId) { uint8_t ctrlMode = ACAMERA_CONTROL_MODE_AUTO; status = ACaptureRequest_setEntry_u8(request->request, ACAMERA_CONTROL_MODE, 1, &ctrlMode); - uint8_t captureIntent = isPreviewRequest ? ACAMERA_CONTROL_CAPTURE_INTENT_PREVIEW : ACAMERA_CONTROL_CAPTURE_INTENT_STILL_CAPTURE; + uint8_t captureIntent = isPreviewRequest ? ACAMERA_CONTROL_CAPTURE_INTENT_PREVIEW : GetCaptureIntent(ACameraDevice_request_template)m_params.requestTemplate); status = ACaptureRequest_setEntry_u8(request->request, ACAMERA_CONTROL_CAPTURE_INTENT, 1, &captureIntent); uint8_t flashMode = ACAMERA_FLASH_MODE_OFF; @@ -808,6 +935,7 @@ int NdkCamera::open(const std::string& cameraId) { camera_capture_session_state_callbacks.onReady = ::onSessionReady; camera_capture_session_state_callbacks.onClosed = onSessionClosed; status = ACameraDevice_createCaptureSession(camera_device, capture_session_output_container, &camera_capture_session_state_callbacks, &capture_session); + AASSERT(status == ACAMERA_OK, "Failed to call ACameraDevice_createCaptureSession, status=%d", status); ACameraCaptureSession_captureCallbacks capture_session_capture_callbacks; capture_session_capture_callbacks.context = this; @@ -820,7 +948,7 @@ int NdkCamera::open(const std::string& cameraId) { capture_session_capture_callbacks.onCaptureBufferLost = 0; status = ACameraCaptureSession_setRepeatingRequest(capture_session, &capture_session_capture_callbacks, 1, &(mCaptureRequests[PREVIEW_REQUEST_IDX]->request), &(mCaptureRequests[PREVIEW_REQUEST_IDX]->sessionSequenceId)); - + AASSERT(status == ACAMERA_OK, "Failed to call ACameraCaptureSession_setRepeatingRequest, status=%d", status); ALOGW("Preview Request: seqId=%d", mCaptureRequests[PREVIEW_REQUEST_IDX]->sessionSequenceId); m_startTime = GetMicroTimeStamp(); @@ -837,24 +965,26 @@ NdkCamera::CaptureRequest* NdkCamera::CreateRequest(bool isPreviewRequest) CaptureRequest *request = new CaptureRequest(); std::memset(request, 0, sizeof(CaptureRequest)); + bool autoSwitchToOneFrame = (m_params.burstRawCapture == 1) && (mFinalOutputFormat == AIMAGE_FORMAT_YUV_420_888); request->pThis = this; - request->imageReader = isPreviewRequest ? mPreviewImageReader : mImageReader; - request->imageWindow = isPreviewRequest ? mPreviewImageWindow : mImageWindow; - request->imageTarget = isPreviewRequest ? mPreviewOutputTarget : mOutputTarget; - request->sessionOutput = isPreviewRequest ? mPreviewSessionOutput : mSessionOutput; + request->imageReader = isPreviewRequest ? mPreviewImageReader : (autoSwitchToOneFrame ? mImageReader2 : mImageReader); + request->imageWindow = isPreviewRequest ? mPreviewImageWindow : (autoSwitchToOneFrame ? mImageWindow2 : mImageWindow); + request->imageTarget = isPreviewRequest ? mPreviewOutputTarget : (autoSwitchToOneFrame ? mOutputTarget2 : mOutputTarget); + request->sessionOutput = isPreviewRequest ? mPreviewSessionOutput : (autoSwitchToOneFrame ? mSessionOutput2 : mSessionOutput); request->templateId = isPreviewRequest ? TEMPLATE_PREVIEW : (ACameraDevice_request_template)m_params.requestTemplate; // mCaptureRequests.push_back(request); // capture request status = ACameraDevice_createCaptureRequest(camera_device, request->templateId, &request->request); + AASSERT(status == ACAMERA_OK, "Failed to call ACameraDevice_createCaptureRequest, status=%d", status); ACaptureRequest_setUserContext(request->request, request); // uint8_t ctrlMode = sceneModeSupported ? ACAMERA_CONTROL_MODE_USE_SCENE_MODE : ACAMERA_CONTROL_MODE_AUTO; uint8_t ctrlMode = ACAMERA_CONTROL_MODE_AUTO; status = ACaptureRequest_setEntry_u8(request->request, ACAMERA_CONTROL_MODE, 1, &ctrlMode); - uint8_t captureIntent = isPreviewRequest ? ACAMERA_CONTROL_CAPTURE_INTENT_PREVIEW : ACAMERA_CONTROL_CAPTURE_INTENT_STILL_CAPTURE; + uint8_t captureIntent = isPreviewRequest ? ACAMERA_CONTROL_CAPTURE_INTENT_PREVIEW : GetCaptureIntent((ACameraDevice_request_template)m_params.requestTemplate); status = ACaptureRequest_setEntry_u8(request->request, ACAMERA_CONTROL_CAPTURE_INTENT, 1, &captureIntent); uint8_t flashMode = ACAMERA_FLASH_MODE_OFF; @@ -1022,6 +1152,7 @@ NdkCamera::CaptureRequest* NdkCamera::CreateRequest(bool isPreviewRequest) } status = ACaptureRequest_addTarget(request->request, request->imageTarget); + AASSERT(status == ACAMERA_OK, "Failed to call ACaptureRequest_addTarget, status=%d", status); // status = ACaptureSessionOutput_create(request->imageWindow, &request->sessionOutput); // status = ACaptureSessionOutputContainer_add(capture_session_output_container, request->sessionOutput); @@ -1034,6 +1165,21 @@ void NdkCamera::close() XYLOG(XYLOG_SEVERITY_DEBUG, "CameraStatus::try close %s", mCameraId.c_str()); camera_status_t res = ACAMERA_OK; + /* + if (mPreviewImageReader != NULL) + { + AImageReader_setImageListener(mPreviewImageReader, NULL); + } + if (mImageReader != NULL) + { + AImageReader_setImageListener(mImageReader, NULL); + } + if (mImageReader2 != NULL) + { + AImageReader_setImageListener(mImageReader2, NULL); + } + */ + mCaptureFrames.clear(); if ((ACameraManager *)camera_manager != NULL) @@ -1085,7 +1231,7 @@ void NdkCamera::close() if (mPreviewImageReader != NULL) { - // AImageReader_setImageListener(image_reader, NULL); + AImageReader_setImageListener(mPreviewImageReader, NULL); //XYLOG(XYLOG_SEVERITY_DEBUG, "CameraStatus::AImageReader_delete %s", mCameraId.c_str()); AImageReader_delete(mPreviewImageReader); //XYLOG(XYLOG_SEVERITY_DEBUG, "CameraStatus::End AImageReader_delete %s", mCameraId.c_str()); @@ -1107,7 +1253,7 @@ void NdkCamera::close() if (mImageReader != NULL) { - // AImageReader_setImageListener(image_reader, NULL); + AImageReader_setImageListener(mImageReader, NULL); //XYLOG(XYLOG_SEVERITY_DEBUG, "CameraStatus::AImageReader_delete %s", mCameraId.c_str()); AImageReader_delete(mImageReader); //XYLOG(XYLOG_SEVERITY_DEBUG, "CameraStatus::End AImageReader_delete %s", mCameraId.c_str()); @@ -1115,6 +1261,28 @@ void NdkCamera::close() mImageReader = 0; } + if (mOutputTarget2 != NULL) + { + ACameraOutputTarget_free(mOutputTarget2); + mOutputTarget2 = 0; + } + + if (mImageWindow2 != NULL) + { + ANativeWindow_release(mImageWindow2); + mImageWindow2 = 0; + } + + if (mImageReader2 != NULL) + { + AImageReader_setImageListener(mImageReader2, NULL); + //XYLOG(XYLOG_SEVERITY_DEBUG, "CameraStatus::AImageReader_delete %s", mCameraId.c_str()); + AImageReader_delete(mImageReader2); + //XYLOG(XYLOG_SEVERITY_DEBUG, "CameraStatus::End AImageReader_delete %s", mCameraId.c_str()); + + mImageReader2 = 0; + } + if (mPreviewSessionOutput != NULL) { if (capture_session_output_container) @@ -1134,6 +1302,15 @@ void NdkCamera::close() ACaptureSessionOutput_free(mSessionOutput); mSessionOutput = 0; } + if (mSessionOutput2 != NULL) + { + if (capture_session_output_container) + { + ACaptureSessionOutputContainer_remove(capture_session_output_container, mSessionOutput2); + } + ACaptureSessionOutput_free(mSessionOutput2); + mSessionOutput2 = 0; + } if (capture_session_output_container) { @@ -1165,12 +1342,12 @@ void NdkCamera::onImageAvailable(AImageReader* reader) // https://stackoverflow.com/questions/67063562 if (mstatus != AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE) { - XYLOG(XYLOG_SEVERITY_ERROR, "AImageReader_acquireLatestImage error: %d", mstatus); + XYLOG(XYLOG_SEVERITY_ERROR, "Preview AImageReader_acquireLatestImage error: %d", mstatus); } return; } - if (mLdr == ~0) + if (!mCaptureTriggered) { uint8_t* y_data = 0; int y_len = 0; @@ -1182,7 +1359,9 @@ void NdkCamera::onImageAvailable(AImageReader* reader) uint64_t avgY = std::accumulate(y_data, y_data + y_len, 0); #endif avgY = avgY / (uint64_t)y_len; - mLdr = avgY; + m_locker.lock(); + mLdr = (uint8_t)avgY; + m_locker.unlock(); } AImage_delete(image); @@ -1190,7 +1369,12 @@ void NdkCamera::onImageAvailable(AImageReader* reader) } else { - while (1) + uint32_t burstCaptures = getBurstCaptures(); + if (burstCaptures == 0) + { + burstCaptures = 1; + } + if (burstCaptures == 1) { mstatus = AImageReader_acquireNextImage(reader, &image); if (mstatus != AMEDIA_OK) @@ -1198,32 +1382,154 @@ void NdkCamera::onImageAvailable(AImageReader* reader) // https://stackoverflow.com/questions/67063562 if (mstatus != AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE) { - if (mCaptureFrames.size() < m_params.burstCaptures) - { - XYLOG(XYLOG_SEVERITY_ERROR, "AImageReader_acquireNextImage error: %d", mstatus); - } + if (mCaptureFrames.size() < burstCaptures) + { + XYLOG(XYLOG_SEVERITY_ERROR, "Capture AImageReader_acquireNextImage error: %d", mstatus); + } } - break; + return; } + unsigned long long ts = GetMicroTimeStamp(); + + int32_t format; + mstatus = AImage_getFormat(image, &format); + + if (format == AIMAGE_FORMAT_YUV_420_888) + { + int32_t width; + int32_t height; + mstatus = AImage_getWidth(image, &width); + mstatus = AImage_getHeight(image, &height); + + int32_t y_pixelStride = 0; + int32_t u_pixelStride = 0; + int32_t v_pixelStride = 0; + AImage_getPlanePixelStride(image, 0, &y_pixelStride); + AImage_getPlanePixelStride(image, 1, &u_pixelStride); + AImage_getPlanePixelStride(image, 2, &v_pixelStride); + + int32_t y_rowStride = 0; + int32_t u_rowStride = 0; + int32_t v_rowStride = 0; + AImage_getPlaneRowStride(image, 0, &y_rowStride); + AImage_getPlaneRowStride(image, 1, &u_rowStride); + AImage_getPlaneRowStride(image, 2, &v_rowStride); + + uint8_t* y_data = 0; + uint8_t* u_data = 0; + uint8_t* v_data = 0; + int y_len = 0; + int u_len = 0; + int v_len = 0; + AImage_getPlaneData(image, 0, &y_data, &y_len); + AImage_getPlaneData(image, 1, &u_data, &u_len); + AImage_getPlaneData(image, 2, &v_data, &v_len); + + if (u_data == v_data + 1 && v_data == y_data + width * height && y_pixelStride == 1 && u_pixelStride == 2 && v_pixelStride == 2 && y_rowStride == width && u_rowStride == width && v_rowStride == width) + { + // already nv21 + ConvertYUV21ToMat(y_data, width, height, mWidth, mHeight, camera_orientation, + camera_facing == ACAMERA_LENS_FACING_FRONT, m_params.orientation, mOneFrame); + } + else + { + // construct nv21 + uint8_t* nv21 = new uint8_t[width * height + width * height / 2]; + { + // Y + uint8_t* yptr = nv21; + for (int y = 0; y < height; y++) + { + const uint8_t* y_data_ptr = y_data + y_rowStride * y; + for (int x = 0; x < width; x++) + { + yptr[0] = y_data_ptr[0]; + yptr++; + y_data_ptr += y_pixelStride; + } + } + + // UV + uint8_t* uvptr = nv21 + width * height; + for (int y = 0; y < height / 2; y++) + { + const uint8_t* v_data_ptr = v_data + v_rowStride * y; + const uint8_t* u_data_ptr = u_data + u_rowStride * y; + for (int x = 0; x < width / 2; x++) + { + uvptr[0] = v_data_ptr[0]; + uvptr[1] = u_data_ptr[0]; + uvptr += 2; + v_data_ptr += v_pixelStride; + u_data_ptr += u_pixelStride; + } + } + } + + ConvertYUV21ToMat(nv21, width, height,mWidth, mHeight, camera_orientation, + camera_facing == ACAMERA_LENS_FACING_FRONT, m_params.orientation, mOneFrame); + + delete[] nv21; + } + } m_photoTaken = true; - m_locker.lock(); - mCaptureFrames.push_back(std::shared_ptr(image, AImage_delete)); - m_locker.unlock(); - ALOGD("Capture Image Received"); + AImage_delete(image); + + std::shared_ptr result; + bool captureCompleted = false; + m_locker.lock(); + if (!mCaptureResults.empty()) + { + captureCompleted = true; + result = mCaptureResults[0]; + } + m_locker.unlock(); + + if (captureCompleted) + { + onOneCapture(mCharacteristics, result, mFinalLdr, ts - m_startTime, mOneFrame); + } } + else + { + while (1) + { + mstatus = AImageReader_acquireNextImage(reader, &image); + if (mstatus != AMEDIA_OK) + { + // https://stackoverflow.com/questions/67063562 + if (mstatus != AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE) + { + if (mCaptureFrames.size() < burstCaptures) + { + XYLOG(XYLOG_SEVERITY_ERROR, "AImageReader_acquireNextImage error: %d", mstatus); + } + } + break; + } - bool captureCompleted = false; - size_t expectedTimes = mCaptureRequests.size() - 1; - m_locker.lock(); - captureCompleted = mCaptureResults.size() >= expectedTimes && mCaptureFrames.size() >= expectedTimes; - m_locker.unlock(); + m_photoTaken = true; + m_locker.lock(); + mCaptureFrames.push_back(std::shared_ptr(image, Auto_AImage_delete)); + m_locker.unlock(); + + ALOGD("Capture Image Received"); + } + + bool captureCompleted = false; + size_t expectedTimes = mCaptureRequests.size() - 1; + m_locker.lock(); + captureCompleted = mCaptureResults.size() >= expectedTimes && mCaptureFrames.size() >= expectedTimes; + m_locker.unlock(); + + if (captureCompleted) + { + FireBurstCapture(); + } + } - if (captureCompleted) - { - onBurstCapture(mCharacteristics, mCaptureResults, mLdr, mCaptureFrames); - } } } @@ -1240,7 +1546,17 @@ bool NdkCamera::on_image(cv::Mat& rgb) return false; } -bool NdkCamera::onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, std::vector >& frames) +bool NdkCamera::onOneCapture(std::shared_ptr characteristics, std::shared_ptr result, uint32_t ldr, uint32_t duration, cv::Mat rgb) +{ + return false; +} + +bool NdkCamera::onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, uint32_t duration, std::vector >& frames) +{ + return false; +} + +bool NdkCamera::onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, uint32_t duration, std::vector >& frames) { return false; } @@ -1449,6 +1765,7 @@ void NdkCamera::onCaptureCompleted(ACameraCaptureSession* session, ACaptureReque aePrecatureTrigger = ACAMERA_CONTROL_AE_PRECAPTURE_TRIGGER_START; status = ACaptureRequest_setEntry_u8(request, ACAMERA_CONTROL_AE_PRECAPTURE_TRIGGER, 1, &aePrecatureTrigger); //XYLOG(XYLOG_SEVERITY_DEBUG, "Trigger PRECAPTURE status=%d AES=%u", (int)status, (unsigned int)mResult.aeState); + AASSERT(status == ACAMERA_OK, "Failed to call PRECAPTURE_TRIGGER, status=%d", status); readyForCapture = false; numberOfPrecaptures = 0; @@ -1522,8 +1839,23 @@ void NdkCamera::onCaptureCompleted(ACameraCaptureSession* session, ACaptureReque if (readyForCapture/* && mCaptureRequests.size() > 1*/) { - ALOGW("Ready for Capture AFS=%u AES=%u AWBS=%u Time=%u", - (unsigned int)afState, (unsigned int)aeState, (unsigned int)awbState, (unsigned int)(ts - m_startTime)); + // Must update mFinalLdr As getBurstCaptures getOutputFormat depends mFinalLdr + if (mLdr != ~0) + { + mFinalLdr = mLdr; + } + + XYLOG(XYLOG_SEVERITY_INFO, "Ready for Capture AFS=%u AES=%u AWBS=%u LDR=%u Time=%u", + (unsigned int)afState, (unsigned int)aeState, (unsigned int)awbState, mFinalLdr, (unsigned int)(ts - m_startTime)); + if (m_params.burstRawCapture == 1) + { + if (mFinalLdr > 50) + { + XYLOG(XYLOG_SEVERITY_WARNING, "Switch to OneFrame Capture(YUV) As LDR=%u", mFinalLdr); + mFinalOutputFormat = AIMAGE_FORMAT_YUV_420_888; + mFinalBurstCaptures = 1; + } + } uint32_t burstCaptures = getBurstCaptures(); if (burstCaptures == 0) @@ -1544,7 +1876,7 @@ void NdkCamera::onCaptureCompleted(ACameraCaptureSession* session, ACaptureReque } // ALOGW("Will Stop Repeating Request"); - status = ACameraCaptureSession_stopRepeating(capture_session); + // status = ACameraCaptureSession_stopRepeating(capture_session); // ALOGW("Finished Repeating Request"); ACameraCaptureSession_captureCallbacks capture_session_capture_cb; @@ -1560,6 +1892,7 @@ void NdkCamera::onCaptureCompleted(ACameraCaptureSession* session, ACaptureReque int numberOfRequests = requests.size(); status = ACameraCaptureSession_capture(capture_session, &capture_session_capture_cb, numberOfRequests, &requests[0], &sequenceId); + AASSERT(status == ACAMERA_OK, "Failed to call ACameraCaptureSession_capture, status=%d", status); ALOGW("Capture num = %d sequenceId=%d", numberOfRequests, sequenceId); for (int idx = 1; idx < mCaptureRequests.size(); idx++) @@ -1572,24 +1905,101 @@ void NdkCamera::onCaptureCompleted(ACameraCaptureSession* session, ACaptureReque } else { +#ifdef _DEBUG uint64_t tid = getThreadIdOfULL(); ALOGW("Capture Result sequenceId=%d TID=%lld", pCaptureRequest->sessionSequenceId, (long long)tid); +#endif + + unsigned long long ts = GetMicroTimeStamp(); ACameraMetadata* pCopy = ACameraMetadata_copy(result); bool captureCompleted = false; size_t expectedTimes = mCaptureRequests.size() - 1; - m_locker.lock(); - mCaptureResults.push_back(std::shared_ptr(pCopy, ACameraMetadata_free)); - captureCompleted = mCaptureResults.size() >= expectedTimes && mCaptureFrames.size() >= expectedTimes; - m_locker.unlock(); - if (captureCompleted) - { - onBurstCapture(mCharacteristics, mCaptureResults, mLdr, mCaptureFrames); - } + std::shared_ptr captureResult(pCopy, ACameraMetadata_free); + if (expectedTimes == 1) + { + m_locker.lock(); + mCaptureResults.push_back(captureResult); + captureCompleted = !mOneFrame.empty(); + m_locker.unlock(); + + if (captureCompleted) + { + onOneCapture(mCharacteristics, captureResult, mFinalLdr, ts - m_startTime, mOneFrame); + } + } + else + { + m_locker.lock(); + mCaptureResults.push_back(captureResult); + captureCompleted = mCaptureFrames.size() >= expectedTimes && mCaptureResults.size() >= expectedTimes; + m_locker.unlock(); + + if (captureCompleted) + { + FireBurstCapture(); + } + } + } } +void NdkCamera::FireBurstCapture() +{ + unsigned long long ts = GetMicroTimeStamp(); + + size_t expectedTimes = mCaptureRequests.size() - 1; + std::vector > captureResults; + uint32_t ldr; + std::vector > captureFrames; + + m_locker.lock(); + ldr = mFinalLdr; + if (ldr == 0 && mLdr != ~0) + { + ldr = mLdr; + } + captureResults.swap(mCaptureResults); + captureFrames.swap(mCaptureFrames); + m_locker.unlock(); + + media_status_t mstatus; + std::vector > frames; + for (int idx = 0; idx < expectedTimes; idx++) + { + std::shared_ptr spImage = captureFrames[idx]; + std::shared_ptr spResult = captureResults[idx]; + + auto it = frames.insert(frames.end(), std::vector()); + + int32_t width = 0; + int32_t height = 0; + mstatus = AImage_getWidth(spImage.get(), &width); + mstatus = AImage_getHeight(spImage.get(), &height); + + int32_t planeCount = 0; + mstatus = AImage_getNumberOfPlanes(spImage.get(), &planeCount); + AASSERT(mstatus == AMEDIA_OK && planeCount == 1, "Error: getNumberOfPlanes() planeCount = %d", planeCount); + + uint8_t *planeData = NULL; + int planeDataLen = 0; + mstatus = AImage_getPlaneData(spImage.get(), 0, &planeData, &planeDataLen); + ALOGD("Start Converting Dng"); + DngCreator dngCreator(mCharacteristics.get(), spResult.get()); + dngCreator.writeInputBuffer(*it, planeData, planeDataLen, width, height, 0); + ALOGD("End Converting Dng"); + } + + captureFrames.clear(); + + onBurstCapture(mCharacteristics, captureResults, ldr, ts - m_startTime, frames); + +#ifdef _DEBUG + ALOGD("Frames Size: %u", (uint32_t)frames.size()); +#endif +} + void NdkCamera::CopyPreviewRequest(ACaptureRequest* request, const ACameraMetadata* previewResult) { camera_status_t status = ACAMERA_ERROR_BASE; @@ -1622,9 +2032,13 @@ void NdkCamera::CopyPreviewRequest(ACaptureRequest* request, const ACameraMetada void NdkCamera::onCaptureFailed(ACameraCaptureSession* session, ACaptureRequest* request, ACameraCaptureFailure* failure) { - XYLOG(XYLOG_SEVERITY_WARNING, "onCaptureFailed session=%p request=%p reason=%d", session, request, failure->reason); + XYLOG(XYLOG_SEVERITY_WARNING, "onCaptureFailed session=%p request=%p reason=%d PhotoTaken=%d", session, request, failure->reason, m_photoTaken ? 1 : 0); - char msg[32] = { 0 }; + if (failure->sequenceId == mCaptureRequests[PREVIEW_REQUEST_IDX]->sessionSequenceId) + { + return; + } + char msg[64] = { 0 }; snprintf(msg, sizeof(msg), "CaptureFailed reason=%d PhotoTaken=%d", failure->reason, m_photoTaken ? 1 : 0); if (!m_photoTaken) { @@ -1674,12 +2088,13 @@ bool NdkCamera::IsCameraAvailable(const std::string& cameraId) int32_t NdkCamera::getOutputFormat() const { - return m_params.burstRawCapture ? AIMAGE_FORMAT_RAW16 : AIMAGE_FORMAT_YUV_420_888; + return mFinalOutputFormat; + // return m_params.burstRawCapture ? AIMAGE_FORMAT_RAW16 : AIMAGE_FORMAT_YUV_420_888; } int32_t NdkCamera::getBurstCaptures() const { - return m_params.burstRawCapture ? m_params.burstCaptures : 1; + return mFinalBurstCaptures; } void NdkCamera::CreateSession(ANativeWindow* previewWindow, @@ -1912,7 +2327,6 @@ bool NdkCamera::convertAImageToNv21(AImage* image, uint8_t** nv21, int32_t& widt } } - // on_image((unsigned ch } } diff --git a/app/src/main/cpp/camera2/ndkcamera.h b/app/src/main/cpp/camera2/ndkcamera.h index 96d5ec07..6c17096c 100644 --- a/app/src/main/cpp/camera2/ndkcamera.h +++ b/app/src/main/cpp/camera2/ndkcamera.h @@ -81,8 +81,8 @@ public: unsigned int orientation:3; unsigned int zoom : 1; unsigned int wait3ALocked : 3; - unsigned int burstRawCapture : 1; - unsigned int reserved : 18; + unsigned int burstRawCapture : 2; + unsigned int reserved : 16; int64_t exposureTime; unsigned int sensitivity; int compensation; @@ -166,7 +166,10 @@ public: virtual void on_error(const std::string& msg); virtual void on_image(const unsigned char* nv21, int nv21_width, int nv21_height); virtual void onDisconnected(ACameraDevice* device); - virtual bool onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, std::vector >& frames); + + virtual bool onOneCapture(std::shared_ptr characteristics, std::shared_ptr result, uint32_t ldr, uint32_t duration, cv::Mat rgb); + virtual bool onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, uint32_t duration, std::vector >& frames); + virtual bool onBurstCapture(std::shared_ptr characteristics, std::vector >& results, uint32_t ldr, uint32_t duration, std::vector >& frames); void onCaptureProgressed(ACameraCaptureSession* session, ACaptureRequest* request, const ACameraMetadata* result); void onCaptureCompleted(ACameraCaptureSession* session, ACaptureRequest* request, const ACameraMetadata* result); @@ -176,9 +179,16 @@ public: void CopyPreviewRequest(ACaptureRequest* request, const ACameraMetadata* previewResult); + void FireBurstCapture(); + uint32_t GetLdr() const { - return mLdr; + return mFinalLdr; + } + + bool HasFatalError() const + { + return m_fatalError; } bool IsCameraAvailable(const std::string& cameraId); @@ -206,6 +216,7 @@ protected: uint8_t awbMode; bool aeLockAvailable; bool awbLockAvailable; + bool m_fatalError; uint64_t numberOfPrecaptures; unsigned long long m_precaptureStartTime; @@ -244,14 +255,25 @@ protected: ACameraOutputTarget* mOutputTarget; ACaptureSessionOutput* mSessionOutput; + AImageReader* mImageReader2; + ANativeWindow* mImageWindow2; + ACameraOutputTarget* mOutputTarget2; + ACaptureSessionOutput* mSessionOutput2; + std::shared_ptr mCharacteristics; std::vector mCaptureRequests; std::shared_ptr mPreviewResults; std::vector > mCaptureResults; uint32_t mLdr; + uint32_t mFinalLdr; + uint32_t mFinalBurstCaptures; + int32_t mFinalOutputFormat; std::vector > mCaptureFrames; + cv::Mat mOneFrame; + std::vector > mRawFrames; + ACameraCaptureSession* capture_session; // AImageReader* image_reader; diff --git a/app/src/main/cpp/hdrplus/bin/hdrplus.cpp b/app/src/main/cpp/hdrplus/bin/hdrplus.cpp new file mode 100644 index 00000000..a01d398d --- /dev/null +++ b/app/src/main/cpp/hdrplus/bin/hdrplus.cpp @@ -0,0 +1,72 @@ +#include "hdrplus/hdrplus_pipeline.h" + +int main( int argc, char** argv ) +{ + int rotation = atoi(argv[1]); + bool frontCamera = atoi(argv[2]) != 0; + + std::vector paths; + for (int idx = 4; idx < argc; idx++) + { + paths.push_back(argv[idx]); + } + + cv::Mat mat; + + hdrplus::hdrplus_pipeline pipeline; + pipeline.run_pipeline( paths, 0, mat); + + if (mat.empty()) + { + printf("run_pipeline return empty mat"); + } + mat = hdrplus::convert16bit2_8bit_(mat.clone()); + + if (rotation > 0) + { + if (rotation == 1) // 0 + { + cv::Mat tempPic; + cv::transpose(mat, tempPic); + cv::flip(tempPic, mat, 0); + } + else if (rotation == 2) // 90 + { + cv::Mat tempPic; + cv::transpose(mat, tempPic); + cv::flip(tempPic, mat, 1); + } + else if (rotation == 3) // 180 + { + if (frontCamera) + { + cv::flip(mat, mat, 0); + } + else + { + cv::flip(mat, mat, -1); + } + } + else if (rotation == 4) // 270 + { + cv::Mat tempPic; + cv::transpose(mat, tempPic); + cv::flip(tempPic, mat, 0); + } + + } + cv::cvtColor(mat, mat, cv::COLOR_RGB2BGR); + + if (mat.empty()) + { + printf("mat is empty before save"); + } + + bool res = cv::imwrite(argv[3], mat); + if (!res) + { + printf("Failed to write file %s err=%d", argv[3], errno); + } + + return 0; +} \ No newline at end of file diff --git a/app/src/main/cpp/hdrplus/include/hdrplus/bayer_image.h b/app/src/main/cpp/hdrplus/include/hdrplus/bayer_image.h index 93230cf2..e2e5059f 100644 --- a/app/src/main/cpp/hdrplus/include/hdrplus/bayer_image.h +++ b/app/src/main/cpp/hdrplus/include/hdrplus/bayer_image.h @@ -10,11 +10,29 @@ namespace hdrplus { + class MemFile + { + public: + std::vector content; + + const std::vector GetConstData() const + { + return content; + } + + std::vector GetData() + { + return content; + } + }; + class bayer_image { public: explicit bayer_image( const std::string& bayer_image_path ); - explicit bayer_image( const std::vector& bayer_image_content ); + explicit bayer_image( const std::vector& bayer_image_content ); + explicit bayer_image( std::shared_ptr bayer_image_file ); + ~bayer_image() = default; std::pair get_noise_params() const; diff --git a/app/src/main/cpp/hdrplus/include/hdrplus/burst.h b/app/src/main/cpp/hdrplus/include/hdrplus/burst.h index bde3c04f..33d2f1cd 100644 --- a/app/src/main/cpp/hdrplus/include/hdrplus/burst.h +++ b/app/src/main/cpp/hdrplus/include/hdrplus/burst.h @@ -8,12 +8,14 @@ namespace hdrplus { + class burst { public: explicit burst( const std::string& burst_path, const std::string& reference_image_path ); explicit burst(const std::vector& burst_paths, int reference_image_index); explicit burst( const std::vector >& bayer_image_contents, int reference_image_index ); + explicit burst( const std::vector >& bayer_image_files, int reference_image_index ); ~burst() = default; diff --git a/app/src/main/cpp/hdrplus/include/hdrplus/finish.h b/app/src/main/cpp/hdrplus/include/hdrplus/finish.h index 7fc68ae1..e70ee277 100644 --- a/app/src/main/cpp/hdrplus/include/hdrplus/finish.h +++ b/app/src/main/cpp/hdrplus/include/hdrplus/finish.h @@ -33,10 +33,14 @@ class finish bayer_image* refBayer; std::string mergedImgPath; - finish() = default; + finish() + { + refBayer = NULL; + } // please use this initialization after merging part finish - finish(std::string burstPath, cv::Mat mergedBayer,int refIdx){ + finish(std::string burstPath, cv::Mat mergedBayer,int refIdx) { + refBayer = NULL; this->refIdx = refIdx; this->burstPath = burstPath; this->mergedBayer = mergedBayer; @@ -60,7 +64,14 @@ class finish - ~finish() = default; + ~finish() + { + if (refBayer != NULL) + { + delete refBayer; + refBayer = NULL; + } + } // finish pipeline func // void process(std::string burstPath, cv::Mat mergedBayer,int refIdx); diff --git a/app/src/main/cpp/hdrplus/include/hdrplus/hdrplus_pipeline.h b/app/src/main/cpp/hdrplus/include/hdrplus/hdrplus_pipeline.h index 3d6f478c..17dd789d 100644 --- a/app/src/main/cpp/hdrplus/include/hdrplus/hdrplus_pipeline.h +++ b/app/src/main/cpp/hdrplus/include/hdrplus/hdrplus_pipeline.h @@ -10,7 +10,31 @@ namespace hdrplus { -class hdrplus_pipeline + inline cv::Mat convert16bit2_8bit_(cv::Mat ans) { + if(ans.type()==CV_16UC3){ + cv::MatIterator_ it, end; + for( it = ans.begin(), end = ans.end(); it != end; ++it) + { + // std::cout<& burst_paths, int reference_image_index, cv::Mat& finalImg ); bool run_pipeline( const std::vector >& burst_contents, int reference_image_index, cv::Mat& finalImg ); + bool run_pipeline( const std::vector >& burst_contents, int reference_image_index, cv::Mat& finalImg ); hdrplus_pipeline() = default; ~hdrplus_pipeline() = default; diff --git a/app/src/main/cpp/hdrplus/src/align.cpp b/app/src/main/cpp/hdrplus/src/align.cpp index cc2d9219..e217bf34 100644 --- a/app/src/main/cpp/hdrplus/src/align.cpp +++ b/app/src/main/cpp/hdrplus/src/align.cpp @@ -107,7 +107,7 @@ static void build_per_grayimg_pyramid( \ break; default: #ifdef __ANDROID__ - + break; #else throw std::runtime_error("inv scale factor " + std::to_string( inv_scale_factors[ i ]) + "invalid" ); #endif @@ -987,6 +987,8 @@ void align::process( const hdrplus::burst& burst_images, \ } // for alternative image + per_grayimg_pyramid.clear(); + } } // namespace hdrplus diff --git a/app/src/main/cpp/hdrplus/src/bayer_image.cpp b/app/src/main/cpp/hdrplus/src/bayer_image.cpp index 0d84199a..cc29ffb2 100644 --- a/app/src/main/cpp/hdrplus/src/bayer_image.cpp +++ b/app/src/main/cpp/hdrplus/src/bayer_image.cpp @@ -141,6 +141,74 @@ bayer_image::bayer_image( const std::vector& bayer_image_content ) #endif } + bayer_image::bayer_image( std::shared_ptr bayer_image_file ) + { + libraw_processor = std::make_shared(); + + // Open RAW image file + int return_code; + { + std::vector& fileData = bayer_image_file->content; + if ( ( return_code = libraw_processor->open_buffer( (void *)(&fileData[0]), fileData.size() ) ) != LIBRAW_SUCCESS ) + { + libraw_processor->recycle(); +#ifdef __ANDROID__ + return; +#else + throw std::runtime_error("Error opening file " + bayer_image_path + " " + libraw_strerror( return_code )); +#endif + } + } + + + // Unpack the raw image + if ( ( return_code = libraw_processor->unpack() ) != LIBRAW_SUCCESS ) + { +#ifdef __ANDROID__ + return; +#else + throw std::runtime_error("Error unpack file " + bayer_image_path + " " + libraw_strerror( return_code )); +#endif + } + + // Get image basic info + width = int( libraw_processor->imgdata.rawdata.sizes.raw_width ); + height = int( libraw_processor->imgdata.rawdata.sizes.raw_height ); + + // Read exif tags + Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(&bayer_image_file->content[0], bayer_image_file->content.size()); + assert(image.get() != 0); + image->readMetadata(); + Exiv2::ExifData &exifData = image->exifData(); + if (exifData.empty()) { + std::string error = "No Exif data found in the file"; + std::cout << error << std::endl; + } + + white_level = exifData["Exif.Image.WhiteLevel"].toLong(); + black_level_per_channel.resize( 4 ); + black_level_per_channel.at(0) = exifData["Exif.Image.BlackLevel"].toLong(0); + black_level_per_channel.at(1) = exifData["Exif.Image.BlackLevel"].toLong(1); + black_level_per_channel.at(2) = exifData["Exif.Image.BlackLevel"].toLong(2); + black_level_per_channel.at(3) = exifData["Exif.Image.BlackLevel"].toLong(3); + iso = exifData["Exif.Image.ISOSpeedRatings"].toLong(); + + // Create CV mat + // https://answers.opencv.org/question/105972/de-bayering-a-cr2-image/ + // https://www.libraw.org/node/2141 + raw_image = cv::Mat( height, width, CV_16U, libraw_processor->imgdata.rawdata.raw_image ).clone(); // changed the order of width and height + + // 2x2 box filter + grayscale_image = box_filter_kxk( raw_image ); + +#ifndef NDEBUG + printf("%s::%s read bayer image with\n width %zu\n height %zu\n iso %.3f\n white level %d\n black level %d %d %d %d\n", \ + __FILE__, __func__, width, height, iso, white_level, \ + black_level_per_channel[0], black_level_per_channel[1], black_level_per_channel[2], black_level_per_channel[3] ); + fflush( stdout ); +#endif + } + std::pair bayer_image::get_noise_params() const { // Set ISO to 100 if not positive diff --git a/app/src/main/cpp/hdrplus/src/burst.cpp b/app/src/main/cpp/hdrplus/src/burst.cpp index 08ab324c..c0c83c89 100644 --- a/app/src/main/cpp/hdrplus/src/burst.cpp +++ b/app/src/main/cpp/hdrplus/src/burst.cpp @@ -248,4 +248,74 @@ burst::burst( const std::vector >& bayer_image_contents, in #endif } +burst::burst( const std::vector >& bayer_image_files, int reference_image_index ) +{ + // Number of images + num_images = bayer_image_files.size(); + + // Find reference image path in input directory + // reference image path need to be absolute path + reference_image_idx = -1; + if ( reference_image_index >= 0 && reference_image_index < bayer_image_files.size() ) + { + reference_image_idx = reference_image_index; + } + + if ( reference_image_idx == -1 ) + { + return; + // throw std::runtime_error("Error reference image index is out of range " ); + } + +#ifndef NDEBUG + printf("%s::%s reference image idx %d\n", \ + __FILE__, __func__, reference_image_idx ); +#endif + + // Get source bayer image + // Downsample original bayer image by 2x2 box filter + for ( const auto& bayer_image_file : bayer_image_files ) + { + bayer_images.emplace_back( bayer_image_file ); + } + + // Pad information + int tile_size_bayer = 32; + int padding_top = tile_size_bayer / 2; + int padding_bottom = tile_size_bayer / 2 + \ + ( (bayer_images[ 0 ].height % tile_size_bayer) == 0 ? \ + 0 : tile_size_bayer - bayer_images[ 0 ].height % tile_size_bayer ); + int padding_left = tile_size_bayer / 2; + int padding_right = tile_size_bayer / 2 + \ + ( (bayer_images[ 0 ].width % tile_size_bayer) == 0 ? \ + 0 : tile_size_bayer - bayer_images[ 0 ].width % tile_size_bayer ); + padding_info_bayer = std::vector{ padding_top, padding_bottom, padding_left, padding_right }; + + // Pad bayer image + for ( const auto& bayer_image_i : bayer_images ) + { + cv::Mat bayer_image_pad_i; + cv::copyMakeBorder( bayer_image_i.raw_image, \ + bayer_image_pad_i, \ + padding_top, padding_bottom, padding_left, padding_right, \ + cv::BORDER_REFLECT ); + + // cv::Mat use internal reference count + bayer_images_pad.emplace_back( bayer_image_pad_i ); + grayscale_images_pad.emplace_back( box_filter_kxk( bayer_image_pad_i ) ); + } + +#ifndef NDEBUG + printf("%s::%s Pad bayer image from (%d, %d) -> (%d, %d)\n", \ + __FILE__, __func__, \ + bayer_images[ 0 ].height, \ + bayer_images[ 0 ].width, \ + bayer_images_pad[ 0 ].size().height, \ + bayer_images_pad[ 0 ].size().width ); + printf("%s::%s pad top %d, buttom %d, left %d, right %d\n", \ + __FILE__, __func__, \ + padding_top, padding_bottom, padding_left, padding_right ); +#endif +} + } // namespace hdrplus diff --git a/app/src/main/cpp/hdrplus/src/finish.cpp b/app/src/main/cpp/hdrplus/src/finish.cpp index 42f82fb2..2284856e 100644 --- a/app/src/main/cpp/hdrplus/src/finish.cpp +++ b/app/src/main/cpp/hdrplus/src/finish.cpp @@ -523,6 +523,7 @@ namespace hdrplus // } cv::Mat processMergedMat(cv::Mat mergedImg, int opencv_type){ cv::Mat m; +#if 0 uint16_t* ptr = (uint16_t*)mergedImg.data; for(int r = 0; r < mergedImg.rows; r++) { std::vector dvals; @@ -533,13 +534,14 @@ namespace hdrplus cv::transpose(mline, mline); m.push_back(mline); } +#endif int ch = CV_MAT_CN(opencv_type); + m = mergedImg.clone(); m = m.reshape(ch); m.convertTo(m, opencv_type); return m; - } void show20_20(cv::Mat m){ @@ -565,17 +567,17 @@ namespace hdrplus std::cout<<"finish pipeline start ..."<refIdx = burst_images.reference_image_idx; // this->burstPath = burstPath; // std::cout<<"processMerged:"<mergedBayer = loadFromCSV(DBG_OUTPUT_ROOT "merged.csv", CV_16UC1); #ifndef HDRPLUS_NO_DETAILED_OUTPUT + this->mergedBayer = loadFromCSV(DBG_OUTPUT_ROOT "merged.csv", CV_16UC1); // this->mergedBayer = processMergedMat(mergedB,CV_16UC1);//loadFromCSV("merged.csv", CV_16UC1); // std::cout<<"processMerged:"<mergedBayer); @@ -583,7 +585,7 @@ namespace hdrplus // this->mergedBayer = processMergedMat(burst_images.merged_bayer_image, CV_16UC1); #else // this->mergedBayer = loadFromCSV(DBG_OUTPUT_ROOT "merged.csv", CV_16UC1); - // this->mergedBayer = processMergedMat(burst_images.merged_bayer_image, CV_16UC1); + this->mergedBayer = processMergedMat(burst_images.merged_bayer_image, CV_16UC1); // std::cout<<"processMerged:"< #ifdef __ANDROID__ -#include +// #include #endif namespace hdrplus @@ -46,25 +46,25 @@ bool hdrplus_pipeline::run_pipeline( \ burst burst_images( burst_paths, reference_image_index ); std::vector>>> alignments; #ifdef __ANDROID__ - ALOGI("Finish loading images"); + // ALOGI("Finish loading images"); #endif // Run align align_module.process( burst_images, alignments ); #ifdef __ANDROID__ - ALOGI("Finish align"); + // ALOGI("Finish align"); #endif // Run merging merge_module.process( burst_images, alignments ); #ifdef __ANDROID__ - ALOGI("Finish merging"); + // ALOGI("Finish merging"); #endif // Run finishing finish_module.process( burst_images, finalImg); #ifdef __ANDROID__ - ALOGI("Finish process"); + // ALOGI("Finish process"); #endif return true; @@ -78,28 +78,61 @@ bool hdrplus_pipeline::run_pipeline( \ burst burst_images( burst_contents, reference_image_index ); std::vector>>> alignments; #ifdef __ANDROID__ - ALOGI("Finish loading images"); + // ALOGI("Finish loading images"); #endif // Run align align_module.process( burst_images, alignments ); #ifdef __ANDROID__ - ALOGI("Finish align"); + // ALOGI("Finish align"); #endif // Run merging merge_module.process( burst_images, alignments ); #ifdef __ANDROID__ - ALOGI("Finish merging"); + // ALOGI("Finish merging"); #endif // Run finishing finish_module.process( burst_images, finalImg); #ifdef __ANDROID__ - ALOGI("Finish process"); + // ALOGI("Finish process"); #endif return true; } + + bool hdrplus_pipeline::run_pipeline( \ + const std::vector >& burst_files, \ + int reference_image_index, cv::Mat& finalImg ) + { + // Create burst of images + burst burst_images( burst_files, reference_image_index ); + std::vector>>> alignments; +#ifdef __ANDROID__ + // ALOGI("Finish loading images"); +#endif + + // Run align + align_module.process( burst_images, alignments ); +#ifdef __ANDROID__ + // ALOGI("Finish align"); +#endif + + // Run merging + merge_module.process( burst_images, alignments ); +#ifdef __ANDROID__ + // ALOGI("Finish merging"); +#endif + + // Run finishing + finish_module.process( burst_images, finalImg); +#ifdef __ANDROID__ + // ALOGI("Finish process"); +#endif + + return true; + } + } // namespace hdrplus diff --git a/app/src/main/cpp/hdrplus/src/merge.cpp b/app/src/main/cpp/hdrplus/src/merge.cpp index bdf5c400..e908bf0a 100644 --- a/app/src/main/cpp/hdrplus/src/merge.cpp +++ b/app/src/main/cpp/hdrplus/src/merge.cpp @@ -20,7 +20,9 @@ namespace hdrplus // Get padded bayer image cv::Mat reference_image = burst_images.bayer_images_pad[burst_images.reference_image_idx]; - cv::imwrite("ref.jpg", reference_image); +#ifndef NDEBUG + // cv::imwrite("ref.jpg", reference_image); +#endif // Get raw channels std::vector channels(4); @@ -98,7 +100,7 @@ namespace hdrplus cv::Range horizontal = cv::Range(padding[2], reference_image.cols - padding[3]); cv::Range vertical = cv::Range(padding[0], reference_image.rows - padding[1]); burst_images.merged_bayer_image = merged(vertical, horizontal); - cv::imwrite("merged.jpg", burst_images.merged_bayer_image); + // cv::imwrite("merged.jpg", burst_images.merged_bayer_image); } std::vector merge::getReferenceTiles(cv::Mat reference_image) { diff --git a/app/src/main/cpp/hdrplus2/arm64-v8a/hdrplus_pipeline.h b/app/src/main/cpp/hdrplus2/arm64-v8a/hdrplus_pipeline.h new file mode 100644 index 00000000..b2bc86a4 --- /dev/null +++ b/app/src/main/cpp/hdrplus2/arm64-v8a/hdrplus_pipeline.h @@ -0,0 +1,2282 @@ +#ifndef HALIDE______arm64___v8a___hdrplus_pipeline_h +#define HALIDE______arm64___v8a___hdrplus_pipeline_h +#include + +// Forward declarations of the types used in the interface +// to the Halide pipeline. +// +// Definitions for these structs are below. + +// Halide's representation of a multi-dimensional array. +// Halide::Runtime::Buffer is a more user-friendly wrapper +// around this. Its declaration is in HalideBuffer.h +struct halide_buffer_t; + +// Metadata describing the arguments to the generated function. +// Used to construct calls to the _argv version of the function. +struct halide_filter_metadata_t; + +#ifndef HALIDE_MUST_USE_RESULT +#ifdef __has_attribute +#if __has_attribute(nodiscard) +#define HALIDE_MUST_USE_RESULT [[nodiscard]] +#elif __has_attribute(warn_unused_result) +#define HALIDE_MUST_USE_RESULT __attribute__((warn_unused_result)) +#else +#define HALIDE_MUST_USE_RESULT +#endif +#else +#define HALIDE_MUST_USE_RESULT +#endif +#endif + +#ifndef HALIDE_FUNCTION_ATTRS +#define HALIDE_FUNCTION_ATTRS +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + +HALIDE_FUNCTION_ATTRS +int hdrplus_pipeline(struct halide_buffer_t *_inputs_buffer, uint16_t _black_point, uint16_t _white_point, float _white_balance_r, float _white_balance_g0, float _white_balance_g1, float _white_balance_b, int32_t _cfa_pattern, struct halide_buffer_t *_ccm_buffer, float _compression, float _gain, struct halide_buffer_t *_8bit_interleaved_output_buffer); + +HALIDE_FUNCTION_ATTRS +int hdrplus_pipeline_argv(void **args); + +HALIDE_FUNCTION_ATTRS +const struct halide_filter_metadata_t *hdrplus_pipeline_metadata(); + +#ifdef __cplusplus +} // extern "C" +#endif + + +// The generated object file that goes with this header +// includes a full copy of the Halide runtime so that it +// can be used standalone. Declarations for the functions +// in the Halide runtime are below. +// +// The runtime is defined using weak linkage, so it is legal +// to link multiple Halide-generated object files together, +// or to clobber any of these functions with your own +// definition. +// +// To generate an object file without a full copy of the +// runtime, use the -no_runtime target flag. To generate a +// standalone Halide runtime to use with such object files +// use the -r flag with any Halide generator binary, e.g.: +// $ ./my_generator -r halide_runtime -o . target=host + +#ifndef HALIDE_HALIDERUNTIME_H +#define HALIDE_HALIDERUNTIME_H + +#ifndef COMPILING_HALIDE_RUNTIME +#ifdef __cplusplus +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif +#else +#include "runtime_internal.h" +#endif + +// Note that the canonical Halide version is considered to be defined here +// (rather than in the build system); we redundantly define the value in +// our CMake build, so that we ensure that the in-build metadata (eg soversion) +// matches, but keeping the canonical version here makes it easier to keep +// downstream build systems (eg Blaze/Bazel) properly in sync with the source. +#define HALIDE_VERSION_MAJOR 18 +#define HALIDE_VERSION_MINOR 0 +#define HALIDE_VERSION_PATCH 0 + +#ifdef __cplusplus +// Forward declare type to allow naming typed handles. +// See Type.h for documentation. +template +struct halide_handle_traits; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +// Note that (for MSVC) you should not use "inline" along with HALIDE_ALWAYS_INLINE; +// it is not necessary, and may produce warnings for some build configurations. +#define HALIDE_ALWAYS_INLINE __forceinline +#define HALIDE_NEVER_INLINE __declspec(noinline) +#else +// Note that (for Posixy compilers) you should always use "inline" along with HALIDE_ALWAYS_INLINE; +// otherwise some corner-case scenarios may erroneously report link errors. +#define HALIDE_ALWAYS_INLINE inline __attribute__((always_inline)) +#define HALIDE_NEVER_INLINE __attribute__((noinline)) +#endif + +#ifndef HALIDE_MUST_USE_RESULT +#ifdef __has_attribute +#if __has_attribute(nodiscard) +// C++17 or later +#define HALIDE_MUST_USE_RESULT [[nodiscard]] +#elif __has_attribute(warn_unused_result) +// Clang/GCC +#define HALIDE_MUST_USE_RESULT __attribute__((warn_unused_result)) +#else +#define HALIDE_MUST_USE_RESULT +#endif +#else +#define HALIDE_MUST_USE_RESULT +#endif +#endif + +// Annotation for AOT and JIT calls -- if undefined, use no annotation. +// To ensure that all results are checked, do something like +// +// -DHALIDE_FUNCTION_ATTRS=HALIDE_MUST_USE_RESULT +// +// in your C++ compiler options +#ifndef HALIDE_FUNCTION_ATTRS +#define HALIDE_FUNCTION_ATTRS +#endif + +#ifndef HALIDE_EXPORT_SYMBOL +#ifdef _MSC_VER +#define HALIDE_EXPORT_SYMBOL __declspec(dllexport) +#else +#define HALIDE_EXPORT_SYMBOL __attribute__((visibility("default"))) +#endif +#endif + +#ifndef COMPILING_HALIDE_RUNTIME + +// ASAN builds can cause linker errors for Float16, so sniff for that and +// don't enable it by default. +#if defined(__has_feature) +#if __has_feature(address_sanitizer) +#define HALIDE_RUNTIME_ASAN_DETECTED +#endif +#endif + +#if defined(__SANITIZE_ADDRESS__) && !defined(HALIDE_RUNTIME_ASAN_DETECTED) +#define HALIDE_RUNTIME_ASAN_DETECTED +#endif + +#if !defined(HALIDE_RUNTIME_ASAN_DETECTED) + +// clang had _Float16 added as a reserved name in clang 8, but +// doesn't actually support it on most platforms until clang 15. +// Ideally there would be a better way to detect if the type +// is supported, even in a compiler independent fashion, but +// coming up with one has proven elusive. +#if defined(__clang__) && (__clang_major__ >= 15) && !defined(__EMSCRIPTEN__) && !defined(__i386__) +#if defined(__is_identifier) +#if !__is_identifier(_Float16) +#define HALIDE_CPP_COMPILER_HAS_FLOAT16 +#endif +#endif +#endif + +// Similarly, detecting _Float16 for gcc is problematic. +// For now, we say that if >= v12, and compiling on x86 or arm, +// we assume support. This may need revision. +#if defined(__GNUC__) && (__GNUC__ >= 12) +#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__) +#define HALIDE_CPP_COMPILER_HAS_FLOAT16 +#endif +#endif + +#endif // !HALIDE_RUNTIME_ASAN_DETECTED + +#endif // !COMPILING_HALIDE_RUNTIME + +/** \file + * + * This file declares the routines used by Halide internally in its + * runtime. On platforms that support weak linking, these can be + * replaced with user-defined versions by defining an extern "C" + * function with the same name and signature. + * + * When doing Just In Time (JIT) compilation members of + * some_pipeline_or_func.jit_handlers() must be replaced instead. The + * corresponding methods are documented below. + * + * All of these functions take a "void *user_context" parameter as their + * first argument; if the Halide kernel that calls back to any of these + * functions has been compiled with the UserContext feature set on its Target, + * then the value of that pointer passed from the code that calls the + * Halide kernel is piped through to the function. + * + * Some of these are also useful to call when using the default + * implementation. E.g. halide_shutdown_thread_pool. + * + * Note that even on platforms with weak linking, some linker setups + * may not respect the override you provide. E.g. if the override is + * in a shared library and the halide object files are linked directly + * into the output, the builtin versions of the runtime functions will + * be called. See your linker documentation for more details. On + * Linux, LD_DYNAMIC_WEAK=1 may help. + * + */ + +// Forward-declare to suppress warnings if compiling as C. +struct halide_buffer_t; + +/** Print a message to stderr. Main use is to support tracing + * functionality, print, and print_when calls. Also called by the default + * halide_error. This function can be replaced in JITed code by using + * halide_custom_print and providing an implementation of halide_print + * in AOT code. See Func::set_custom_print. + */ +// @{ +extern void halide_print(void *user_context, const char *); +extern void halide_default_print(void *user_context, const char *); +typedef void (*halide_print_t)(void *, const char *); +extern halide_print_t halide_set_custom_print(halide_print_t print); +// @} + +/** Halide calls this function on runtime errors (for example bounds + * checking failures). This function can be replaced in JITed code by + * using Func::set_error_handler, or in AOT code by calling + * halide_set_error_handler. In AOT code on platforms that support + * weak linking (i.e. not Windows), you can also override it by simply + * defining your own halide_error. + */ +// @{ +extern void halide_error(void *user_context, const char *); +extern void halide_default_error(void *user_context, const char *); +typedef void (*halide_error_handler_t)(void *, const char *); +extern halide_error_handler_t halide_set_error_handler(halide_error_handler_t handler); +// @} + +/** Cross-platform mutex. Must be initialized with zero and implementation + * must treat zero as an unlocked mutex with no waiters, etc. + */ +struct halide_mutex { + uintptr_t _private[1]; +}; + +/** Cross platform condition variable. Must be initialized to 0. */ +struct halide_cond { + uintptr_t _private[1]; +}; + +/** A basic set of mutex and condition variable functions, which call + * platform specific code for mutual exclusion. Equivalent to posix + * calls. */ +//@{ +extern void halide_mutex_lock(struct halide_mutex *mutex); +extern void halide_mutex_unlock(struct halide_mutex *mutex); +extern void halide_cond_signal(struct halide_cond *cond); +extern void halide_cond_broadcast(struct halide_cond *cond); +extern void halide_cond_wait(struct halide_cond *cond, struct halide_mutex *mutex); +//@} + +/** Functions for constructing/destroying/locking/unlocking arrays of mutexes. */ +struct halide_mutex_array; +//@{ +extern struct halide_mutex_array *halide_mutex_array_create(uint64_t sz); +extern void halide_mutex_array_destroy(void *user_context, void *array); +extern int halide_mutex_array_lock(struct halide_mutex_array *array, int entry); +extern int halide_mutex_array_unlock(struct halide_mutex_array *array, int entry); +//@} + +/** Define halide_do_par_for to replace the default thread pool + * implementation. halide_shutdown_thread_pool can also be called to + * release resources used by the default thread pool on platforms + * where it makes sense. See Func::set_custom_do_task and + * Func::set_custom_do_par_for. Should return zero if all the jobs + * return zero, or an arbitrarily chosen return value from one of the + * jobs otherwise. + */ +//@{ +typedef int (*halide_task_t)(void *user_context, int task_number, uint8_t *closure); +extern int halide_do_par_for(void *user_context, + halide_task_t task, + int min, int size, uint8_t *closure); +extern void halide_shutdown_thread_pool(void); +//@} + +/** Set a custom method for performing a parallel for loop. Returns + * the old do_par_for handler. */ +typedef int (*halide_do_par_for_t)(void *, halide_task_t, int, int, uint8_t *); +extern halide_do_par_for_t halide_set_custom_do_par_for(halide_do_par_for_t do_par_for); + +/** An opaque struct representing a semaphore. Used by the task system for async tasks. */ +struct halide_semaphore_t { + uint64_t _private[2]; +}; + +/** A struct representing a semaphore and a number of items that must + * be acquired from it. Used in halide_parallel_task_t below. */ +struct halide_semaphore_acquire_t { + struct halide_semaphore_t *semaphore; + int count; +}; +extern int halide_semaphore_init(struct halide_semaphore_t *, int n); +extern int halide_semaphore_release(struct halide_semaphore_t *, int n); +extern bool halide_semaphore_try_acquire(struct halide_semaphore_t *, int n); +typedef int (*halide_semaphore_init_t)(struct halide_semaphore_t *, int); +typedef int (*halide_semaphore_release_t)(struct halide_semaphore_t *, int); +typedef bool (*halide_semaphore_try_acquire_t)(struct halide_semaphore_t *, int); + +/** A task representing a serial for loop evaluated over some range. + * Note that task_parent is a pass through argument that should be + * passed to any dependent taks that are invoked using halide_do_parallel_tasks + * underneath this call. */ +typedef int (*halide_loop_task_t)(void *user_context, int min, int extent, + uint8_t *closure, void *task_parent); + +/** A parallel task to be passed to halide_do_parallel_tasks. This + * task may recursively call halide_do_parallel_tasks, and there may + * be complex dependencies between seemingly unrelated tasks expressed + * using semaphores. If you are using a custom task system, care must + * be taken to avoid potential deadlock. This can be done by carefully + * respecting the static metadata at the end of the task struct.*/ +struct halide_parallel_task_t { + // The function to call. It takes a user context, a min and + // extent, a closure, and a task system pass through argument. + halide_loop_task_t fn; + + // The closure to pass it + uint8_t *closure; + + // The name of the function to be called. For debugging purposes only. + const char *name; + + // An array of semaphores that must be acquired before the + // function is called. Must be reacquired for every call made. + struct halide_semaphore_acquire_t *semaphores; + int num_semaphores; + + // The entire range the function should be called over. This range + // may be sliced up and the function called multiple times. + int min, extent; + + // A parallel task provides several pieces of metadata to prevent + // unbounded resource usage or deadlock. + + // The first is the minimum number of execution contexts (call + // stacks or threads) necessary for the function to run to + // completion. This may be greater than one when there is nested + // parallelism with internal producer-consumer relationships + // (calling the function recursively spawns and blocks on parallel + // sub-tasks that communicate with each other via semaphores). If + // a parallel runtime calls the function when fewer than this many + // threads are idle, it may need to create more threads to + // complete the task, or else risk deadlock due to committing all + // threads to tasks that cannot complete without more. + // + // FIXME: Note that extern stages are assumed to only require a + // single thread to complete. If the extern stage is itself a + // Halide pipeline, this may be an underestimate. + int min_threads; + + // The calls to the function should be in serial order from min to min+extent-1, with only + // one executing at a time. If false, any order is fine, and + // concurrency is fine. + bool serial; +}; + +/** Enqueue some number of the tasks described above and wait for them + * to complete. While waiting, the calling threads assists with either + * the tasks enqueued, or other non-blocking tasks in the task + * system. Note that task_parent should be NULL for top-level calls + * and the pass through argument if this call is being made from + * another task. */ +extern int halide_do_parallel_tasks(void *user_context, int num_tasks, + struct halide_parallel_task_t *tasks, + void *task_parent); + +/** If you use the default do_par_for, you can still set a custom + * handler to perform each individual task. Returns the old handler. */ +//@{ +typedef int (*halide_do_task_t)(void *, halide_task_t, int, uint8_t *); +extern halide_do_task_t halide_set_custom_do_task(halide_do_task_t do_task); +extern int halide_do_task(void *user_context, halide_task_t f, int idx, + uint8_t *closure); +//@} + +/** The version of do_task called for loop tasks. By default calls the + * loop task with the same arguments. */ +// @{ +typedef int (*halide_do_loop_task_t)(void *, halide_loop_task_t, int, int, uint8_t *, void *); +extern halide_do_loop_task_t halide_set_custom_do_loop_task(halide_do_loop_task_t do_task); +extern int halide_do_loop_task(void *user_context, halide_loop_task_t f, int min, int extent, + uint8_t *closure, void *task_parent); +//@} + +/** Provide an entire custom tasking runtime via function + * pointers. Note that do_task and semaphore_try_acquire are only ever + * called by halide_default_do_par_for and + * halide_default_do_parallel_tasks, so it's only necessary to provide + * those if you are mixing in the default implementations of + * do_par_for and do_parallel_tasks. */ +// @{ +typedef int (*halide_do_parallel_tasks_t)(void *, int, struct halide_parallel_task_t *, + void *task_parent); +extern void halide_set_custom_parallel_runtime( + halide_do_par_for_t, + halide_do_task_t, + halide_do_loop_task_t, + halide_do_parallel_tasks_t, + halide_semaphore_init_t, + halide_semaphore_try_acquire_t, + halide_semaphore_release_t); +// @} + +/** The default versions of the parallel runtime functions. */ +// @{ +extern int halide_default_do_par_for(void *user_context, + halide_task_t task, + int min, int size, uint8_t *closure); +extern int halide_default_do_parallel_tasks(void *user_context, + int num_tasks, + struct halide_parallel_task_t *tasks, + void *task_parent); +extern int halide_default_do_task(void *user_context, halide_task_t f, int idx, + uint8_t *closure); +extern int halide_default_do_loop_task(void *user_context, halide_loop_task_t f, + int min, int extent, + uint8_t *closure, void *task_parent); +extern int halide_default_semaphore_init(struct halide_semaphore_t *, int n); +extern int halide_default_semaphore_release(struct halide_semaphore_t *, int n); +extern bool halide_default_semaphore_try_acquire(struct halide_semaphore_t *, int n); +// @} + +struct halide_thread; + +/** Spawn a thread. Returns a handle to the thread for the purposes of + * joining it. The thread must be joined in order to clean up any + * resources associated with it. */ +extern struct halide_thread *halide_spawn_thread(void (*f)(void *), void *closure); + +/** Join a thread. */ +extern void halide_join_thread(struct halide_thread *); + +/** Set the number of threads used by Halide's thread pool. Returns + * the old number. + * + * n < 0 : error condition + * n == 0 : use a reasonable system default (typically, number of cpus online). + * n == 1 : use exactly one thread; this will always enforce serial execution + * n > 1 : use a pool of exactly n threads. + * + * (Note that this is only guaranteed when using the default implementations + * of halide_do_par_for(); custom implementations may completely ignore values + * passed to halide_set_num_threads().) + */ +extern int halide_set_num_threads(int n); + +/** Halide calls these functions to allocate and free memory. To + * replace in AOT code, use the halide_set_custom_malloc and + * halide_set_custom_free, or (on platforms that support weak + * linking), simply define these functions yourself. In JIT-compiled + * code use Func::set_custom_allocator. + * + * If you override them, and find yourself wanting to call the default + * implementation from within your override, use + * halide_default_malloc/free. + * + * Note that halide_malloc must return a pointer aligned to the + * maximum meaningful alignment for the platform for the purpose of + * vector loads and stores, *and* with an allocated size that is (at least) + * an integral multiple of that same alignment. The default implementation + * uses 32-byte alignment on arm and 64-byte alignment on x86. Additionally, + * it must be safe to read at least 8 bytes before the start and beyond the end. + */ +//@{ +extern void *halide_malloc(void *user_context, size_t x); +extern void halide_free(void *user_context, void *ptr); +extern void *halide_default_malloc(void *user_context, size_t x); +extern void halide_default_free(void *user_context, void *ptr); +typedef void *(*halide_malloc_t)(void *, size_t); +typedef void (*halide_free_t)(void *, void *); +extern halide_malloc_t halide_set_custom_malloc(halide_malloc_t user_malloc); +extern halide_free_t halide_set_custom_free(halide_free_t user_free); +//@} + +/** Halide calls these functions to interact with the underlying + * system runtime functions. To replace in AOT code on platforms that + * support weak linking, define these functions yourself, or use + * the halide_set_custom_load_library() and halide_set_custom_get_library_symbol() + * functions. In JIT-compiled code, use JITSharedRuntime::set_default_handlers(). + * + * halide_load_library and halide_get_library_symbol are equivalent to + * dlopen and dlsym. halide_get_symbol(sym) is equivalent to + * dlsym(RTLD_DEFAULT, sym). + */ +//@{ +extern void *halide_get_symbol(const char *name); +extern void *halide_load_library(const char *name); +extern void *halide_get_library_symbol(void *lib, const char *name); +extern void *halide_default_get_symbol(const char *name); +extern void *halide_default_load_library(const char *name); +extern void *halide_default_get_library_symbol(void *lib, const char *name); +typedef void *(*halide_get_symbol_t)(const char *name); +typedef void *(*halide_load_library_t)(const char *name); +typedef void *(*halide_get_library_symbol_t)(void *lib, const char *name); +extern halide_get_symbol_t halide_set_custom_get_symbol(halide_get_symbol_t user_get_symbol); +extern halide_load_library_t halide_set_custom_load_library(halide_load_library_t user_load_library); +extern halide_get_library_symbol_t halide_set_custom_get_library_symbol(halide_get_library_symbol_t user_get_library_symbol); +//@} + +/** Called when debug_to_file is used inside %Halide code. See + * Func::debug_to_file for how this is called + * + * Cannot be replaced in JITted code at present. + */ +extern int32_t halide_debug_to_file(void *user_context, const char *filename, + struct halide_buffer_t *buf); + +/** Types in the halide type system. They can be ints, unsigned ints, + * or floats (of various bit-widths), or a handle (which is always 64-bits). + * Note that the int/uint/float values do not imply a specific bit width + * (the bit width is expected to be encoded in a separate value). + */ +typedef enum halide_type_code_t +#if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) + : uint8_t +#endif +{ + halide_type_int = 0, ///< signed integers + halide_type_uint = 1, ///< unsigned integers + halide_type_float = 2, ///< IEEE floating point numbers + halide_type_handle = 3, ///< opaque pointer type (void *) + halide_type_bfloat = 4, ///< floating point numbers in the bfloat format +} halide_type_code_t; + +// Note that while __attribute__ can go before or after the declaration, +// __declspec apparently is only allowed before. +#ifndef HALIDE_ATTRIBUTE_ALIGN +#ifdef _MSC_VER +#define HALIDE_ATTRIBUTE_ALIGN(x) __declspec(align(x)) +#else +#define HALIDE_ATTRIBUTE_ALIGN(x) __attribute__((aligned(x))) +#endif +#endif + +/** A runtime tag for a type in the halide type system. Can be ints, + * unsigned ints, or floats of various bit-widths (the 'bits' + * field). Can also be vectors of the same (by setting the 'lanes' + * field to something larger than one). This struct should be + * exactly 32-bits in size. */ +struct halide_type_t { + /** The basic type code: signed integer, unsigned integer, or floating point. */ +#if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) + HALIDE_ATTRIBUTE_ALIGN(1) + halide_type_code_t code; // halide_type_code_t +#else + HALIDE_ATTRIBUTE_ALIGN(1) + uint8_t code; // halide_type_code_t +#endif + + /** The number of bits of precision of a single scalar value of this type. */ + HALIDE_ATTRIBUTE_ALIGN(1) + uint8_t bits; + + /** How many elements in a vector. This is 1 for scalar types. */ + HALIDE_ATTRIBUTE_ALIGN(2) + uint16_t lanes; + +#if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) + /** Construct a runtime representation of a Halide type from: + * code: The fundamental type from an enum. + * bits: The bit size of one element. + * lanes: The number of vector elements in the type. */ + HALIDE_ALWAYS_INLINE constexpr halide_type_t(halide_type_code_t code, uint8_t bits, uint16_t lanes = 1) + : code(code), bits(bits), lanes(lanes) { + } + + /** Default constructor is required e.g. to declare halide_trace_event + * instances. */ + HALIDE_ALWAYS_INLINE constexpr halide_type_t() + : code((halide_type_code_t)0), bits(0), lanes(0) { + } + + HALIDE_ALWAYS_INLINE constexpr halide_type_t with_lanes(uint16_t new_lanes) const { + return halide_type_t((halide_type_code_t)code, bits, new_lanes); + } + + HALIDE_ALWAYS_INLINE constexpr halide_type_t element_of() const { + return with_lanes(1); + } + /** Compare two types for equality. */ + HALIDE_ALWAYS_INLINE constexpr bool operator==(const halide_type_t &other) const { + return as_u32() == other.as_u32(); + } + + HALIDE_ALWAYS_INLINE constexpr bool operator!=(const halide_type_t &other) const { + return !(*this == other); + } + + HALIDE_ALWAYS_INLINE constexpr bool operator<(const halide_type_t &other) const { + return as_u32() < other.as_u32(); + } + + /** Size in bytes for a single element, even if width is not 1, of this type. */ + HALIDE_ALWAYS_INLINE constexpr int bytes() const { + return (bits + 7) / 8; + } + + HALIDE_ALWAYS_INLINE constexpr uint32_t as_u32() const { + // Note that this produces a result that is identical to memcpy'ing 'this' + // into a u32 (on a little-endian machine, anyway), and at -O1 or greater + // on Clang, the compiler knows this and optimizes this into a single 32-bit move. + // (At -O0 it will look awful.) + return static_cast(code) | + (static_cast(bits) << 8) | + (static_cast(lanes) << 16); + } +#endif +}; + +#if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) +static_assert(sizeof(halide_type_t) == sizeof(uint32_t), "size mismatch in halide_type_t"); +#endif + +enum halide_trace_event_code_t { halide_trace_load = 0, + halide_trace_store = 1, + halide_trace_begin_realization = 2, + halide_trace_end_realization = 3, + halide_trace_produce = 4, + halide_trace_end_produce = 5, + halide_trace_consume = 6, + halide_trace_end_consume = 7, + halide_trace_begin_pipeline = 8, + halide_trace_end_pipeline = 9, + halide_trace_tag = 10 }; + +struct halide_trace_event_t { + /** The name of the Func or Pipeline that this event refers to */ + const char *func; + + /** If the event type is a load or a store, this points to the + * value being loaded or stored. Use the type field to safely cast + * this to a concrete pointer type and retrieve it. For other + * events this is null. */ + void *value; + + /** For loads and stores, an array which contains the location + * being accessed. For vector loads or stores it is an array of + * vectors of coordinates (the vector dimension is innermost). + * + * For realization or production-related events, this will contain + * the mins and extents of the region being accessed, in the order + * min0, extent0, min1, extent1, ... + * + * For pipeline-related events, this will be null. + */ + int32_t *coordinates; + + /** For halide_trace_tag, this points to a read-only null-terminated string + * of arbitrary text. For all other events, this will be null. + */ + const char *trace_tag; + + /** If the event type is a load or a store, this is the type of + * the data. Otherwise, the value is meaningless. */ + struct halide_type_t type; + + /** The type of event */ + enum halide_trace_event_code_t event; + + /* The ID of the parent event (see below for an explanation of + * event ancestry). */ + int32_t parent_id; + + /** If this was a load or store of a Tuple-valued Func, this is + * which tuple element was accessed. */ + int32_t value_index; + + /** The length of the coordinates array */ + int32_t dimensions; +}; + +/** Called when Funcs are marked as trace_load, trace_store, or + * trace_realization. See Func::set_custom_trace. The default + * implementation either prints events via halide_print, or if + * HL_TRACE_FILE is defined, dumps the trace to that file in a + * sequence of trace packets. The header for a trace packet is defined + * below. If the trace is going to be large, you may want to make the + * file a named pipe, and then read from that pipe into gzip. + * + * halide_trace returns a unique ID which will be passed to future + * events that "belong" to the earlier event as the parent id. The + * ownership hierarchy looks like: + * + * begin_pipeline + * +--trace_tag (if any) + * +--trace_tag (if any) + * ... + * +--begin_realization + * | +--produce + * | | +--load/store + * | | +--end_produce + * | +--consume + * | | +--load + * | | +--end_consume + * | +--end_realization + * +--end_pipeline + * + * Threading means that ownership cannot be inferred from the ordering + * of events. There can be many active realizations of a given + * function, or many active productions for a single + * realization. Within a single production, the ordering of events is + * meaningful. + * + * Note that all trace_tag events (if any) will occur just after the begin_pipeline + * event, but before any begin_realization events. All trace_tags for a given Func + * will be emitted in the order added. + */ +// @} +extern int32_t halide_trace(void *user_context, const struct halide_trace_event_t *event); +extern int32_t halide_default_trace(void *user_context, const struct halide_trace_event_t *event); +typedef int32_t (*halide_trace_t)(void *user_context, const struct halide_trace_event_t *); +extern halide_trace_t halide_set_custom_trace(halide_trace_t trace); +// @} + +/** The header of a packet in a binary trace. All fields are 32-bit. */ +struct halide_trace_packet_t { + /** The total size of this packet in bytes. Always a multiple of + * four. Equivalently, the number of bytes until the next + * packet. */ + uint32_t size; + + /** The id of this packet (for the purpose of parent_id). */ + int32_t id; + + /** The remaining fields are equivalent to those in halide_trace_event_t */ + // @{ + struct halide_type_t type; + enum halide_trace_event_code_t event; + int32_t parent_id; + int32_t value_index; + int32_t dimensions; + // @} + +#if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) + /** Get the coordinates array, assuming this packet is laid out in + * memory as it was written. The coordinates array comes + * immediately after the packet header. */ + HALIDE_ALWAYS_INLINE const int *coordinates() const { + return (const int *)(this + 1); + } + + HALIDE_ALWAYS_INLINE int *coordinates() { + return (int *)(this + 1); + } + + /** Get the value, assuming this packet is laid out in memory as + * it was written. The packet comes immediately after the coordinates + * array. */ + HALIDE_ALWAYS_INLINE const void *value() const { + return (const void *)(coordinates() + dimensions); + } + + HALIDE_ALWAYS_INLINE void *value() { + return (void *)(coordinates() + dimensions); + } + + /** Get the func name, assuming this packet is laid out in memory + * as it was written. It comes after the value. */ + HALIDE_ALWAYS_INLINE const char *func() const { + return (const char *)value() + type.lanes * type.bytes(); + } + + HALIDE_ALWAYS_INLINE char *func() { + return (char *)value() + type.lanes * type.bytes(); + } + + /** Get the trace_tag (if any), assuming this packet is laid out in memory + * as it was written. It comes after the func name. If there is no trace_tag, + * this will return a pointer to an empty string. */ + HALIDE_ALWAYS_INLINE const char *trace_tag() const { + const char *f = func(); + // strlen may not be available here + while (*f++) { + // nothing + } + return f; + } + + HALIDE_ALWAYS_INLINE char *trace_tag() { + char *f = func(); + // strlen may not be available here + while (*f++) { + // nothing + } + return f; + } +#endif +}; + +/** Set the file descriptor that Halide should write binary trace + * events to. If called with 0 as the argument, Halide outputs trace + * information to stdout in a human-readable format. If never called, + * Halide checks the for existence of an environment variable called + * HL_TRACE_FILE and opens that file. If HL_TRACE_FILE is not defined, + * it outputs trace information to stdout in a human-readable + * format. */ +extern void halide_set_trace_file(int fd); + +/** Halide calls this to retrieve the file descriptor to write binary + * trace events to. The default implementation returns the value set + * by halide_set_trace_file. Implement it yourself if you wish to use + * a custom file descriptor per user_context. Return zero from your + * implementation to tell Halide to print human-readable trace + * information to stdout. */ +extern int halide_get_trace_file(void *user_context); + +/** If tracing is writing to a file. This call closes that file + * (flushing the trace). Returns zero on success. */ +extern int halide_shutdown_trace(void); + +/** All Halide GPU or device backend implementations provide an + * interface to be used with halide_device_malloc, etc. This is + * accessed via the functions below. + */ + +/** An opaque struct containing per-GPU API implementations of the + * device functions. */ +struct halide_device_interface_impl_t; + +/** Each GPU API provides a halide_device_interface_t struct pointing + * to the code that manages device allocations. You can access these + * functions directly from the struct member function pointers, or by + * calling the functions declared below. Note that the global + * functions are not available when using Halide as a JIT compiler. + * If you are using raw halide_buffer_t in that context you must use + * the function pointers in the device_interface struct. + * + * The function pointers below are currently the same for every GPU + * API; only the impl field varies. These top-level functions do the + * bookkeeping that is common across all GPU APIs, and then dispatch + * to more API-specific functions via another set of function pointers + * hidden inside the impl field. + */ +struct halide_device_interface_t { + int (*device_malloc)(void *user_context, struct halide_buffer_t *buf, + const struct halide_device_interface_t *device_interface); + int (*device_free)(void *user_context, struct halide_buffer_t *buf); + int (*device_sync)(void *user_context, struct halide_buffer_t *buf); + void (*device_release)(void *user_context, + const struct halide_device_interface_t *device_interface); + int (*copy_to_host)(void *user_context, struct halide_buffer_t *buf); + int (*copy_to_device)(void *user_context, struct halide_buffer_t *buf, + const struct halide_device_interface_t *device_interface); + int (*device_and_host_malloc)(void *user_context, struct halide_buffer_t *buf, + const struct halide_device_interface_t *device_interface); + int (*device_and_host_free)(void *user_context, struct halide_buffer_t *buf); + int (*buffer_copy)(void *user_context, struct halide_buffer_t *src, + const struct halide_device_interface_t *dst_device_interface, struct halide_buffer_t *dst); + int (*device_crop)(void *user_context, const struct halide_buffer_t *src, + struct halide_buffer_t *dst); + int (*device_slice)(void *user_context, const struct halide_buffer_t *src, + int slice_dim, int slice_pos, struct halide_buffer_t *dst); + int (*device_release_crop)(void *user_context, struct halide_buffer_t *buf); + int (*wrap_native)(void *user_context, struct halide_buffer_t *buf, uint64_t handle, + const struct halide_device_interface_t *device_interface); + int (*detach_native)(void *user_context, struct halide_buffer_t *buf); + int (*compute_capability)(void *user_context, int *major, int *minor); + const struct halide_device_interface_impl_t *impl; +}; + +/** Release all data associated with the given device interface, in + * particular all resources (memory, texture, context handles) + * allocated by Halide. Must be called explicitly when using AOT + * compilation. This is *not* thread-safe with respect to actively + * running Halide code. Ensure all pipelines are finished before + * calling this. */ +extern void halide_device_release(void *user_context, + const struct halide_device_interface_t *device_interface); + +/** Copy image data from device memory to host memory. This must be called + * explicitly to copy back the results of a GPU-based filter. */ +extern int halide_copy_to_host(void *user_context, struct halide_buffer_t *buf); + +/** Copy image data from host memory to device memory. This should not + * be called directly; Halide handles copying to the device + * automatically. If interface is NULL and the buf has a non-zero dev + * field, the device associated with the dev handle will be + * used. Otherwise if the dev field is 0 and interface is NULL, an + * error is returned. */ +extern int halide_copy_to_device(void *user_context, struct halide_buffer_t *buf, + const struct halide_device_interface_t *device_interface); + +/** Copy data from one buffer to another. The buffers may have + * different shapes and sizes, but the destination buffer's shape must + * be contained within the source buffer's shape. That is, for each + * dimension, the min on the destination buffer must be greater than + * or equal to the min on the source buffer, and min+extent on the + * destination buffer must be less that or equal to min+extent on the + * source buffer. The source data is pulled from either device or + * host memory on the source, depending on the dirty flags. host is + * preferred if both are valid. The dst_device_interface parameter + * controls the destination memory space. NULL means host memory. */ +extern int halide_buffer_copy(void *user_context, struct halide_buffer_t *src, + const struct halide_device_interface_t *dst_device_interface, + struct halide_buffer_t *dst); + +/** Give the destination buffer a device allocation which is an alias + * for the same coordinate range in the source buffer. Modifies the + * device, device_interface, and the device_dirty flag only. Only + * supported by some device APIs (others will return + * halide_error_code_device_crop_unsupported). Call + * halide_device_release_crop instead of halide_device_free to clean + * up resources associated with the cropped view. Do not free the + * device allocation on the source buffer while the destination buffer + * still lives. Note that the two buffers do not share dirty flags, so + * care must be taken to update them together as needed. Note that src + * and dst are required to have the same number of dimensions. + * + * Note also that (in theory) device interfaces which support cropping may + * still not support cropping a crop (instead, create a new crop of the parent + * buffer); in practice, no known implementation has this limitation, although + * it is possible that some future implementations may require it. */ +extern int halide_device_crop(void *user_context, + const struct halide_buffer_t *src, + struct halide_buffer_t *dst); + +/** Give the destination buffer a device allocation which is an alias + * for a similar coordinate range in the source buffer, but with one dimension + * sliced away in the dst. Modifies the device, device_interface, and the + * device_dirty flag only. Only supported by some device APIs (others will return + * halide_error_code_device_crop_unsupported). Call + * halide_device_release_crop instead of halide_device_free to clean + * up resources associated with the sliced view. Do not free the + * device allocation on the source buffer while the destination buffer + * still lives. Note that the two buffers do not share dirty flags, so + * care must be taken to update them together as needed. Note that the dst buffer + * must have exactly one fewer dimension than the src buffer, and that slice_dim + * and slice_pos must be valid within src. */ +extern int halide_device_slice(void *user_context, + const struct halide_buffer_t *src, + int slice_dim, int slice_pos, + struct halide_buffer_t *dst); + +/** Release any resources associated with a cropped/sliced view of another + * buffer. */ +extern int halide_device_release_crop(void *user_context, + struct halide_buffer_t *buf); + +/** Wait for current GPU operations to complete. Calling this explicitly + * should rarely be necessary, except maybe for profiling. */ +extern int halide_device_sync(void *user_context, struct halide_buffer_t *buf); + +/** + * Wait for current GPU operations to complete. Calling this explicitly + * should rarely be necessary, except maybe for profiling. + * This variation of the synchronizing is useful when a synchronization is desirable + * without specifying any buffer to synchronize on. + * Calling this with a null device_interface is always illegal. + */ +extern int halide_device_sync_global(void *user_context, const struct halide_device_interface_t *device_interface); + +/** Allocate device memory to back a halide_buffer_t. */ +extern int halide_device_malloc(void *user_context, struct halide_buffer_t *buf, + const struct halide_device_interface_t *device_interface); + +/** Free device memory. */ +extern int halide_device_free(void *user_context, struct halide_buffer_t *buf); + +/** Wrap or detach a native device handle, setting the device field + * and device_interface field as appropriate for the given GPU + * API. The meaning of the opaque handle is specific to the device + * interface, so if you know the device interface in use, call the + * more specific functions in the runtime headers for your specific + * device API instead (e.g. HalideRuntimeCuda.h). */ +// @{ +extern int halide_device_wrap_native(void *user_context, + struct halide_buffer_t *buf, + uint64_t handle, + const struct halide_device_interface_t *device_interface); +extern int halide_device_detach_native(void *user_context, struct halide_buffer_t *buf); +// @} + +/** Selects which gpu device to use. 0 is usually the display + * device. If never called, Halide uses the environment variable + * HL_GPU_DEVICE. If that variable is unset, Halide uses the last + * device. Set this to -1 to use the last device. */ +extern void halide_set_gpu_device(int n); + +/** Halide calls this to get the desired halide gpu device + * setting. Implement this yourself to use a different gpu device per + * user_context. The default implementation returns the value set by + * halide_set_gpu_device, or the environment variable + * HL_GPU_DEVICE. */ +extern int halide_get_gpu_device(void *user_context); + +/** Set the soft maximum amount of memory, in bytes, that the LRU + * cache will use to memoize Func results. This is not a strict + * maximum in that concurrency and simultaneous use of memoized + * reults larger than the cache size can both cause it to + * temporariliy be larger than the size specified here. + */ +extern void halide_memoization_cache_set_size(int64_t size); + +/** Given a cache key for a memoized result, currently constructed + * from the Func name and top-level Func name plus the arguments of + * the computation, determine if the result is in the cache and + * return it if so. (The internals of the cache key should be + * considered opaque by this function.) If this routine returns true, + * it is a cache miss. Otherwise, it will return false and the + * buffers passed in will be filled, via copying, with memoized + * data. The last argument is a list if halide_buffer_t pointers which + * represents the outputs of the memoized Func. If the Func does not + * return a Tuple, there will only be one halide_buffer_t in the list. The + * tuple_count parameters determines the length of the list. + * + * The return values are: + * -1: Signals an error. + * 0: Success and cache hit. + * 1: Success and cache miss. + */ +extern int halide_memoization_cache_lookup(void *user_context, const uint8_t *cache_key, int32_t size, + struct halide_buffer_t *realized_bounds, + int32_t tuple_count, struct halide_buffer_t **tuple_buffers); + +/** Given a cache key for a memoized result, currently constructed + * from the Func name and top-level Func name plus the arguments of + * the computation, store the result in the cache for futre access by + * halide_memoization_cache_lookup. (The internals of the cache key + * should be considered opaque by this function.) Data is copied out + * from the inputs and inputs are unmodified. The last argument is a + * list if halide_buffer_t pointers which represents the outputs of the + * memoized Func. If the Func does not return a Tuple, there will + * only be one halide_buffer_t in the list. The tuple_count parameters + * determines the length of the list. + * + * If there is a memory allocation failure, the store does not store + * the data into the cache. + * + * If has_eviction_key is true, the entry is marked with eviction_key to + * allow removing the key with halide_memoization_cache_evict. + */ +extern int halide_memoization_cache_store(void *user_context, const uint8_t *cache_key, int32_t size, + struct halide_buffer_t *realized_bounds, + int32_t tuple_count, + struct halide_buffer_t **tuple_buffers, + bool has_eviction_key, uint64_t eviction_key); + +/** Evict all cache entries that were tagged with the given + * eviction_key in the memoize scheduling directive. + */ +extern void halide_memoization_cache_evict(void *user_context, uint64_t eviction_key); + +/** If halide_memoization_cache_lookup succeeds, + * halide_memoization_cache_release must be called to signal the + * storage is no longer being used by the caller. It will be passed + * the host pointer of one the buffers returned by + * halide_memoization_cache_lookup. That is + * halide_memoization_cache_release will be called multiple times for + * the case where halide_memoization_cache_lookup is handling multiple + * buffers. (This corresponds to memoizing a Tuple in Halide.) Note + * that the host pointer must be sufficient to get to all information + * the release operation needs. The default Halide cache impleemntation + * accomplishes this by storing extra data before the start of the user + * modifiable host storage. + * + * This call is like free and does not have a failure return. + */ +extern void halide_memoization_cache_release(void *user_context, void *host); + +/** Free all memory and resources associated with the memoization cache. + * Must be called at a time when no other threads are accessing the cache. + */ +extern void halide_memoization_cache_cleanup(void); + +/** Verify that a given range of memory has been initialized; only used when Target::MSAN is enabled. + * + * The default implementation simply calls the LLVM-provided __msan_check_mem_is_initialized() function. + * + * The return value should always be zero. + */ +extern int halide_msan_check_memory_is_initialized(void *user_context, const void *ptr, uint64_t len, const char *name); + +/** Verify that the data pointed to by the halide_buffer_t is initialized (but *not* the halide_buffer_t itself), + * using halide_msan_check_memory_is_initialized() for checking. + * + * The default implementation takes pains to only check the active memory ranges + * (skipping padding), and sorting into ranges to always check the smallest number of + * ranges, in monotonically increasing memory order. + * + * Most client code should never need to replace the default implementation. + * + * The return value should always be zero. + */ +extern int halide_msan_check_buffer_is_initialized(void *user_context, struct halide_buffer_t *buffer, const char *buf_name); + +/** Annotate that a given range of memory has been initialized; + * only used when Target::MSAN is enabled. + * + * The default implementation simply calls the LLVM-provided __msan_unpoison() function. + * + * The return value should always be zero. + */ +extern int halide_msan_annotate_memory_is_initialized(void *user_context, const void *ptr, uint64_t len); + +/** Mark the data pointed to by the halide_buffer_t as initialized (but *not* the halide_buffer_t itself), + * using halide_msan_annotate_memory_is_initialized() for marking. + * + * The default implementation takes pains to only mark the active memory ranges + * (skipping padding), and sorting into ranges to always mark the smallest number of + * ranges, in monotonically increasing memory order. + * + * Most client code should never need to replace the default implementation. + * + * The return value should always be zero. + */ +extern int halide_msan_annotate_buffer_is_initialized(void *user_context, struct halide_buffer_t *buffer); +extern void halide_msan_annotate_buffer_is_initialized_as_destructor(void *user_context, void *buffer); + +/** The error codes that may be returned by a Halide pipeline. */ +enum halide_error_code_t { + /** There was no error. This is the value returned by Halide on success. */ + halide_error_code_success = 0, + + /** An uncategorized error occurred. Refer to the string passed to halide_error. */ + halide_error_code_generic_error = -1, + + /** A Func was given an explicit bound via Func::bound, but this + * was not large enough to encompass the region that is used of + * the Func by the rest of the pipeline. */ + halide_error_code_explicit_bounds_too_small = -2, + + /** The elem_size field of a halide_buffer_t does not match the size in + * bytes of the type of that ImageParam. Probable type mismatch. */ + halide_error_code_bad_type = -3, + + /** A pipeline would access memory outside of the halide_buffer_t passed + * in. */ + halide_error_code_access_out_of_bounds = -4, + + /** A halide_buffer_t was given that spans more than 2GB of memory. */ + halide_error_code_buffer_allocation_too_large = -5, + + /** A halide_buffer_t was given with extents that multiply to a number + * greater than 2^31-1 */ + halide_error_code_buffer_extents_too_large = -6, + + /** Applying explicit constraints on the size of an input or + * output buffer shrank the size of that buffer below what will be + * accessed by the pipeline. */ + halide_error_code_constraints_make_required_region_smaller = -7, + + /** A constraint on a size or stride of an input or output buffer + * was not met by the halide_buffer_t passed in. */ + halide_error_code_constraint_violated = -8, + + /** A scalar parameter passed in was smaller than its minimum + * declared value. */ + halide_error_code_param_too_small = -9, + + /** A scalar parameter passed in was greater than its minimum + * declared value. */ + halide_error_code_param_too_large = -10, + + /** A call to halide_malloc returned NULL. */ + halide_error_code_out_of_memory = -11, + + /** A halide_buffer_t pointer passed in was NULL. */ + halide_error_code_buffer_argument_is_null = -12, + + /** debug_to_file failed to open or write to the specified + * file. */ + halide_error_code_debug_to_file_failed = -13, + + /** The Halide runtime encountered an error while trying to copy + * from device to host. Turn on -debug in your target string to + * see more details. */ + halide_error_code_copy_to_host_failed = -14, + + /** The Halide runtime encountered an error while trying to copy + * from host to device. Turn on -debug in your target string to + * see more details. */ + halide_error_code_copy_to_device_failed = -15, + + /** The Halide runtime encountered an error while trying to + * allocate memory on device. Turn on -debug in your target string + * to see more details. */ + halide_error_code_device_malloc_failed = -16, + + /** The Halide runtime encountered an error while trying to + * synchronize with a device. Turn on -debug in your target string + * to see more details. */ + halide_error_code_device_sync_failed = -17, + + /** The Halide runtime encountered an error while trying to free a + * device allocation. Turn on -debug in your target string to see + * more details. */ + halide_error_code_device_free_failed = -18, + + /** Buffer has a non-zero device but no device interface, which + * violates a Halide invariant. */ + halide_error_code_no_device_interface = -19, + + /** This part of the Halide runtime is unimplemented on this platform. */ + halide_error_code_unimplemented = -20, + + /** A runtime symbol could not be loaded. */ + halide_error_code_symbol_not_found = -21, + + /** There is a bug in the Halide compiler. */ + halide_error_code_internal_error = -22, + + /** The Halide runtime encountered an error while trying to launch + * a GPU kernel. Turn on -debug in your target string to see more + * details. */ + halide_error_code_device_run_failed = -23, + + /** The Halide runtime encountered a host pointer that violated + * the alignment set for it by way of a call to + * set_host_alignment */ + halide_error_code_unaligned_host_ptr = -24, + + /** A fold_storage directive was used on a dimension that is not + * accessed in a monotonically increasing or decreasing fashion. */ + halide_error_code_bad_fold = -25, + + /** A fold_storage directive was used with a fold factor that was + * too small to store all the values of a producer needed by the + * consumer. */ + halide_error_code_fold_factor_too_small = -26, + + /** User-specified require() expression was not satisfied. */ + halide_error_code_requirement_failed = -27, + + /** At least one of the buffer's extents are negative. */ + halide_error_code_buffer_extents_negative = -28, + + /** Call(s) to a GPU backend API failed. */ + halide_error_code_gpu_device_error = -29, + + /** Failure recording trace packets for one of the halide_target_feature_trace features. */ + halide_error_code_trace_failed = -30, + + /** A specialize_fail() schedule branch was selected at runtime. */ + halide_error_code_specialize_fail = -31, + + /** The Halide runtime encountered an error while trying to wrap a + * native device handle. Turn on -debug in your target string to + * see more details. */ + halide_error_code_device_wrap_native_failed = -32, + + /** The Halide runtime encountered an error while trying to detach + * a native device handle. Turn on -debug in your target string + * to see more details. */ + halide_error_code_device_detach_native_failed = -33, + + /** The host field on an input or output was null, the device + * field was not zero, and the pipeline tries to use the buffer on + * the host. You may be passing a GPU-only buffer to a pipeline + * which is scheduled to use it on the CPU. */ + halide_error_code_host_is_null = -34, + + /** A folded buffer was passed to an extern stage, but the region + * touched wraps around the fold boundary. */ + halide_error_code_bad_extern_fold = -35, + + /** Buffer has a non-null device_interface but device is 0, which + * violates a Halide invariant. */ + halide_error_code_device_interface_no_device = -36, + + /** Buffer has both host and device dirty bits set, which violates + * a Halide invariant. */ + halide_error_code_host_and_device_dirty = -37, + + /** The halide_buffer_t * passed to a halide runtime routine is + * nullptr and this is not allowed. */ + halide_error_code_buffer_is_null = -38, + + /** The Halide runtime encountered an error while trying to copy + * from one buffer to another. Turn on -debug in your target + * string to see more details. */ + halide_error_code_device_buffer_copy_failed = -39, + + /** Attempted to make cropped/sliced alias of a buffer with a device + * field, but the device_interface does not support cropping. */ + halide_error_code_device_crop_unsupported = -40, + + /** Cropping/slicing a buffer failed for some other reason. Turn on -debug + * in your target string. */ + halide_error_code_device_crop_failed = -41, + + /** An operation on a buffer required an allocation on a + * particular device interface, but a device allocation already + * existed on a different device interface. Free the old one + * first. */ + halide_error_code_incompatible_device_interface = -42, + + /** The dimensions field of a halide_buffer_t does not match the dimensions of that ImageParam. */ + halide_error_code_bad_dimensions = -43, + + /** A buffer with the device_dirty flag set was passed to a + * pipeline compiled with no device backends enabled, so it + * doesn't know how to copy the data back from device memory to + * host memory. Either call copy_to_host before calling the Halide + * pipeline, or enable the appropriate device backend. */ + halide_error_code_device_dirty_with_no_device_support = -44, + + /** An explicit storage bound provided is too small to store + * all the values produced by the function. */ + halide_error_code_storage_bound_too_small = -45, + + /** A factor used to split a loop was discovered to be zero or negative at + * runtime. */ + halide_error_code_split_factor_not_positive = -46, + + /** "vscale" value of Scalable Vector detected in runtime does not match + * the vscale value used in compilation. */ + halide_error_code_vscale_invalid = -47, + + /** Profiling failed for a pipeline invocation. */ + halide_error_code_cannot_profile_pipeline = -48, +}; + +/** Halide calls the functions below on various error conditions. The + * default implementations construct an error message, call + * halide_error, then return the matching error code above. On + * platforms that support weak linking, you can override these to + * catch the errors individually. */ + +/** A call into an extern stage for the purposes of bounds inference + * failed. Returns the error code given by the extern stage. */ +extern int halide_error_bounds_inference_call_failed(void *user_context, const char *extern_stage_name, int result); + +/** A call to an extern stage failed. Returned the error code given by + * the extern stage. */ +extern int halide_error_extern_stage_failed(void *user_context, const char *extern_stage_name, int result); + +/** Various other error conditions. See the enum above for a + * description of each. */ +// @{ +extern int halide_error_explicit_bounds_too_small(void *user_context, const char *func_name, const char *var_name, + int min_bound, int max_bound, int min_required, int max_required); +extern int halide_error_bad_type(void *user_context, const char *func_name, + uint32_t type_given, uint32_t correct_type); // N.B. The last two args are the bit representation of a halide_type_t +extern int halide_error_bad_dimensions(void *user_context, const char *func_name, + int32_t dimensions_given, int32_t correct_dimensions); +extern int halide_error_access_out_of_bounds(void *user_context, const char *func_name, + int dimension, int min_touched, int max_touched, + int min_valid, int max_valid); +extern int halide_error_buffer_allocation_too_large(void *user_context, const char *buffer_name, + uint64_t allocation_size, uint64_t max_size); +extern int halide_error_buffer_extents_negative(void *user_context, const char *buffer_name, int dimension, int extent); +extern int halide_error_buffer_extents_too_large(void *user_context, const char *buffer_name, + int64_t actual_size, int64_t max_size); +extern int halide_error_constraints_make_required_region_smaller(void *user_context, const char *buffer_name, + int dimension, + int constrained_min, int constrained_extent, + int required_min, int required_extent); +extern int halide_error_constraint_violated(void *user_context, const char *var, int val, + const char *constrained_var, int constrained_val); +extern int halide_error_param_too_small_i64(void *user_context, const char *param_name, + int64_t val, int64_t min_val); +extern int halide_error_param_too_small_u64(void *user_context, const char *param_name, + uint64_t val, uint64_t min_val); +extern int halide_error_param_too_small_f64(void *user_context, const char *param_name, + double val, double min_val); +extern int halide_error_param_too_large_i64(void *user_context, const char *param_name, + int64_t val, int64_t max_val); +extern int halide_error_param_too_large_u64(void *user_context, const char *param_name, + uint64_t val, uint64_t max_val); +extern int halide_error_param_too_large_f64(void *user_context, const char *param_name, + double val, double max_val); +extern int halide_error_out_of_memory(void *user_context); +extern int halide_error_buffer_argument_is_null(void *user_context, const char *buffer_name); +extern int halide_error_debug_to_file_failed(void *user_context, const char *func, + const char *filename, int error_code); +extern int halide_error_unaligned_host_ptr(void *user_context, const char *func_name, int alignment); +extern int halide_error_host_is_null(void *user_context, const char *func_name); +extern int halide_error_bad_fold(void *user_context, const char *func_name, const char *var_name, + const char *loop_name); +extern int halide_error_bad_extern_fold(void *user_context, const char *func_name, + int dim, int min, int extent, int valid_min, int fold_factor); + +extern int halide_error_fold_factor_too_small(void *user_context, const char *func_name, const char *var_name, + int fold_factor, const char *loop_name, int required_extent); +extern int halide_error_requirement_failed(void *user_context, const char *condition, const char *message); +extern int halide_error_specialize_fail(void *user_context, const char *message); +extern int halide_error_no_device_interface(void *user_context); +extern int halide_error_device_interface_no_device(void *user_context); +extern int halide_error_host_and_device_dirty(void *user_context); +extern int halide_error_buffer_is_null(void *user_context, const char *routine); +extern int halide_error_device_dirty_with_no_device_support(void *user_context, const char *buffer_name); +extern int halide_error_storage_bound_too_small(void *user_context, const char *func_name, const char *var_name, + int provided_size, int required_size); +extern int halide_error_device_crop_failed(void *user_context); +extern int halide_error_split_factor_not_positive(void *user_context, const char *func_name, const char *orig, const char *outer, const char *inner, const char *factor_str, int factor); +extern int halide_error_vscale_invalid(void *user_context, const char *func_name, int runtime_vscale, int compiletime_vscale); +// @} + +/** Optional features a compilation Target can have. + * Be sure to keep this in sync with the Feature enum in Target.h and the implementation of + * get_runtime_compatible_target in Target.cpp if you add a new feature. + */ +typedef enum halide_target_feature_t { + halide_target_feature_jit = 0, ///< Generate code that will run immediately inside the calling process. + halide_target_feature_debug, ///< Turn on debug info and output for runtime code. + halide_target_feature_no_asserts, ///< Disable all runtime checks, for slightly tighter code. + halide_target_feature_no_bounds_query, ///< Disable the bounds querying functionality. + + halide_target_feature_sse41, ///< Use SSE 4.1 and earlier instructions. Only relevant on x86. + halide_target_feature_avx, ///< Use AVX 1 instructions. Only relevant on x86. + halide_target_feature_avx2, ///< Use AVX 2 instructions. Only relevant on x86. + halide_target_feature_fma, ///< Enable x86 FMA instruction + halide_target_feature_fma4, ///< Enable x86 (AMD) FMA4 instruction set + halide_target_feature_f16c, ///< Enable x86 16-bit float support + + halide_target_feature_armv7s, ///< Generate code for ARMv7s. Only relevant for 32-bit ARM. + halide_target_feature_no_neon, ///< Avoid using NEON instructions. Only relevant for 32-bit ARM. + + halide_target_feature_vsx, ///< Use VSX instructions. Only relevant on POWERPC. + halide_target_feature_power_arch_2_07, ///< Use POWER ISA 2.07 new instructions. Only relevant on POWERPC. + + halide_target_feature_cuda, ///< Enable the CUDA runtime. Defaults to compute capability 2.0 (Fermi) + halide_target_feature_cuda_capability30, ///< Enable CUDA compute capability 3.0 (Kepler) + halide_target_feature_cuda_capability32, ///< Enable CUDA compute capability 3.2 (Tegra K1) + halide_target_feature_cuda_capability35, ///< Enable CUDA compute capability 3.5 (Kepler) + halide_target_feature_cuda_capability50, ///< Enable CUDA compute capability 5.0 (Maxwell) + halide_target_feature_cuda_capability61, ///< Enable CUDA compute capability 6.1 (Pascal) + halide_target_feature_cuda_capability70, ///< Enable CUDA compute capability 7.0 (Volta) + halide_target_feature_cuda_capability75, ///< Enable CUDA compute capability 7.5 (Turing) + halide_target_feature_cuda_capability80, ///< Enable CUDA compute capability 8.0 (Ampere) + halide_target_feature_cuda_capability86, ///< Enable CUDA compute capability 8.6 (Ampere) + + halide_target_feature_opencl, ///< Enable the OpenCL runtime. + halide_target_feature_cl_doubles, ///< Enable double support on OpenCL targets + halide_target_feature_cl_atomic64, ///< Enable 64-bit atomics operations on OpenCL targets + + halide_target_feature_user_context, ///< Generated code takes a user_context pointer as first argument + + halide_target_feature_profile, ///< Launch a sampling profiler alongside the Halide pipeline that monitors and reports the runtime used by each Func + halide_target_feature_no_runtime, ///< Do not include a copy of the Halide runtime in any generated object file or assembly + + halide_target_feature_metal, ///< Enable the (Apple) Metal runtime. + + halide_target_feature_c_plus_plus_mangling, ///< Generate C++ mangled names for result function, et al + + halide_target_feature_large_buffers, ///< Enable 64-bit buffer indexing to support buffers > 2GB. Ignored if bits != 64. + + halide_target_feature_hvx_128, ///< Enable HVX 128 byte mode. + halide_target_feature_hvx_v62, ///< Enable Hexagon v62 architecture. + halide_target_feature_fuzz_float_stores, ///< On every floating point store, set the last bit of the mantissa to zero. Pipelines for which the output is very different with this feature enabled may also produce very different output on different processors. + halide_target_feature_soft_float_abi, ///< Enable soft float ABI. This only enables the soft float ABI calling convention, which does not necessarily use soft floats. + halide_target_feature_msan, ///< Enable hooks for MSAN support. + halide_target_feature_avx512, ///< Enable the base AVX512 subset supported by all AVX512 architectures. The specific feature sets are AVX-512F and AVX512-CD. See https://en.wikipedia.org/wiki/AVX-512 for a description of each AVX subset. + halide_target_feature_avx512_knl, ///< Enable the AVX512 features supported by Knight's Landing chips, such as the Xeon Phi x200. This includes the base AVX512 set, and also AVX512-CD and AVX512-ER. + halide_target_feature_avx512_skylake, ///< Enable the AVX512 features supported by Skylake Xeon server processors. This adds AVX512-VL, AVX512-BW, and AVX512-DQ to the base set. The main difference from the base AVX512 set is better support for small integer ops. Note that this does not include the Knight's Landing features. Note also that these features are not available on Skylake desktop and mobile processors. + halide_target_feature_avx512_cannonlake, ///< Enable the AVX512 features expected to be supported by future Cannonlake processors. This includes all of the Skylake features, plus AVX512-IFMA and AVX512-VBMI. + halide_target_feature_avx512_zen4, ///< Enable the AVX512 features supported by Zen4 processors. This include all of the Cannonlake features, plus AVX512-VNNI, AVX512-BF16, and more. + halide_target_feature_avx512_sapphirerapids, ///< Enable the AVX512 features supported by Sapphire Rapids processors. This include all of the Zen4 features, plus AVX-VNNI and AMX instructions. + halide_target_feature_trace_loads, ///< Trace all loads done by the pipeline. Equivalent to calling Func::trace_loads on every non-inlined Func. + halide_target_feature_trace_stores, ///< Trace all stores done by the pipeline. Equivalent to calling Func::trace_stores on every non-inlined Func. + halide_target_feature_trace_realizations, ///< Trace all realizations done by the pipeline. Equivalent to calling Func::trace_realizations on every non-inlined Func. + halide_target_feature_trace_pipeline, ///< Trace the pipeline. + halide_target_feature_hvx_v65, ///< Enable Hexagon v65 architecture. + halide_target_feature_hvx_v66, ///< Enable Hexagon v66 architecture. + halide_target_feature_hvx_v68, ///< Enable Hexagon v68 architecture. + halide_target_feature_cl_half, ///< Enable half support on OpenCL targets + halide_target_feature_strict_float, ///< Turn off all non-IEEE floating-point optimization. Currently applies only to LLVM targets. + halide_target_feature_tsan, ///< Enable hooks for TSAN support. + halide_target_feature_asan, ///< Enable hooks for ASAN support. + halide_target_feature_d3d12compute, ///< Enable Direct3D 12 Compute runtime. + halide_target_feature_check_unsafe_promises, ///< Insert assertions for promises. + halide_target_feature_hexagon_dma, ///< Enable Hexagon DMA buffers. + halide_target_feature_embed_bitcode, ///< Emulate clang -fembed-bitcode flag. + halide_target_feature_enable_llvm_loop_opt, ///< Enable loop vectorization + unrolling in LLVM. Overrides halide_target_feature_disable_llvm_loop_opt. (Ignored for non-LLVM targets.) + halide_target_feature_wasm_mvponly, ///< Disable all extensions to WebAssembly codegen (including +sign-ext and +nontrapping-fptoint, which are on by default). + halide_target_feature_wasm_simd128, ///< Enable +simd128 instructions for WebAssembly codegen. + halide_target_feature_wasm_threads, ///< Enable use of threads in WebAssembly codegen. Requires the use of a wasm runtime that provides pthread-compatible wrappers (typically, Emscripten with the -pthreads flag). Unsupported under WASI. + halide_target_feature_wasm_bulk_memory, ///< Enable +bulk-memory instructions for WebAssembly codegen. + halide_target_feature_webgpu, ///< Enable the WebGPU runtime. + halide_target_feature_sve, ///< Enable ARM Scalable Vector Extensions + halide_target_feature_sve2, ///< Enable ARM Scalable Vector Extensions v2 + halide_target_feature_egl, ///< Force use of EGL support. + halide_target_feature_arm_dot_prod, ///< Enable ARMv8.2-a dotprod extension (i.e. udot and sdot instructions) + halide_target_feature_arm_fp16, ///< Enable ARMv8.2-a half-precision floating point data processing + halide_llvm_large_code_model, ///< Use the LLVM large code model to compile + halide_target_feature_rvv, ///< Enable RISCV "V" Vector Extension + halide_target_feature_armv81a, ///< Enable ARMv8.1-a instructions + halide_target_feature_sanitizer_coverage, ///< Enable hooks for SanitizerCoverage support. + halide_target_feature_profile_by_timer, ///< Alternative to halide_target_feature_profile using timer interrupt for systems without threads or applicartions that need to avoid them. + halide_target_feature_spirv, ///< Enable SPIR-V code generation support. + halide_target_feature_vulkan, ///< Enable Vulkan runtime support. + halide_target_feature_vulkan_int8, ///< Enable Vulkan 8-bit integer support. + halide_target_feature_vulkan_int16, ///< Enable Vulkan 16-bit integer support. + halide_target_feature_vulkan_int64, ///< Enable Vulkan 64-bit integer support. + halide_target_feature_vulkan_float16, ///< Enable Vulkan 16-bit float support. + halide_target_feature_vulkan_float64, ///< Enable Vulkan 64-bit float support. + halide_target_feature_vulkan_version10, ///< Enable Vulkan v1.0 runtime target support. + halide_target_feature_vulkan_version12, ///< Enable Vulkan v1.2 runtime target support. + halide_target_feature_vulkan_version13, ///< Enable Vulkan v1.3 runtime target support. + halide_target_feature_semihosting, ///< Used together with Target::NoOS for the baremetal target built with semihosting library and run with semihosting mode where minimum I/O communication with a host PC is available. + halide_target_feature_avx10_1, ///< Intel AVX10 version 1 support. vector_bits is used to indicate width. + halide_target_feature_x86_apx, ///< Intel x86 APX support. Covers initial set of features released as APX: egpr,push2pop2,ppx,ndd . + halide_target_feature_end ///< A sentinel. Every target is considered to have this feature, and setting this feature does nothing. +} halide_target_feature_t; + +/** This function is called internally by Halide in some situations to determine + * if the current execution environment can support the given set of + * halide_target_feature_t flags. The implementation must do the following: + * + * -- If there are flags set in features that the function knows *cannot* be supported, return 0. + * -- Otherwise, return 1. + * -- Note that any flags set in features that the function doesn't know how to test should be ignored; + * this implies that a return value of 1 means "not known to be bad" rather than "known to be good". + * + * In other words: a return value of 0 means "It is not safe to use code compiled with these features", + * while a return value of 1 means "It is not obviously unsafe to use code compiled with these features". + * + * The default implementation simply calls halide_default_can_use_target_features. + * + * Note that `features` points to an array of `count` uint64_t; this array must contain enough + * bits to represent all the currently known features. Any excess bits must be set to zero. + */ +// @{ +extern int halide_can_use_target_features(int count, const uint64_t *features); +typedef int (*halide_can_use_target_features_t)(int count, const uint64_t *features); +extern halide_can_use_target_features_t halide_set_custom_can_use_target_features(halide_can_use_target_features_t); +// @} + +/** + * This is the default implementation of halide_can_use_target_features; it is provided + * for convenience of user code that may wish to extend halide_can_use_target_features + * but continue providing existing support, e.g. + * + * int halide_can_use_target_features(int count, const uint64_t *features) { + * if (features[halide_target_somefeature >> 6] & (1LL << (halide_target_somefeature & 63))) { + * if (!can_use_somefeature()) { + * return 0; + * } + * } + * return halide_default_can_use_target_features(count, features); + * } + */ +extern int halide_default_can_use_target_features(int count, const uint64_t *features); + +typedef struct halide_dimension_t { +#if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) + int32_t min = 0, extent = 0, stride = 0; + + // Per-dimension flags. None are defined yet (This is reserved for future use). + uint32_t flags = 0; + + HALIDE_ALWAYS_INLINE halide_dimension_t() = default; + HALIDE_ALWAYS_INLINE halide_dimension_t(int32_t m, int32_t e, int32_t s, uint32_t f = 0) + : min(m), extent(e), stride(s), flags(f) { + } + + HALIDE_ALWAYS_INLINE bool operator==(const halide_dimension_t &other) const { + return (min == other.min) && + (extent == other.extent) && + (stride == other.stride) && + (flags == other.flags); + } + + HALIDE_ALWAYS_INLINE bool operator!=(const halide_dimension_t &other) const { + return !(*this == other); + } +#else + int32_t min, extent, stride; + + // Per-dimension flags. None are defined yet (This is reserved for future use). + uint32_t flags; +#endif +} halide_dimension_t; + +#ifdef __cplusplus +} // extern "C" +#endif + +typedef enum { halide_buffer_flag_host_dirty = 1, + halide_buffer_flag_device_dirty = 2 } halide_buffer_flags; + +/** + * The raw representation of an image passed around by generated + * Halide code. It includes some stuff to track whether the image is + * not actually in main memory, but instead on a device (like a + * GPU). For a more convenient C++ wrapper, use Halide::Buffer. */ +typedef struct halide_buffer_t { + /** A device-handle for e.g. GPU memory used to back this buffer. */ + uint64_t device; + + /** The interface used to interpret the above handle. */ + const struct halide_device_interface_t *device_interface; + + /** A pointer to the start of the data in main memory. In terms of + * the Halide coordinate system, this is the address of the min + * coordinates (defined below). */ + uint8_t *host; + + /** flags with various meanings. */ + uint64_t flags; + + /** The type of each buffer element. */ + struct halide_type_t type; + + /** The dimensionality of the buffer. */ + int32_t dimensions; + + /** The shape of the buffer. Halide does not own this array - you + * must manage the memory for it yourself. */ + halide_dimension_t *dim; + + /** Pads the buffer up to a multiple of 8 bytes */ + void *padding; + +#if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) + /** Convenience methods for accessing the flags */ + // @{ + HALIDE_ALWAYS_INLINE bool get_flag(halide_buffer_flags flag) const { + return (flags & flag) != 0; + } + + HALIDE_ALWAYS_INLINE void set_flag(halide_buffer_flags flag, bool value) { + if (value) { + flags |= flag; + } else { + flags &= ~uint64_t(flag); + } + } + + HALIDE_MUST_USE_RESULT HALIDE_ALWAYS_INLINE bool host_dirty() const { + return get_flag(halide_buffer_flag_host_dirty); + } + + HALIDE_MUST_USE_RESULT HALIDE_ALWAYS_INLINE bool device_dirty() const { + return get_flag(halide_buffer_flag_device_dirty); + } + + HALIDE_ALWAYS_INLINE void set_host_dirty(bool v = true) { + set_flag(halide_buffer_flag_host_dirty, v); + } + + HALIDE_ALWAYS_INLINE void set_device_dirty(bool v = true) { + set_flag(halide_buffer_flag_device_dirty, v); + } + // @} + + /** The total number of elements this buffer represents. Equal to + * the product of the extents */ + HALIDE_ALWAYS_INLINE size_t number_of_elements() const { + size_t s = 1; + for (int i = 0; i < dimensions; i++) { + s *= dim[i].extent; + } + return s; + } + + /** Offset to the element with the lowest address. + * If all strides are positive, equal to zero. + * Offset is in elements, not bytes. + * Unlike begin(), this is ok to call on an unallocated buffer. */ + HALIDE_ALWAYS_INLINE ptrdiff_t begin_offset() const { + ptrdiff_t index = 0; + for (int i = 0; i < dimensions; i++) { + const int stride = dim[i].stride; + if (stride < 0) { + index += stride * (ptrdiff_t)(dim[i].extent - 1); + } + } + return index; + } + + /** An offset to one beyond the element with the highest address. + * Offset is in elements, not bytes. + * Unlike end(), this is ok to call on an unallocated buffer. */ + HALIDE_ALWAYS_INLINE ptrdiff_t end_offset() const { + ptrdiff_t index = 0; + for (int i = 0; i < dimensions; i++) { + const int stride = dim[i].stride; + if (stride > 0) { + index += stride * (ptrdiff_t)(dim[i].extent - 1); + } + } + index += 1; + return index; + } + + /** A pointer to the element with the lowest address. + * If all strides are positive, equal to the host pointer. + * Illegal to call on an unallocated buffer. */ + HALIDE_ALWAYS_INLINE uint8_t *begin() const { + return host + begin_offset() * type.bytes(); + } + + /** A pointer to one beyond the element with the highest address. + * Illegal to call on an unallocated buffer. */ + HALIDE_ALWAYS_INLINE uint8_t *end() const { + return host + end_offset() * type.bytes(); + } + + /** The total number of bytes spanned by the data in memory. */ + HALIDE_ALWAYS_INLINE size_t size_in_bytes() const { + return (size_t)(end_offset() - begin_offset()) * type.bytes(); + } + + /** A pointer to the element at the given location. */ + HALIDE_ALWAYS_INLINE uint8_t *address_of(const int *pos) const { + ptrdiff_t index = 0; + for (int i = 0; i < dimensions; i++) { + index += (ptrdiff_t)dim[i].stride * (pos[i] - dim[i].min); + } + return host + index * type.bytes(); + } + + /** Attempt to call device_sync for the buffer. If the buffer + * has no device_interface (or no device_sync), this is a quiet no-op. + * Calling this explicitly should rarely be necessary, except for profiling. */ + HALIDE_ALWAYS_INLINE int device_sync(void *ctx = nullptr) { + if (device_interface && device_interface->device_sync) { + return device_interface->device_sync(ctx, this); + } + return 0; + } + + /** Check if an input buffer passed extern stage is a querying + * bounds. Compared to doing the host pointer check directly, + * this both adds clarity to code and will facilitate moving to + * another representation for bounds query arguments. */ + HALIDE_ALWAYS_INLINE bool is_bounds_query() const { + return host == nullptr && device == 0; + } + +#endif +} halide_buffer_t; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HALIDE_ATTRIBUTE_DEPRECATED +#ifdef HALIDE_ALLOW_DEPRECATED +#define HALIDE_ATTRIBUTE_DEPRECATED(x) +#else +#ifdef _MSC_VER +#define HALIDE_ATTRIBUTE_DEPRECATED(x) __declspec(deprecated(x)) +#else +#define HALIDE_ATTRIBUTE_DEPRECATED(x) __attribute__((deprecated(x))) +#endif +#endif +#endif + +/** halide_scalar_value_t is a simple union able to represent all the well-known + * scalar values in a filter argument. Note that it isn't tagged with a type; + * you must ensure you know the proper type before accessing. Most user + * code will never need to create instances of this struct; its primary use + * is to hold def/min/max values in a halide_filter_argument_t. (Note that + * this is conceptually just a union; it's wrapped in a struct to ensure + * that it doesn't get anonymized by LLVM.) + */ +struct halide_scalar_value_t { + union { + bool b; + int8_t i8; + int16_t i16; + int32_t i32; + int64_t i64; + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + float f32; + double f64; + void *handle; + } u; +#ifdef __cplusplus + HALIDE_ALWAYS_INLINE halide_scalar_value_t() { + u.u64 = 0; + } +#endif +}; + +enum halide_argument_kind_t { + halide_argument_kind_input_scalar = 0, + halide_argument_kind_input_buffer = 1, + halide_argument_kind_output_buffer = 2 +}; + +/* + These structs must be robust across different compilers and settings; when + modifying them, strive for the following rules: + + 1) All fields are explicitly sized. I.e. must use int32_t and not "int" + 2) All fields must land on an alignment boundary that is the same as their size + 3) Explicit padding is added to make that so + 4) The sizeof the struct is padded out to a multiple of the largest natural size thing in the struct + 5) don't forget that 32 and 64 bit pointers are different sizes +*/ + +/** + * Obsolete version of halide_filter_argument_t; only present in + * code that wrote halide_filter_metadata_t version 0. + */ +struct halide_filter_argument_t_v0 { + const char *name; + int32_t kind; + int32_t dimensions; + struct halide_type_t type; + const struct halide_scalar_value_t *def, *min, *max; +}; + +/** + * halide_filter_argument_t is essentially a plain-C-struct equivalent to + * Halide::Argument; most user code will never need to create one. + */ +struct halide_filter_argument_t { + const char *name; // name of the argument; will never be null or empty. + int32_t kind; // actually halide_argument_kind_t + int32_t dimensions; // always zero for scalar arguments + struct halide_type_t type; + // These pointers should always be null for buffer arguments, + // and *may* be null for scalar arguments. (A null value means + // there is no def/min/max/estimate specified for this argument.) + const struct halide_scalar_value_t *scalar_def, *scalar_min, *scalar_max, *scalar_estimate; + // This pointer should always be null for scalar arguments, + // and *may* be null for buffer arguments. If not null, it should always + // point to an array of dimensions*2 pointers, which will be the (min, extent) + // estimates for each dimension of the buffer. (Note that any of the pointers + // may be null as well.) + int64_t const *const *buffer_estimates; +}; + +struct halide_filter_metadata_t { +#ifdef __cplusplus + static const int32_t VERSION = 1; +#endif + + /** version of this metadata; currently always 1. */ + int32_t version; + + /** The number of entries in the arguments field. This is always >= 1. */ + int32_t num_arguments; + + /** An array of the filters input and output arguments; this will never be + * null. The order of arguments is not guaranteed (input and output arguments + * may come in any order); however, it is guaranteed that all arguments + * will have a unique name within a given filter. */ + const struct halide_filter_argument_t *arguments; + + /** The Target for which the filter was compiled. This is always + * a canonical Target string (ie a product of Target::to_string). */ + const char *target; + + /** The function name of the filter. */ + const char *name; +}; + +/** halide_register_argv_and_metadata() is a **user-defined** function that + * must be provided in order to use the registration.cc files produced + * by Generators when the 'registration' output is requested. Each registration.cc + * file provides a static initializer that calls this function with the given + * filter's argv-call variant, its metadata, and (optionally) and additional + * textual data that the build system chooses to tack on for its own purposes. + * Note that this will be called at static-initializer time (i.e., before + * main() is called), and in an unpredictable order. Note that extra_key_value_pairs + * may be nullptr; if it's not null, it's expected to be a null-terminated list + * of strings, with an even number of entries. */ +void halide_register_argv_and_metadata( + int (*filter_argv_call)(void **), + const struct halide_filter_metadata_t *filter_metadata, + const char *const *extra_key_value_pairs); + +/** The functions below here are relevant for pipelines compiled with + * the -profile target flag, which runs a sampling profiler thread + * alongside the pipeline. */ + +/** Per-Func state tracked by the sampling profiler. */ +struct HALIDE_ATTRIBUTE_ALIGN(8) halide_profiler_func_stats { + /** Total time taken evaluating this Func (in nanoseconds). */ + uint64_t time; + + /** The current memory allocation of this Func. */ + uint64_t memory_current; + + /** The peak memory allocation of this Func. */ + uint64_t memory_peak; + + /** The total memory allocation of this Func. */ + uint64_t memory_total; + + /** The peak stack allocation of this Func's threads. */ + uint64_t stack_peak; + + /** The average number of thread pool worker threads active while computing this Func. */ + uint64_t active_threads_numerator, active_threads_denominator; + + /** The name of this Func. A global constant string. */ + const char *name; + + /** The total number of memory allocation of this Func. */ + int num_allocs; +}; + +/** Per-pipeline state tracked by the sampling profiler. These exist + * in a linked list. */ +struct HALIDE_ATTRIBUTE_ALIGN(8) halide_profiler_pipeline_stats { + /** Total time spent in this pipeline (in nanoseconds) */ + uint64_t time; + + /** The current memory allocation of funcs in this pipeline. */ + uint64_t memory_current; + + /** The peak memory allocation of funcs in this pipeline. */ + uint64_t memory_peak; + + /** The total memory allocation of funcs in this pipeline. */ + uint64_t memory_total; + + /** The average number of thread pool worker threads doing useful + * work while computing this pipeline. */ + uint64_t active_threads_numerator, active_threads_denominator; + + /** The name of this pipeline. A global constant string. */ + const char *name; + + /** An array containing states for each Func in this pipeline. */ + struct halide_profiler_func_stats *funcs; + + /** The next pipeline_stats pointer. It's a void * because types + * in the Halide runtime may not currently be recursive. */ + void *next; + + /** The number of funcs in this pipeline. */ + int num_funcs; + + /** The number of times this pipeline has been run. */ + int runs; + + /** The total number of samples taken inside of this pipeline. */ + int samples; + + /** The total number of memory allocation of funcs in this pipeline. */ + int num_allocs; +}; + +/** Per-invocation-of-a-pipeline state. Lives on the stack of the Halide + * code. Exists in a doubly-linked list to that it can be cleanly + * removed. */ +struct HALIDE_ATTRIBUTE_ALIGN(8) halide_profiler_instance_state { + /** Time billed to funcs in this instance by the sampling thread. */ + uint64_t billed_time; + + /** Wall clock time of the start of the instance. */ + uint64_t start_time; + + /** The current memory allocation of funcs in this instance. */ + uint64_t memory_current; + + /** The peak memory allocation of funcs in this instance. */ + uint64_t memory_peak; + + /** The total memory allocation of funcs in this instance. */ + uint64_t memory_total; + + /** The average number of thread pool worker threads doing useful + * work while computing this instance. */ + uint64_t active_threads_numerator, active_threads_denominator; + + /** A pointer to the next running instance, so that the running instances + * can exist in a linked list. */ + struct halide_profiler_instance_state *next; + + /** A pointer to the address of the next pointer of the previous instance, + * so that this can be removed from the linked list when the instance + * terminates. */ + struct halide_profiler_instance_state **prev_next; + + /** Information shared across all instances. The stats above are merged into + * it when the instance is retired. */ + struct halide_profiler_pipeline_stats *pipeline_stats; + + /** An array containing states for each Func in this instance of this pipeline. */ + struct halide_profiler_func_stats *funcs; + + /** The id of the current running Func. Set by the pipeline, read + * periodically by the profiler thread. */ + int current_func; + + /** The number of threads currently doing work on this pipeline instance. */ + int active_threads; + + /** The number of samples taken by this instance. */ + int samples; + + /** The total number of memory allocation of funcs in this instance. */ + int num_allocs; + + /** Whether or not this instance should count towards pipeline + * statistics. */ + int should_collect_statistics; +}; + +/** The global state of the profiler. */ +struct halide_profiler_state { + /** Guards access to the fields below. If not locked, the sampling + * profiler thread is free to modify things below (including + * reordering the linked list of pipeline stats). */ + struct halide_mutex lock; + + /** A linked list of stats gathered for each pipeline. */ + struct halide_profiler_pipeline_stats *pipelines; + + /** Retrieve remote profiler state. Used so that the sampling + * profiler can follow along with execution that occurs elsewhere, + * e.g. on a DSP. If null, it reads from the int above instead. */ + + /** Sampling thread reference to be joined at shutdown. */ + struct halide_thread *sampling_thread; + + /** The running instances of Halide pipelines. */ + struct halide_profiler_instance_state *instances; + + /** If this callback is defined, the profiler asserts that there is a single + * live instance, and then uses it to get the current func and number of + * active threads insted of reading the fields in the instance. This is used + * so that the profiler can follow along with execution that occurs + * elsewhere (e.g. on an accelerator). */ + void (*get_remote_profiler_state)(int *func, int *active_workers); + + /** The amount of time the profiler thread sleeps between samples in + * microseconds. Defaults to 1000. To change it call + * halide_profiler_get_state and mutate this field. */ + int sleep_time; + + /** Set to 1 when you want the profiler to wait for all running instances to + * finish and then stop gracefully. */ + int shutdown; +}; + +/** Get a pointer to the global profiler state for programmatic + * inspection. Lock it before using to pause the profiler. */ +extern struct halide_profiler_state *halide_profiler_get_state(void); + +/** Get a pointer to the pipeline state associated with pipeline_name. + * This function grabs the global profiler state's lock on entry. */ +extern struct halide_profiler_pipeline_stats *halide_profiler_get_pipeline_state(const char *pipeline_name); + +/** Collects profiling information. Intended to be called from a timer + * interrupt handler if timer based profiling is being used. + * State argument is acquired via halide_profiler_get_pipeline_state. + * prev_t argument is the previous time and can be used to set a more + * accurate time interval if desired. */ +extern int halide_profiler_sample(struct halide_profiler_state *s, uint64_t *prev_t); + +/** Reset profiler state cheaply. May leave threads running or some memory + * allocated but all accumulated statistics are reset. Blocks until all running + * profiled Halide pipelines exit. */ +extern void halide_profiler_reset(void); + +/** Reset all profiler state. Blocks until all running profiled Halide + * pipelines exit. */ +extern void halide_profiler_shutdown(void); + +/** Print out timing statistics for everything run since the last + * reset. Also happens at process exit. */ +extern void halide_profiler_report(void *user_context); + +/** These routines are called to temporarily disable and then reenable + * the profiler. */ +//@{ +extern void halide_profiler_lock(struct halide_profiler_state *); +extern void halide_profiler_unlock(struct halide_profiler_state *); +//@} + +/// \name "Float16" functions +/// These functions operate of bits (``uint16_t``) representing a half +/// precision floating point number (IEEE-754 2008 binary16). +//{@ + +/** Read bits representing a half precision floating point number and return + * the float that represents the same value */ +extern float halide_float16_bits_to_float(uint16_t); + +/** Read bits representing a half precision floating point number and return + * the double that represents the same value */ +extern double halide_float16_bits_to_double(uint16_t); + +// TODO: Conversion functions to half + +//@} + +// Allocating and freeing device memory is often very slow. The +// methods below give Halide's runtime permission to hold onto device +// memory to service future requests instead of returning it to the +// underlying device API. The API does not manage an allocation pool, +// all it does is provide access to a shared counter that acts as a +// limit on the unused memory not yet returned to the underlying +// device API. It makes callbacks to participants when memory needs to +// be released because the limit is about to be exceeded (either +// because the limit has been reduced, or because the memory owned by +// some participant becomes unused). + +/** Tell Halide whether or not it is permitted to hold onto device + * allocations to service future requests instead of returning them + * eagerly to the underlying device API. Many device allocators are + * quite slow, so it can be beneficial to set this to true. The + * default value for now is false. + * + * Note that if enabled, the eviction policy is very simplistic. The + * 32 most-recently used allocations are preserved, regardless of + * their size. Additionally, if a call to cuMalloc results in an + * out-of-memory error, the entire cache is flushed and the allocation + * is retried. See https://github.com/halide/Halide/issues/4093 + * + * If set to false, releases all unused device allocations back to the + * underlying device APIs. For finer-grained control, see specific + * methods in each device api runtime. + * + * Note that if the flag is set to true, this call *must* succeed and return + * a value of halide_error_code_success (i.e., zero); if you replace + * the implementation of this call in the runtime, you must honor this contract. + * */ +extern int halide_reuse_device_allocations(void *user_context, bool); + +/** Determines whether on device_free the memory is returned + * immediately to the device API, or placed on a free list for future + * use. Override and switch based on the user_context for + * finer-grained control. By default just returns the value most + * recently set by the method above. */ +extern bool halide_can_reuse_device_allocations(void *user_context); + +struct halide_device_allocation_pool { + int (*release_unused)(void *user_context); + struct halide_device_allocation_pool *next; +}; + +/** Register a callback to be informed when + * halide_reuse_device_allocations(false) is called, and all unused + * device allocations must be released. The object passed should have + * global lifetime, and its next field will be clobbered. */ +extern void halide_register_device_allocation_pool(struct halide_device_allocation_pool *); + +#ifdef __cplusplus +} // End extern "C" +#endif + +#if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) + +namespace { + +template +struct check_is_pointer { + static constexpr bool value = false; +}; + +template +struct check_is_pointer { + static constexpr bool value = true; +}; + +} // namespace + +/** Construct the halide equivalent of a C type */ +template +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + // Create a compile-time error if T is not a pointer (without + // using any includes - this code goes into the runtime). + // (Note that we can't have uninitialized variables in constexpr functions, + // even if those variables aren't used.) + static_assert(check_is_pointer::value, "Expected a pointer type here"); + return halide_type_t(halide_type_handle, 64); +} + +#ifdef HALIDE_CPP_COMPILER_HAS_FLOAT16 +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of<_Float16>() { + return halide_type_t(halide_type_float, 16); +} +#endif + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_float, 32); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_float, 64); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_uint, 1); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_uint, 8); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_uint, 16); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_uint, 32); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_uint, 64); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_int, 8); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_int, 16); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_int, 32); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_int, 64); +} + +#ifndef COMPILING_HALIDE_RUNTIME + +// These structures are used by `function_info_header` files +// (generated by passing `-e function_info_header` to a Generator). +// The generated files contain documentation on the proper usage. +namespace HalideFunctionInfo { + +enum ArgumentKind { InputScalar = 0, + InputBuffer = 1, + OutputBuffer = 2 }; + +struct ArgumentInfo { + std::string_view name; + ArgumentKind kind; + int32_t dimensions; // always zero for scalar arguments + halide_type_t type; +}; + +} // namespace HalideFunctionInfo + +#endif // COMPILING_HALIDE_RUNTIME + +#endif // (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) + +#endif // HALIDE_HALIDERUNTIME_H + +#endif diff --git a/app/src/main/cpp/hdrplus2/arm64-v8a/hdrplus_pipeline.registration.cpp b/app/src/main/cpp/hdrplus2/arm64-v8a/hdrplus_pipeline.registration.cpp new file mode 100644 index 00000000..30760463 --- /dev/null +++ b/app/src/main/cpp/hdrplus2/arm64-v8a/hdrplus_pipeline.registration.cpp @@ -0,0 +1,36 @@ + +// MACHINE GENERATED -- DO NOT EDIT + +extern "C" { +struct halide_filter_metadata_t; +void halide_register_argv_and_metadata( + int (*filter_argv_call)(void **), + const struct halide_filter_metadata_t *filter_metadata, + const char * const *extra_key_value_pairs +); +} + +extern "C" { +extern int hdrplus_pipeline_argv(void **args); +extern const struct halide_filter_metadata_t *hdrplus_pipeline_metadata(); +} + +#ifdef HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC +extern "C" const char * const *HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC(); +#endif // HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC + +namespace halide_nsreg_hdrplus_pipeline { +namespace { +struct Registerer { + Registerer() { +#ifdef HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC + halide_register_argv_and_metadata(::hdrplus_pipeline_argv, ::hdrplus_pipeline_metadata(), HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC()); +#else + halide_register_argv_and_metadata(::hdrplus_pipeline_argv, ::hdrplus_pipeline_metadata(), nullptr); +#endif // HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC + } +}; +static Registerer registerer; +} // namespace +} // halide_nsreg_hdrplus_pipeline + diff --git a/app/src/main/cpp/hdrplus2/armeabi-v7a/hdrplus_pipeline.h b/app/src/main/cpp/hdrplus2/armeabi-v7a/hdrplus_pipeline.h new file mode 100644 index 00000000..7b02a87f --- /dev/null +++ b/app/src/main/cpp/hdrplus2/armeabi-v7a/hdrplus_pipeline.h @@ -0,0 +1,2282 @@ +#ifndef HALIDE______armeabi___v7a___hdrplus_pipeline_h +#define HALIDE______armeabi___v7a___hdrplus_pipeline_h +#include + +// Forward declarations of the types used in the interface +// to the Halide pipeline. +// +// Definitions for these structs are below. + +// Halide's representation of a multi-dimensional array. +// Halide::Runtime::Buffer is a more user-friendly wrapper +// around this. Its declaration is in HalideBuffer.h +struct halide_buffer_t; + +// Metadata describing the arguments to the generated function. +// Used to construct calls to the _argv version of the function. +struct halide_filter_metadata_t; + +#ifndef HALIDE_MUST_USE_RESULT +#ifdef __has_attribute +#if __has_attribute(nodiscard) +#define HALIDE_MUST_USE_RESULT [[nodiscard]] +#elif __has_attribute(warn_unused_result) +#define HALIDE_MUST_USE_RESULT __attribute__((warn_unused_result)) +#else +#define HALIDE_MUST_USE_RESULT +#endif +#else +#define HALIDE_MUST_USE_RESULT +#endif +#endif + +#ifndef HALIDE_FUNCTION_ATTRS +#define HALIDE_FUNCTION_ATTRS +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + +HALIDE_FUNCTION_ATTRS +int hdrplus_pipeline(struct halide_buffer_t *_inputs_buffer, uint16_t _black_point, uint16_t _white_point, float _white_balance_r, float _white_balance_g0, float _white_balance_g1, float _white_balance_b, int32_t _cfa_pattern, struct halide_buffer_t *_ccm_buffer, float _compression, float _gain, struct halide_buffer_t *_8bit_interleaved_output_buffer); + +HALIDE_FUNCTION_ATTRS +int hdrplus_pipeline_argv(void **args); + +HALIDE_FUNCTION_ATTRS +const struct halide_filter_metadata_t *hdrplus_pipeline_metadata(); + +#ifdef __cplusplus +} // extern "C" +#endif + + +// The generated object file that goes with this header +// includes a full copy of the Halide runtime so that it +// can be used standalone. Declarations for the functions +// in the Halide runtime are below. +// +// The runtime is defined using weak linkage, so it is legal +// to link multiple Halide-generated object files together, +// or to clobber any of these functions with your own +// definition. +// +// To generate an object file without a full copy of the +// runtime, use the -no_runtime target flag. To generate a +// standalone Halide runtime to use with such object files +// use the -r flag with any Halide generator binary, e.g.: +// $ ./my_generator -r halide_runtime -o . target=host + +#ifndef HALIDE_HALIDERUNTIME_H +#define HALIDE_HALIDERUNTIME_H + +#ifndef COMPILING_HALIDE_RUNTIME +#ifdef __cplusplus +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif +#else +#include "runtime_internal.h" +#endif + +// Note that the canonical Halide version is considered to be defined here +// (rather than in the build system); we redundantly define the value in +// our CMake build, so that we ensure that the in-build metadata (eg soversion) +// matches, but keeping the canonical version here makes it easier to keep +// downstream build systems (eg Blaze/Bazel) properly in sync with the source. +#define HALIDE_VERSION_MAJOR 18 +#define HALIDE_VERSION_MINOR 0 +#define HALIDE_VERSION_PATCH 0 + +#ifdef __cplusplus +// Forward declare type to allow naming typed handles. +// See Type.h for documentation. +template +struct halide_handle_traits; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +// Note that (for MSVC) you should not use "inline" along with HALIDE_ALWAYS_INLINE; +// it is not necessary, and may produce warnings for some build configurations. +#define HALIDE_ALWAYS_INLINE __forceinline +#define HALIDE_NEVER_INLINE __declspec(noinline) +#else +// Note that (for Posixy compilers) you should always use "inline" along with HALIDE_ALWAYS_INLINE; +// otherwise some corner-case scenarios may erroneously report link errors. +#define HALIDE_ALWAYS_INLINE inline __attribute__((always_inline)) +#define HALIDE_NEVER_INLINE __attribute__((noinline)) +#endif + +#ifndef HALIDE_MUST_USE_RESULT +#ifdef __has_attribute +#if __has_attribute(nodiscard) +// C++17 or later +#define HALIDE_MUST_USE_RESULT [[nodiscard]] +#elif __has_attribute(warn_unused_result) +// Clang/GCC +#define HALIDE_MUST_USE_RESULT __attribute__((warn_unused_result)) +#else +#define HALIDE_MUST_USE_RESULT +#endif +#else +#define HALIDE_MUST_USE_RESULT +#endif +#endif + +// Annotation for AOT and JIT calls -- if undefined, use no annotation. +// To ensure that all results are checked, do something like +// +// -DHALIDE_FUNCTION_ATTRS=HALIDE_MUST_USE_RESULT +// +// in your C++ compiler options +#ifndef HALIDE_FUNCTION_ATTRS +#define HALIDE_FUNCTION_ATTRS +#endif + +#ifndef HALIDE_EXPORT_SYMBOL +#ifdef _MSC_VER +#define HALIDE_EXPORT_SYMBOL __declspec(dllexport) +#else +#define HALIDE_EXPORT_SYMBOL __attribute__((visibility("default"))) +#endif +#endif + +#ifndef COMPILING_HALIDE_RUNTIME + +// ASAN builds can cause linker errors for Float16, so sniff for that and +// don't enable it by default. +#if defined(__has_feature) +#if __has_feature(address_sanitizer) +#define HALIDE_RUNTIME_ASAN_DETECTED +#endif +#endif + +#if defined(__SANITIZE_ADDRESS__) && !defined(HALIDE_RUNTIME_ASAN_DETECTED) +#define HALIDE_RUNTIME_ASAN_DETECTED +#endif + +#if !defined(HALIDE_RUNTIME_ASAN_DETECTED) + +// clang had _Float16 added as a reserved name in clang 8, but +// doesn't actually support it on most platforms until clang 15. +// Ideally there would be a better way to detect if the type +// is supported, even in a compiler independent fashion, but +// coming up with one has proven elusive. +#if defined(__clang__) && (__clang_major__ >= 15) && !defined(__EMSCRIPTEN__) && !defined(__i386__) +#if defined(__is_identifier) +#if !__is_identifier(_Float16) +#define HALIDE_CPP_COMPILER_HAS_FLOAT16 +#endif +#endif +#endif + +// Similarly, detecting _Float16 for gcc is problematic. +// For now, we say that if >= v12, and compiling on x86 or arm, +// we assume support. This may need revision. +#if defined(__GNUC__) && (__GNUC__ >= 12) +#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__) +#define HALIDE_CPP_COMPILER_HAS_FLOAT16 +#endif +#endif + +#endif // !HALIDE_RUNTIME_ASAN_DETECTED + +#endif // !COMPILING_HALIDE_RUNTIME + +/** \file + * + * This file declares the routines used by Halide internally in its + * runtime. On platforms that support weak linking, these can be + * replaced with user-defined versions by defining an extern "C" + * function with the same name and signature. + * + * When doing Just In Time (JIT) compilation members of + * some_pipeline_or_func.jit_handlers() must be replaced instead. The + * corresponding methods are documented below. + * + * All of these functions take a "void *user_context" parameter as their + * first argument; if the Halide kernel that calls back to any of these + * functions has been compiled with the UserContext feature set on its Target, + * then the value of that pointer passed from the code that calls the + * Halide kernel is piped through to the function. + * + * Some of these are also useful to call when using the default + * implementation. E.g. halide_shutdown_thread_pool. + * + * Note that even on platforms with weak linking, some linker setups + * may not respect the override you provide. E.g. if the override is + * in a shared library and the halide object files are linked directly + * into the output, the builtin versions of the runtime functions will + * be called. See your linker documentation for more details. On + * Linux, LD_DYNAMIC_WEAK=1 may help. + * + */ + +// Forward-declare to suppress warnings if compiling as C. +struct halide_buffer_t; + +/** Print a message to stderr. Main use is to support tracing + * functionality, print, and print_when calls. Also called by the default + * halide_error. This function can be replaced in JITed code by using + * halide_custom_print and providing an implementation of halide_print + * in AOT code. See Func::set_custom_print. + */ +// @{ +extern void halide_print(void *user_context, const char *); +extern void halide_default_print(void *user_context, const char *); +typedef void (*halide_print_t)(void *, const char *); +extern halide_print_t halide_set_custom_print(halide_print_t print); +// @} + +/** Halide calls this function on runtime errors (for example bounds + * checking failures). This function can be replaced in JITed code by + * using Func::set_error_handler, or in AOT code by calling + * halide_set_error_handler. In AOT code on platforms that support + * weak linking (i.e. not Windows), you can also override it by simply + * defining your own halide_error. + */ +// @{ +extern void halide_error(void *user_context, const char *); +extern void halide_default_error(void *user_context, const char *); +typedef void (*halide_error_handler_t)(void *, const char *); +extern halide_error_handler_t halide_set_error_handler(halide_error_handler_t handler); +// @} + +/** Cross-platform mutex. Must be initialized with zero and implementation + * must treat zero as an unlocked mutex with no waiters, etc. + */ +struct halide_mutex { + uintptr_t _private[1]; +}; + +/** Cross platform condition variable. Must be initialized to 0. */ +struct halide_cond { + uintptr_t _private[1]; +}; + +/** A basic set of mutex and condition variable functions, which call + * platform specific code for mutual exclusion. Equivalent to posix + * calls. */ +//@{ +extern void halide_mutex_lock(struct halide_mutex *mutex); +extern void halide_mutex_unlock(struct halide_mutex *mutex); +extern void halide_cond_signal(struct halide_cond *cond); +extern void halide_cond_broadcast(struct halide_cond *cond); +extern void halide_cond_wait(struct halide_cond *cond, struct halide_mutex *mutex); +//@} + +/** Functions for constructing/destroying/locking/unlocking arrays of mutexes. */ +struct halide_mutex_array; +//@{ +extern struct halide_mutex_array *halide_mutex_array_create(uint64_t sz); +extern void halide_mutex_array_destroy(void *user_context, void *array); +extern int halide_mutex_array_lock(struct halide_mutex_array *array, int entry); +extern int halide_mutex_array_unlock(struct halide_mutex_array *array, int entry); +//@} + +/** Define halide_do_par_for to replace the default thread pool + * implementation. halide_shutdown_thread_pool can also be called to + * release resources used by the default thread pool on platforms + * where it makes sense. See Func::set_custom_do_task and + * Func::set_custom_do_par_for. Should return zero if all the jobs + * return zero, or an arbitrarily chosen return value from one of the + * jobs otherwise. + */ +//@{ +typedef int (*halide_task_t)(void *user_context, int task_number, uint8_t *closure); +extern int halide_do_par_for(void *user_context, + halide_task_t task, + int min, int size, uint8_t *closure); +extern void halide_shutdown_thread_pool(void); +//@} + +/** Set a custom method for performing a parallel for loop. Returns + * the old do_par_for handler. */ +typedef int (*halide_do_par_for_t)(void *, halide_task_t, int, int, uint8_t *); +extern halide_do_par_for_t halide_set_custom_do_par_for(halide_do_par_for_t do_par_for); + +/** An opaque struct representing a semaphore. Used by the task system for async tasks. */ +struct halide_semaphore_t { + uint64_t _private[2]; +}; + +/** A struct representing a semaphore and a number of items that must + * be acquired from it. Used in halide_parallel_task_t below. */ +struct halide_semaphore_acquire_t { + struct halide_semaphore_t *semaphore; + int count; +}; +extern int halide_semaphore_init(struct halide_semaphore_t *, int n); +extern int halide_semaphore_release(struct halide_semaphore_t *, int n); +extern bool halide_semaphore_try_acquire(struct halide_semaphore_t *, int n); +typedef int (*halide_semaphore_init_t)(struct halide_semaphore_t *, int); +typedef int (*halide_semaphore_release_t)(struct halide_semaphore_t *, int); +typedef bool (*halide_semaphore_try_acquire_t)(struct halide_semaphore_t *, int); + +/** A task representing a serial for loop evaluated over some range. + * Note that task_parent is a pass through argument that should be + * passed to any dependent taks that are invoked using halide_do_parallel_tasks + * underneath this call. */ +typedef int (*halide_loop_task_t)(void *user_context, int min, int extent, + uint8_t *closure, void *task_parent); + +/** A parallel task to be passed to halide_do_parallel_tasks. This + * task may recursively call halide_do_parallel_tasks, and there may + * be complex dependencies between seemingly unrelated tasks expressed + * using semaphores. If you are using a custom task system, care must + * be taken to avoid potential deadlock. This can be done by carefully + * respecting the static metadata at the end of the task struct.*/ +struct halide_parallel_task_t { + // The function to call. It takes a user context, a min and + // extent, a closure, and a task system pass through argument. + halide_loop_task_t fn; + + // The closure to pass it + uint8_t *closure; + + // The name of the function to be called. For debugging purposes only. + const char *name; + + // An array of semaphores that must be acquired before the + // function is called. Must be reacquired for every call made. + struct halide_semaphore_acquire_t *semaphores; + int num_semaphores; + + // The entire range the function should be called over. This range + // may be sliced up and the function called multiple times. + int min, extent; + + // A parallel task provides several pieces of metadata to prevent + // unbounded resource usage or deadlock. + + // The first is the minimum number of execution contexts (call + // stacks or threads) necessary for the function to run to + // completion. This may be greater than one when there is nested + // parallelism with internal producer-consumer relationships + // (calling the function recursively spawns and blocks on parallel + // sub-tasks that communicate with each other via semaphores). If + // a parallel runtime calls the function when fewer than this many + // threads are idle, it may need to create more threads to + // complete the task, or else risk deadlock due to committing all + // threads to tasks that cannot complete without more. + // + // FIXME: Note that extern stages are assumed to only require a + // single thread to complete. If the extern stage is itself a + // Halide pipeline, this may be an underestimate. + int min_threads; + + // The calls to the function should be in serial order from min to min+extent-1, with only + // one executing at a time. If false, any order is fine, and + // concurrency is fine. + bool serial; +}; + +/** Enqueue some number of the tasks described above and wait for them + * to complete. While waiting, the calling threads assists with either + * the tasks enqueued, or other non-blocking tasks in the task + * system. Note that task_parent should be NULL for top-level calls + * and the pass through argument if this call is being made from + * another task. */ +extern int halide_do_parallel_tasks(void *user_context, int num_tasks, + struct halide_parallel_task_t *tasks, + void *task_parent); + +/** If you use the default do_par_for, you can still set a custom + * handler to perform each individual task. Returns the old handler. */ +//@{ +typedef int (*halide_do_task_t)(void *, halide_task_t, int, uint8_t *); +extern halide_do_task_t halide_set_custom_do_task(halide_do_task_t do_task); +extern int halide_do_task(void *user_context, halide_task_t f, int idx, + uint8_t *closure); +//@} + +/** The version of do_task called for loop tasks. By default calls the + * loop task with the same arguments. */ +// @{ +typedef int (*halide_do_loop_task_t)(void *, halide_loop_task_t, int, int, uint8_t *, void *); +extern halide_do_loop_task_t halide_set_custom_do_loop_task(halide_do_loop_task_t do_task); +extern int halide_do_loop_task(void *user_context, halide_loop_task_t f, int min, int extent, + uint8_t *closure, void *task_parent); +//@} + +/** Provide an entire custom tasking runtime via function + * pointers. Note that do_task and semaphore_try_acquire are only ever + * called by halide_default_do_par_for and + * halide_default_do_parallel_tasks, so it's only necessary to provide + * those if you are mixing in the default implementations of + * do_par_for and do_parallel_tasks. */ +// @{ +typedef int (*halide_do_parallel_tasks_t)(void *, int, struct halide_parallel_task_t *, + void *task_parent); +extern void halide_set_custom_parallel_runtime( + halide_do_par_for_t, + halide_do_task_t, + halide_do_loop_task_t, + halide_do_parallel_tasks_t, + halide_semaphore_init_t, + halide_semaphore_try_acquire_t, + halide_semaphore_release_t); +// @} + +/** The default versions of the parallel runtime functions. */ +// @{ +extern int halide_default_do_par_for(void *user_context, + halide_task_t task, + int min, int size, uint8_t *closure); +extern int halide_default_do_parallel_tasks(void *user_context, + int num_tasks, + struct halide_parallel_task_t *tasks, + void *task_parent); +extern int halide_default_do_task(void *user_context, halide_task_t f, int idx, + uint8_t *closure); +extern int halide_default_do_loop_task(void *user_context, halide_loop_task_t f, + int min, int extent, + uint8_t *closure, void *task_parent); +extern int halide_default_semaphore_init(struct halide_semaphore_t *, int n); +extern int halide_default_semaphore_release(struct halide_semaphore_t *, int n); +extern bool halide_default_semaphore_try_acquire(struct halide_semaphore_t *, int n); +// @} + +struct halide_thread; + +/** Spawn a thread. Returns a handle to the thread for the purposes of + * joining it. The thread must be joined in order to clean up any + * resources associated with it. */ +extern struct halide_thread *halide_spawn_thread(void (*f)(void *), void *closure); + +/** Join a thread. */ +extern void halide_join_thread(struct halide_thread *); + +/** Set the number of threads used by Halide's thread pool. Returns + * the old number. + * + * n < 0 : error condition + * n == 0 : use a reasonable system default (typically, number of cpus online). + * n == 1 : use exactly one thread; this will always enforce serial execution + * n > 1 : use a pool of exactly n threads. + * + * (Note that this is only guaranteed when using the default implementations + * of halide_do_par_for(); custom implementations may completely ignore values + * passed to halide_set_num_threads().) + */ +extern int halide_set_num_threads(int n); + +/** Halide calls these functions to allocate and free memory. To + * replace in AOT code, use the halide_set_custom_malloc and + * halide_set_custom_free, or (on platforms that support weak + * linking), simply define these functions yourself. In JIT-compiled + * code use Func::set_custom_allocator. + * + * If you override them, and find yourself wanting to call the default + * implementation from within your override, use + * halide_default_malloc/free. + * + * Note that halide_malloc must return a pointer aligned to the + * maximum meaningful alignment for the platform for the purpose of + * vector loads and stores, *and* with an allocated size that is (at least) + * an integral multiple of that same alignment. The default implementation + * uses 32-byte alignment on arm and 64-byte alignment on x86. Additionally, + * it must be safe to read at least 8 bytes before the start and beyond the end. + */ +//@{ +extern void *halide_malloc(void *user_context, size_t x); +extern void halide_free(void *user_context, void *ptr); +extern void *halide_default_malloc(void *user_context, size_t x); +extern void halide_default_free(void *user_context, void *ptr); +typedef void *(*halide_malloc_t)(void *, size_t); +typedef void (*halide_free_t)(void *, void *); +extern halide_malloc_t halide_set_custom_malloc(halide_malloc_t user_malloc); +extern halide_free_t halide_set_custom_free(halide_free_t user_free); +//@} + +/** Halide calls these functions to interact with the underlying + * system runtime functions. To replace in AOT code on platforms that + * support weak linking, define these functions yourself, or use + * the halide_set_custom_load_library() and halide_set_custom_get_library_symbol() + * functions. In JIT-compiled code, use JITSharedRuntime::set_default_handlers(). + * + * halide_load_library and halide_get_library_symbol are equivalent to + * dlopen and dlsym. halide_get_symbol(sym) is equivalent to + * dlsym(RTLD_DEFAULT, sym). + */ +//@{ +extern void *halide_get_symbol(const char *name); +extern void *halide_load_library(const char *name); +extern void *halide_get_library_symbol(void *lib, const char *name); +extern void *halide_default_get_symbol(const char *name); +extern void *halide_default_load_library(const char *name); +extern void *halide_default_get_library_symbol(void *lib, const char *name); +typedef void *(*halide_get_symbol_t)(const char *name); +typedef void *(*halide_load_library_t)(const char *name); +typedef void *(*halide_get_library_symbol_t)(void *lib, const char *name); +extern halide_get_symbol_t halide_set_custom_get_symbol(halide_get_symbol_t user_get_symbol); +extern halide_load_library_t halide_set_custom_load_library(halide_load_library_t user_load_library); +extern halide_get_library_symbol_t halide_set_custom_get_library_symbol(halide_get_library_symbol_t user_get_library_symbol); +//@} + +/** Called when debug_to_file is used inside %Halide code. See + * Func::debug_to_file for how this is called + * + * Cannot be replaced in JITted code at present. + */ +extern int32_t halide_debug_to_file(void *user_context, const char *filename, + struct halide_buffer_t *buf); + +/** Types in the halide type system. They can be ints, unsigned ints, + * or floats (of various bit-widths), or a handle (which is always 64-bits). + * Note that the int/uint/float values do not imply a specific bit width + * (the bit width is expected to be encoded in a separate value). + */ +typedef enum halide_type_code_t +#if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) + : uint8_t +#endif +{ + halide_type_int = 0, ///< signed integers + halide_type_uint = 1, ///< unsigned integers + halide_type_float = 2, ///< IEEE floating point numbers + halide_type_handle = 3, ///< opaque pointer type (void *) + halide_type_bfloat = 4, ///< floating point numbers in the bfloat format +} halide_type_code_t; + +// Note that while __attribute__ can go before or after the declaration, +// __declspec apparently is only allowed before. +#ifndef HALIDE_ATTRIBUTE_ALIGN +#ifdef _MSC_VER +#define HALIDE_ATTRIBUTE_ALIGN(x) __declspec(align(x)) +#else +#define HALIDE_ATTRIBUTE_ALIGN(x) __attribute__((aligned(x))) +#endif +#endif + +/** A runtime tag for a type in the halide type system. Can be ints, + * unsigned ints, or floats of various bit-widths (the 'bits' + * field). Can also be vectors of the same (by setting the 'lanes' + * field to something larger than one). This struct should be + * exactly 32-bits in size. */ +struct halide_type_t { + /** The basic type code: signed integer, unsigned integer, or floating point. */ +#if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) + HALIDE_ATTRIBUTE_ALIGN(1) + halide_type_code_t code; // halide_type_code_t +#else + HALIDE_ATTRIBUTE_ALIGN(1) + uint8_t code; // halide_type_code_t +#endif + + /** The number of bits of precision of a single scalar value of this type. */ + HALIDE_ATTRIBUTE_ALIGN(1) + uint8_t bits; + + /** How many elements in a vector. This is 1 for scalar types. */ + HALIDE_ATTRIBUTE_ALIGN(2) + uint16_t lanes; + +#if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) + /** Construct a runtime representation of a Halide type from: + * code: The fundamental type from an enum. + * bits: The bit size of one element. + * lanes: The number of vector elements in the type. */ + HALIDE_ALWAYS_INLINE constexpr halide_type_t(halide_type_code_t code, uint8_t bits, uint16_t lanes = 1) + : code(code), bits(bits), lanes(lanes) { + } + + /** Default constructor is required e.g. to declare halide_trace_event + * instances. */ + HALIDE_ALWAYS_INLINE constexpr halide_type_t() + : code((halide_type_code_t)0), bits(0), lanes(0) { + } + + HALIDE_ALWAYS_INLINE constexpr halide_type_t with_lanes(uint16_t new_lanes) const { + return halide_type_t((halide_type_code_t)code, bits, new_lanes); + } + + HALIDE_ALWAYS_INLINE constexpr halide_type_t element_of() const { + return with_lanes(1); + } + /** Compare two types for equality. */ + HALIDE_ALWAYS_INLINE constexpr bool operator==(const halide_type_t &other) const { + return as_u32() == other.as_u32(); + } + + HALIDE_ALWAYS_INLINE constexpr bool operator!=(const halide_type_t &other) const { + return !(*this == other); + } + + HALIDE_ALWAYS_INLINE constexpr bool operator<(const halide_type_t &other) const { + return as_u32() < other.as_u32(); + } + + /** Size in bytes for a single element, even if width is not 1, of this type. */ + HALIDE_ALWAYS_INLINE constexpr int bytes() const { + return (bits + 7) / 8; + } + + HALIDE_ALWAYS_INLINE constexpr uint32_t as_u32() const { + // Note that this produces a result that is identical to memcpy'ing 'this' + // into a u32 (on a little-endian machine, anyway), and at -O1 or greater + // on Clang, the compiler knows this and optimizes this into a single 32-bit move. + // (At -O0 it will look awful.) + return static_cast(code) | + (static_cast(bits) << 8) | + (static_cast(lanes) << 16); + } +#endif +}; + +#if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) +static_assert(sizeof(halide_type_t) == sizeof(uint32_t), "size mismatch in halide_type_t"); +#endif + +enum halide_trace_event_code_t { halide_trace_load = 0, + halide_trace_store = 1, + halide_trace_begin_realization = 2, + halide_trace_end_realization = 3, + halide_trace_produce = 4, + halide_trace_end_produce = 5, + halide_trace_consume = 6, + halide_trace_end_consume = 7, + halide_trace_begin_pipeline = 8, + halide_trace_end_pipeline = 9, + halide_trace_tag = 10 }; + +struct halide_trace_event_t { + /** The name of the Func or Pipeline that this event refers to */ + const char *func; + + /** If the event type is a load or a store, this points to the + * value being loaded or stored. Use the type field to safely cast + * this to a concrete pointer type and retrieve it. For other + * events this is null. */ + void *value; + + /** For loads and stores, an array which contains the location + * being accessed. For vector loads or stores it is an array of + * vectors of coordinates (the vector dimension is innermost). + * + * For realization or production-related events, this will contain + * the mins and extents of the region being accessed, in the order + * min0, extent0, min1, extent1, ... + * + * For pipeline-related events, this will be null. + */ + int32_t *coordinates; + + /** For halide_trace_tag, this points to a read-only null-terminated string + * of arbitrary text. For all other events, this will be null. + */ + const char *trace_tag; + + /** If the event type is a load or a store, this is the type of + * the data. Otherwise, the value is meaningless. */ + struct halide_type_t type; + + /** The type of event */ + enum halide_trace_event_code_t event; + + /* The ID of the parent event (see below for an explanation of + * event ancestry). */ + int32_t parent_id; + + /** If this was a load or store of a Tuple-valued Func, this is + * which tuple element was accessed. */ + int32_t value_index; + + /** The length of the coordinates array */ + int32_t dimensions; +}; + +/** Called when Funcs are marked as trace_load, trace_store, or + * trace_realization. See Func::set_custom_trace. The default + * implementation either prints events via halide_print, or if + * HL_TRACE_FILE is defined, dumps the trace to that file in a + * sequence of trace packets. The header for a trace packet is defined + * below. If the trace is going to be large, you may want to make the + * file a named pipe, and then read from that pipe into gzip. + * + * halide_trace returns a unique ID which will be passed to future + * events that "belong" to the earlier event as the parent id. The + * ownership hierarchy looks like: + * + * begin_pipeline + * +--trace_tag (if any) + * +--trace_tag (if any) + * ... + * +--begin_realization + * | +--produce + * | | +--load/store + * | | +--end_produce + * | +--consume + * | | +--load + * | | +--end_consume + * | +--end_realization + * +--end_pipeline + * + * Threading means that ownership cannot be inferred from the ordering + * of events. There can be many active realizations of a given + * function, or many active productions for a single + * realization. Within a single production, the ordering of events is + * meaningful. + * + * Note that all trace_tag events (if any) will occur just after the begin_pipeline + * event, but before any begin_realization events. All trace_tags for a given Func + * will be emitted in the order added. + */ +// @} +extern int32_t halide_trace(void *user_context, const struct halide_trace_event_t *event); +extern int32_t halide_default_trace(void *user_context, const struct halide_trace_event_t *event); +typedef int32_t (*halide_trace_t)(void *user_context, const struct halide_trace_event_t *); +extern halide_trace_t halide_set_custom_trace(halide_trace_t trace); +// @} + +/** The header of a packet in a binary trace. All fields are 32-bit. */ +struct halide_trace_packet_t { + /** The total size of this packet in bytes. Always a multiple of + * four. Equivalently, the number of bytes until the next + * packet. */ + uint32_t size; + + /** The id of this packet (for the purpose of parent_id). */ + int32_t id; + + /** The remaining fields are equivalent to those in halide_trace_event_t */ + // @{ + struct halide_type_t type; + enum halide_trace_event_code_t event; + int32_t parent_id; + int32_t value_index; + int32_t dimensions; + // @} + +#if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) + /** Get the coordinates array, assuming this packet is laid out in + * memory as it was written. The coordinates array comes + * immediately after the packet header. */ + HALIDE_ALWAYS_INLINE const int *coordinates() const { + return (const int *)(this + 1); + } + + HALIDE_ALWAYS_INLINE int *coordinates() { + return (int *)(this + 1); + } + + /** Get the value, assuming this packet is laid out in memory as + * it was written. The packet comes immediately after the coordinates + * array. */ + HALIDE_ALWAYS_INLINE const void *value() const { + return (const void *)(coordinates() + dimensions); + } + + HALIDE_ALWAYS_INLINE void *value() { + return (void *)(coordinates() + dimensions); + } + + /** Get the func name, assuming this packet is laid out in memory + * as it was written. It comes after the value. */ + HALIDE_ALWAYS_INLINE const char *func() const { + return (const char *)value() + type.lanes * type.bytes(); + } + + HALIDE_ALWAYS_INLINE char *func() { + return (char *)value() + type.lanes * type.bytes(); + } + + /** Get the trace_tag (if any), assuming this packet is laid out in memory + * as it was written. It comes after the func name. If there is no trace_tag, + * this will return a pointer to an empty string. */ + HALIDE_ALWAYS_INLINE const char *trace_tag() const { + const char *f = func(); + // strlen may not be available here + while (*f++) { + // nothing + } + return f; + } + + HALIDE_ALWAYS_INLINE char *trace_tag() { + char *f = func(); + // strlen may not be available here + while (*f++) { + // nothing + } + return f; + } +#endif +}; + +/** Set the file descriptor that Halide should write binary trace + * events to. If called with 0 as the argument, Halide outputs trace + * information to stdout in a human-readable format. If never called, + * Halide checks the for existence of an environment variable called + * HL_TRACE_FILE and opens that file. If HL_TRACE_FILE is not defined, + * it outputs trace information to stdout in a human-readable + * format. */ +extern void halide_set_trace_file(int fd); + +/** Halide calls this to retrieve the file descriptor to write binary + * trace events to. The default implementation returns the value set + * by halide_set_trace_file. Implement it yourself if you wish to use + * a custom file descriptor per user_context. Return zero from your + * implementation to tell Halide to print human-readable trace + * information to stdout. */ +extern int halide_get_trace_file(void *user_context); + +/** If tracing is writing to a file. This call closes that file + * (flushing the trace). Returns zero on success. */ +extern int halide_shutdown_trace(void); + +/** All Halide GPU or device backend implementations provide an + * interface to be used with halide_device_malloc, etc. This is + * accessed via the functions below. + */ + +/** An opaque struct containing per-GPU API implementations of the + * device functions. */ +struct halide_device_interface_impl_t; + +/** Each GPU API provides a halide_device_interface_t struct pointing + * to the code that manages device allocations. You can access these + * functions directly from the struct member function pointers, or by + * calling the functions declared below. Note that the global + * functions are not available when using Halide as a JIT compiler. + * If you are using raw halide_buffer_t in that context you must use + * the function pointers in the device_interface struct. + * + * The function pointers below are currently the same for every GPU + * API; only the impl field varies. These top-level functions do the + * bookkeeping that is common across all GPU APIs, and then dispatch + * to more API-specific functions via another set of function pointers + * hidden inside the impl field. + */ +struct halide_device_interface_t { + int (*device_malloc)(void *user_context, struct halide_buffer_t *buf, + const struct halide_device_interface_t *device_interface); + int (*device_free)(void *user_context, struct halide_buffer_t *buf); + int (*device_sync)(void *user_context, struct halide_buffer_t *buf); + void (*device_release)(void *user_context, + const struct halide_device_interface_t *device_interface); + int (*copy_to_host)(void *user_context, struct halide_buffer_t *buf); + int (*copy_to_device)(void *user_context, struct halide_buffer_t *buf, + const struct halide_device_interface_t *device_interface); + int (*device_and_host_malloc)(void *user_context, struct halide_buffer_t *buf, + const struct halide_device_interface_t *device_interface); + int (*device_and_host_free)(void *user_context, struct halide_buffer_t *buf); + int (*buffer_copy)(void *user_context, struct halide_buffer_t *src, + const struct halide_device_interface_t *dst_device_interface, struct halide_buffer_t *dst); + int (*device_crop)(void *user_context, const struct halide_buffer_t *src, + struct halide_buffer_t *dst); + int (*device_slice)(void *user_context, const struct halide_buffer_t *src, + int slice_dim, int slice_pos, struct halide_buffer_t *dst); + int (*device_release_crop)(void *user_context, struct halide_buffer_t *buf); + int (*wrap_native)(void *user_context, struct halide_buffer_t *buf, uint64_t handle, + const struct halide_device_interface_t *device_interface); + int (*detach_native)(void *user_context, struct halide_buffer_t *buf); + int (*compute_capability)(void *user_context, int *major, int *minor); + const struct halide_device_interface_impl_t *impl; +}; + +/** Release all data associated with the given device interface, in + * particular all resources (memory, texture, context handles) + * allocated by Halide. Must be called explicitly when using AOT + * compilation. This is *not* thread-safe with respect to actively + * running Halide code. Ensure all pipelines are finished before + * calling this. */ +extern void halide_device_release(void *user_context, + const struct halide_device_interface_t *device_interface); + +/** Copy image data from device memory to host memory. This must be called + * explicitly to copy back the results of a GPU-based filter. */ +extern int halide_copy_to_host(void *user_context, struct halide_buffer_t *buf); + +/** Copy image data from host memory to device memory. This should not + * be called directly; Halide handles copying to the device + * automatically. If interface is NULL and the buf has a non-zero dev + * field, the device associated with the dev handle will be + * used. Otherwise if the dev field is 0 and interface is NULL, an + * error is returned. */ +extern int halide_copy_to_device(void *user_context, struct halide_buffer_t *buf, + const struct halide_device_interface_t *device_interface); + +/** Copy data from one buffer to another. The buffers may have + * different shapes and sizes, but the destination buffer's shape must + * be contained within the source buffer's shape. That is, for each + * dimension, the min on the destination buffer must be greater than + * or equal to the min on the source buffer, and min+extent on the + * destination buffer must be less that or equal to min+extent on the + * source buffer. The source data is pulled from either device or + * host memory on the source, depending on the dirty flags. host is + * preferred if both are valid. The dst_device_interface parameter + * controls the destination memory space. NULL means host memory. */ +extern int halide_buffer_copy(void *user_context, struct halide_buffer_t *src, + const struct halide_device_interface_t *dst_device_interface, + struct halide_buffer_t *dst); + +/** Give the destination buffer a device allocation which is an alias + * for the same coordinate range in the source buffer. Modifies the + * device, device_interface, and the device_dirty flag only. Only + * supported by some device APIs (others will return + * halide_error_code_device_crop_unsupported). Call + * halide_device_release_crop instead of halide_device_free to clean + * up resources associated with the cropped view. Do not free the + * device allocation on the source buffer while the destination buffer + * still lives. Note that the two buffers do not share dirty flags, so + * care must be taken to update them together as needed. Note that src + * and dst are required to have the same number of dimensions. + * + * Note also that (in theory) device interfaces which support cropping may + * still not support cropping a crop (instead, create a new crop of the parent + * buffer); in practice, no known implementation has this limitation, although + * it is possible that some future implementations may require it. */ +extern int halide_device_crop(void *user_context, + const struct halide_buffer_t *src, + struct halide_buffer_t *dst); + +/** Give the destination buffer a device allocation which is an alias + * for a similar coordinate range in the source buffer, but with one dimension + * sliced away in the dst. Modifies the device, device_interface, and the + * device_dirty flag only. Only supported by some device APIs (others will return + * halide_error_code_device_crop_unsupported). Call + * halide_device_release_crop instead of halide_device_free to clean + * up resources associated with the sliced view. Do not free the + * device allocation on the source buffer while the destination buffer + * still lives. Note that the two buffers do not share dirty flags, so + * care must be taken to update them together as needed. Note that the dst buffer + * must have exactly one fewer dimension than the src buffer, and that slice_dim + * and slice_pos must be valid within src. */ +extern int halide_device_slice(void *user_context, + const struct halide_buffer_t *src, + int slice_dim, int slice_pos, + struct halide_buffer_t *dst); + +/** Release any resources associated with a cropped/sliced view of another + * buffer. */ +extern int halide_device_release_crop(void *user_context, + struct halide_buffer_t *buf); + +/** Wait for current GPU operations to complete. Calling this explicitly + * should rarely be necessary, except maybe for profiling. */ +extern int halide_device_sync(void *user_context, struct halide_buffer_t *buf); + +/** + * Wait for current GPU operations to complete. Calling this explicitly + * should rarely be necessary, except maybe for profiling. + * This variation of the synchronizing is useful when a synchronization is desirable + * without specifying any buffer to synchronize on. + * Calling this with a null device_interface is always illegal. + */ +extern int halide_device_sync_global(void *user_context, const struct halide_device_interface_t *device_interface); + +/** Allocate device memory to back a halide_buffer_t. */ +extern int halide_device_malloc(void *user_context, struct halide_buffer_t *buf, + const struct halide_device_interface_t *device_interface); + +/** Free device memory. */ +extern int halide_device_free(void *user_context, struct halide_buffer_t *buf); + +/** Wrap or detach a native device handle, setting the device field + * and device_interface field as appropriate for the given GPU + * API. The meaning of the opaque handle is specific to the device + * interface, so if you know the device interface in use, call the + * more specific functions in the runtime headers for your specific + * device API instead (e.g. HalideRuntimeCuda.h). */ +// @{ +extern int halide_device_wrap_native(void *user_context, + struct halide_buffer_t *buf, + uint64_t handle, + const struct halide_device_interface_t *device_interface); +extern int halide_device_detach_native(void *user_context, struct halide_buffer_t *buf); +// @} + +/** Selects which gpu device to use. 0 is usually the display + * device. If never called, Halide uses the environment variable + * HL_GPU_DEVICE. If that variable is unset, Halide uses the last + * device. Set this to -1 to use the last device. */ +extern void halide_set_gpu_device(int n); + +/** Halide calls this to get the desired halide gpu device + * setting. Implement this yourself to use a different gpu device per + * user_context. The default implementation returns the value set by + * halide_set_gpu_device, or the environment variable + * HL_GPU_DEVICE. */ +extern int halide_get_gpu_device(void *user_context); + +/** Set the soft maximum amount of memory, in bytes, that the LRU + * cache will use to memoize Func results. This is not a strict + * maximum in that concurrency and simultaneous use of memoized + * reults larger than the cache size can both cause it to + * temporariliy be larger than the size specified here. + */ +extern void halide_memoization_cache_set_size(int64_t size); + +/** Given a cache key for a memoized result, currently constructed + * from the Func name and top-level Func name plus the arguments of + * the computation, determine if the result is in the cache and + * return it if so. (The internals of the cache key should be + * considered opaque by this function.) If this routine returns true, + * it is a cache miss. Otherwise, it will return false and the + * buffers passed in will be filled, via copying, with memoized + * data. The last argument is a list if halide_buffer_t pointers which + * represents the outputs of the memoized Func. If the Func does not + * return a Tuple, there will only be one halide_buffer_t in the list. The + * tuple_count parameters determines the length of the list. + * + * The return values are: + * -1: Signals an error. + * 0: Success and cache hit. + * 1: Success and cache miss. + */ +extern int halide_memoization_cache_lookup(void *user_context, const uint8_t *cache_key, int32_t size, + struct halide_buffer_t *realized_bounds, + int32_t tuple_count, struct halide_buffer_t **tuple_buffers); + +/** Given a cache key for a memoized result, currently constructed + * from the Func name and top-level Func name plus the arguments of + * the computation, store the result in the cache for futre access by + * halide_memoization_cache_lookup. (The internals of the cache key + * should be considered opaque by this function.) Data is copied out + * from the inputs and inputs are unmodified. The last argument is a + * list if halide_buffer_t pointers which represents the outputs of the + * memoized Func. If the Func does not return a Tuple, there will + * only be one halide_buffer_t in the list. The tuple_count parameters + * determines the length of the list. + * + * If there is a memory allocation failure, the store does not store + * the data into the cache. + * + * If has_eviction_key is true, the entry is marked with eviction_key to + * allow removing the key with halide_memoization_cache_evict. + */ +extern int halide_memoization_cache_store(void *user_context, const uint8_t *cache_key, int32_t size, + struct halide_buffer_t *realized_bounds, + int32_t tuple_count, + struct halide_buffer_t **tuple_buffers, + bool has_eviction_key, uint64_t eviction_key); + +/** Evict all cache entries that were tagged with the given + * eviction_key in the memoize scheduling directive. + */ +extern void halide_memoization_cache_evict(void *user_context, uint64_t eviction_key); + +/** If halide_memoization_cache_lookup succeeds, + * halide_memoization_cache_release must be called to signal the + * storage is no longer being used by the caller. It will be passed + * the host pointer of one the buffers returned by + * halide_memoization_cache_lookup. That is + * halide_memoization_cache_release will be called multiple times for + * the case where halide_memoization_cache_lookup is handling multiple + * buffers. (This corresponds to memoizing a Tuple in Halide.) Note + * that the host pointer must be sufficient to get to all information + * the release operation needs. The default Halide cache impleemntation + * accomplishes this by storing extra data before the start of the user + * modifiable host storage. + * + * This call is like free and does not have a failure return. + */ +extern void halide_memoization_cache_release(void *user_context, void *host); + +/** Free all memory and resources associated with the memoization cache. + * Must be called at a time when no other threads are accessing the cache. + */ +extern void halide_memoization_cache_cleanup(void); + +/** Verify that a given range of memory has been initialized; only used when Target::MSAN is enabled. + * + * The default implementation simply calls the LLVM-provided __msan_check_mem_is_initialized() function. + * + * The return value should always be zero. + */ +extern int halide_msan_check_memory_is_initialized(void *user_context, const void *ptr, uint64_t len, const char *name); + +/** Verify that the data pointed to by the halide_buffer_t is initialized (but *not* the halide_buffer_t itself), + * using halide_msan_check_memory_is_initialized() for checking. + * + * The default implementation takes pains to only check the active memory ranges + * (skipping padding), and sorting into ranges to always check the smallest number of + * ranges, in monotonically increasing memory order. + * + * Most client code should never need to replace the default implementation. + * + * The return value should always be zero. + */ +extern int halide_msan_check_buffer_is_initialized(void *user_context, struct halide_buffer_t *buffer, const char *buf_name); + +/** Annotate that a given range of memory has been initialized; + * only used when Target::MSAN is enabled. + * + * The default implementation simply calls the LLVM-provided __msan_unpoison() function. + * + * The return value should always be zero. + */ +extern int halide_msan_annotate_memory_is_initialized(void *user_context, const void *ptr, uint64_t len); + +/** Mark the data pointed to by the halide_buffer_t as initialized (but *not* the halide_buffer_t itself), + * using halide_msan_annotate_memory_is_initialized() for marking. + * + * The default implementation takes pains to only mark the active memory ranges + * (skipping padding), and sorting into ranges to always mark the smallest number of + * ranges, in monotonically increasing memory order. + * + * Most client code should never need to replace the default implementation. + * + * The return value should always be zero. + */ +extern int halide_msan_annotate_buffer_is_initialized(void *user_context, struct halide_buffer_t *buffer); +extern void halide_msan_annotate_buffer_is_initialized_as_destructor(void *user_context, void *buffer); + +/** The error codes that may be returned by a Halide pipeline. */ +enum halide_error_code_t { + /** There was no error. This is the value returned by Halide on success. */ + halide_error_code_success = 0, + + /** An uncategorized error occurred. Refer to the string passed to halide_error. */ + halide_error_code_generic_error = -1, + + /** A Func was given an explicit bound via Func::bound, but this + * was not large enough to encompass the region that is used of + * the Func by the rest of the pipeline. */ + halide_error_code_explicit_bounds_too_small = -2, + + /** The elem_size field of a halide_buffer_t does not match the size in + * bytes of the type of that ImageParam. Probable type mismatch. */ + halide_error_code_bad_type = -3, + + /** A pipeline would access memory outside of the halide_buffer_t passed + * in. */ + halide_error_code_access_out_of_bounds = -4, + + /** A halide_buffer_t was given that spans more than 2GB of memory. */ + halide_error_code_buffer_allocation_too_large = -5, + + /** A halide_buffer_t was given with extents that multiply to a number + * greater than 2^31-1 */ + halide_error_code_buffer_extents_too_large = -6, + + /** Applying explicit constraints on the size of an input or + * output buffer shrank the size of that buffer below what will be + * accessed by the pipeline. */ + halide_error_code_constraints_make_required_region_smaller = -7, + + /** A constraint on a size or stride of an input or output buffer + * was not met by the halide_buffer_t passed in. */ + halide_error_code_constraint_violated = -8, + + /** A scalar parameter passed in was smaller than its minimum + * declared value. */ + halide_error_code_param_too_small = -9, + + /** A scalar parameter passed in was greater than its minimum + * declared value. */ + halide_error_code_param_too_large = -10, + + /** A call to halide_malloc returned NULL. */ + halide_error_code_out_of_memory = -11, + + /** A halide_buffer_t pointer passed in was NULL. */ + halide_error_code_buffer_argument_is_null = -12, + + /** debug_to_file failed to open or write to the specified + * file. */ + halide_error_code_debug_to_file_failed = -13, + + /** The Halide runtime encountered an error while trying to copy + * from device to host. Turn on -debug in your target string to + * see more details. */ + halide_error_code_copy_to_host_failed = -14, + + /** The Halide runtime encountered an error while trying to copy + * from host to device. Turn on -debug in your target string to + * see more details. */ + halide_error_code_copy_to_device_failed = -15, + + /** The Halide runtime encountered an error while trying to + * allocate memory on device. Turn on -debug in your target string + * to see more details. */ + halide_error_code_device_malloc_failed = -16, + + /** The Halide runtime encountered an error while trying to + * synchronize with a device. Turn on -debug in your target string + * to see more details. */ + halide_error_code_device_sync_failed = -17, + + /** The Halide runtime encountered an error while trying to free a + * device allocation. Turn on -debug in your target string to see + * more details. */ + halide_error_code_device_free_failed = -18, + + /** Buffer has a non-zero device but no device interface, which + * violates a Halide invariant. */ + halide_error_code_no_device_interface = -19, + + /** This part of the Halide runtime is unimplemented on this platform. */ + halide_error_code_unimplemented = -20, + + /** A runtime symbol could not be loaded. */ + halide_error_code_symbol_not_found = -21, + + /** There is a bug in the Halide compiler. */ + halide_error_code_internal_error = -22, + + /** The Halide runtime encountered an error while trying to launch + * a GPU kernel. Turn on -debug in your target string to see more + * details. */ + halide_error_code_device_run_failed = -23, + + /** The Halide runtime encountered a host pointer that violated + * the alignment set for it by way of a call to + * set_host_alignment */ + halide_error_code_unaligned_host_ptr = -24, + + /** A fold_storage directive was used on a dimension that is not + * accessed in a monotonically increasing or decreasing fashion. */ + halide_error_code_bad_fold = -25, + + /** A fold_storage directive was used with a fold factor that was + * too small to store all the values of a producer needed by the + * consumer. */ + halide_error_code_fold_factor_too_small = -26, + + /** User-specified require() expression was not satisfied. */ + halide_error_code_requirement_failed = -27, + + /** At least one of the buffer's extents are negative. */ + halide_error_code_buffer_extents_negative = -28, + + /** Call(s) to a GPU backend API failed. */ + halide_error_code_gpu_device_error = -29, + + /** Failure recording trace packets for one of the halide_target_feature_trace features. */ + halide_error_code_trace_failed = -30, + + /** A specialize_fail() schedule branch was selected at runtime. */ + halide_error_code_specialize_fail = -31, + + /** The Halide runtime encountered an error while trying to wrap a + * native device handle. Turn on -debug in your target string to + * see more details. */ + halide_error_code_device_wrap_native_failed = -32, + + /** The Halide runtime encountered an error while trying to detach + * a native device handle. Turn on -debug in your target string + * to see more details. */ + halide_error_code_device_detach_native_failed = -33, + + /** The host field on an input or output was null, the device + * field was not zero, and the pipeline tries to use the buffer on + * the host. You may be passing a GPU-only buffer to a pipeline + * which is scheduled to use it on the CPU. */ + halide_error_code_host_is_null = -34, + + /** A folded buffer was passed to an extern stage, but the region + * touched wraps around the fold boundary. */ + halide_error_code_bad_extern_fold = -35, + + /** Buffer has a non-null device_interface but device is 0, which + * violates a Halide invariant. */ + halide_error_code_device_interface_no_device = -36, + + /** Buffer has both host and device dirty bits set, which violates + * a Halide invariant. */ + halide_error_code_host_and_device_dirty = -37, + + /** The halide_buffer_t * passed to a halide runtime routine is + * nullptr and this is not allowed. */ + halide_error_code_buffer_is_null = -38, + + /** The Halide runtime encountered an error while trying to copy + * from one buffer to another. Turn on -debug in your target + * string to see more details. */ + halide_error_code_device_buffer_copy_failed = -39, + + /** Attempted to make cropped/sliced alias of a buffer with a device + * field, but the device_interface does not support cropping. */ + halide_error_code_device_crop_unsupported = -40, + + /** Cropping/slicing a buffer failed for some other reason. Turn on -debug + * in your target string. */ + halide_error_code_device_crop_failed = -41, + + /** An operation on a buffer required an allocation on a + * particular device interface, but a device allocation already + * existed on a different device interface. Free the old one + * first. */ + halide_error_code_incompatible_device_interface = -42, + + /** The dimensions field of a halide_buffer_t does not match the dimensions of that ImageParam. */ + halide_error_code_bad_dimensions = -43, + + /** A buffer with the device_dirty flag set was passed to a + * pipeline compiled with no device backends enabled, so it + * doesn't know how to copy the data back from device memory to + * host memory. Either call copy_to_host before calling the Halide + * pipeline, or enable the appropriate device backend. */ + halide_error_code_device_dirty_with_no_device_support = -44, + + /** An explicit storage bound provided is too small to store + * all the values produced by the function. */ + halide_error_code_storage_bound_too_small = -45, + + /** A factor used to split a loop was discovered to be zero or negative at + * runtime. */ + halide_error_code_split_factor_not_positive = -46, + + /** "vscale" value of Scalable Vector detected in runtime does not match + * the vscale value used in compilation. */ + halide_error_code_vscale_invalid = -47, + + /** Profiling failed for a pipeline invocation. */ + halide_error_code_cannot_profile_pipeline = -48, +}; + +/** Halide calls the functions below on various error conditions. The + * default implementations construct an error message, call + * halide_error, then return the matching error code above. On + * platforms that support weak linking, you can override these to + * catch the errors individually. */ + +/** A call into an extern stage for the purposes of bounds inference + * failed. Returns the error code given by the extern stage. */ +extern int halide_error_bounds_inference_call_failed(void *user_context, const char *extern_stage_name, int result); + +/** A call to an extern stage failed. Returned the error code given by + * the extern stage. */ +extern int halide_error_extern_stage_failed(void *user_context, const char *extern_stage_name, int result); + +/** Various other error conditions. See the enum above for a + * description of each. */ +// @{ +extern int halide_error_explicit_bounds_too_small(void *user_context, const char *func_name, const char *var_name, + int min_bound, int max_bound, int min_required, int max_required); +extern int halide_error_bad_type(void *user_context, const char *func_name, + uint32_t type_given, uint32_t correct_type); // N.B. The last two args are the bit representation of a halide_type_t +extern int halide_error_bad_dimensions(void *user_context, const char *func_name, + int32_t dimensions_given, int32_t correct_dimensions); +extern int halide_error_access_out_of_bounds(void *user_context, const char *func_name, + int dimension, int min_touched, int max_touched, + int min_valid, int max_valid); +extern int halide_error_buffer_allocation_too_large(void *user_context, const char *buffer_name, + uint64_t allocation_size, uint64_t max_size); +extern int halide_error_buffer_extents_negative(void *user_context, const char *buffer_name, int dimension, int extent); +extern int halide_error_buffer_extents_too_large(void *user_context, const char *buffer_name, + int64_t actual_size, int64_t max_size); +extern int halide_error_constraints_make_required_region_smaller(void *user_context, const char *buffer_name, + int dimension, + int constrained_min, int constrained_extent, + int required_min, int required_extent); +extern int halide_error_constraint_violated(void *user_context, const char *var, int val, + const char *constrained_var, int constrained_val); +extern int halide_error_param_too_small_i64(void *user_context, const char *param_name, + int64_t val, int64_t min_val); +extern int halide_error_param_too_small_u64(void *user_context, const char *param_name, + uint64_t val, uint64_t min_val); +extern int halide_error_param_too_small_f64(void *user_context, const char *param_name, + double val, double min_val); +extern int halide_error_param_too_large_i64(void *user_context, const char *param_name, + int64_t val, int64_t max_val); +extern int halide_error_param_too_large_u64(void *user_context, const char *param_name, + uint64_t val, uint64_t max_val); +extern int halide_error_param_too_large_f64(void *user_context, const char *param_name, + double val, double max_val); +extern int halide_error_out_of_memory(void *user_context); +extern int halide_error_buffer_argument_is_null(void *user_context, const char *buffer_name); +extern int halide_error_debug_to_file_failed(void *user_context, const char *func, + const char *filename, int error_code); +extern int halide_error_unaligned_host_ptr(void *user_context, const char *func_name, int alignment); +extern int halide_error_host_is_null(void *user_context, const char *func_name); +extern int halide_error_bad_fold(void *user_context, const char *func_name, const char *var_name, + const char *loop_name); +extern int halide_error_bad_extern_fold(void *user_context, const char *func_name, + int dim, int min, int extent, int valid_min, int fold_factor); + +extern int halide_error_fold_factor_too_small(void *user_context, const char *func_name, const char *var_name, + int fold_factor, const char *loop_name, int required_extent); +extern int halide_error_requirement_failed(void *user_context, const char *condition, const char *message); +extern int halide_error_specialize_fail(void *user_context, const char *message); +extern int halide_error_no_device_interface(void *user_context); +extern int halide_error_device_interface_no_device(void *user_context); +extern int halide_error_host_and_device_dirty(void *user_context); +extern int halide_error_buffer_is_null(void *user_context, const char *routine); +extern int halide_error_device_dirty_with_no_device_support(void *user_context, const char *buffer_name); +extern int halide_error_storage_bound_too_small(void *user_context, const char *func_name, const char *var_name, + int provided_size, int required_size); +extern int halide_error_device_crop_failed(void *user_context); +extern int halide_error_split_factor_not_positive(void *user_context, const char *func_name, const char *orig, const char *outer, const char *inner, const char *factor_str, int factor); +extern int halide_error_vscale_invalid(void *user_context, const char *func_name, int runtime_vscale, int compiletime_vscale); +// @} + +/** Optional features a compilation Target can have. + * Be sure to keep this in sync with the Feature enum in Target.h and the implementation of + * get_runtime_compatible_target in Target.cpp if you add a new feature. + */ +typedef enum halide_target_feature_t { + halide_target_feature_jit = 0, ///< Generate code that will run immediately inside the calling process. + halide_target_feature_debug, ///< Turn on debug info and output for runtime code. + halide_target_feature_no_asserts, ///< Disable all runtime checks, for slightly tighter code. + halide_target_feature_no_bounds_query, ///< Disable the bounds querying functionality. + + halide_target_feature_sse41, ///< Use SSE 4.1 and earlier instructions. Only relevant on x86. + halide_target_feature_avx, ///< Use AVX 1 instructions. Only relevant on x86. + halide_target_feature_avx2, ///< Use AVX 2 instructions. Only relevant on x86. + halide_target_feature_fma, ///< Enable x86 FMA instruction + halide_target_feature_fma4, ///< Enable x86 (AMD) FMA4 instruction set + halide_target_feature_f16c, ///< Enable x86 16-bit float support + + halide_target_feature_armv7s, ///< Generate code for ARMv7s. Only relevant for 32-bit ARM. + halide_target_feature_no_neon, ///< Avoid using NEON instructions. Only relevant for 32-bit ARM. + + halide_target_feature_vsx, ///< Use VSX instructions. Only relevant on POWERPC. + halide_target_feature_power_arch_2_07, ///< Use POWER ISA 2.07 new instructions. Only relevant on POWERPC. + + halide_target_feature_cuda, ///< Enable the CUDA runtime. Defaults to compute capability 2.0 (Fermi) + halide_target_feature_cuda_capability30, ///< Enable CUDA compute capability 3.0 (Kepler) + halide_target_feature_cuda_capability32, ///< Enable CUDA compute capability 3.2 (Tegra K1) + halide_target_feature_cuda_capability35, ///< Enable CUDA compute capability 3.5 (Kepler) + halide_target_feature_cuda_capability50, ///< Enable CUDA compute capability 5.0 (Maxwell) + halide_target_feature_cuda_capability61, ///< Enable CUDA compute capability 6.1 (Pascal) + halide_target_feature_cuda_capability70, ///< Enable CUDA compute capability 7.0 (Volta) + halide_target_feature_cuda_capability75, ///< Enable CUDA compute capability 7.5 (Turing) + halide_target_feature_cuda_capability80, ///< Enable CUDA compute capability 8.0 (Ampere) + halide_target_feature_cuda_capability86, ///< Enable CUDA compute capability 8.6 (Ampere) + + halide_target_feature_opencl, ///< Enable the OpenCL runtime. + halide_target_feature_cl_doubles, ///< Enable double support on OpenCL targets + halide_target_feature_cl_atomic64, ///< Enable 64-bit atomics operations on OpenCL targets + + halide_target_feature_user_context, ///< Generated code takes a user_context pointer as first argument + + halide_target_feature_profile, ///< Launch a sampling profiler alongside the Halide pipeline that monitors and reports the runtime used by each Func + halide_target_feature_no_runtime, ///< Do not include a copy of the Halide runtime in any generated object file or assembly + + halide_target_feature_metal, ///< Enable the (Apple) Metal runtime. + + halide_target_feature_c_plus_plus_mangling, ///< Generate C++ mangled names for result function, et al + + halide_target_feature_large_buffers, ///< Enable 64-bit buffer indexing to support buffers > 2GB. Ignored if bits != 64. + + halide_target_feature_hvx_128, ///< Enable HVX 128 byte mode. + halide_target_feature_hvx_v62, ///< Enable Hexagon v62 architecture. + halide_target_feature_fuzz_float_stores, ///< On every floating point store, set the last bit of the mantissa to zero. Pipelines for which the output is very different with this feature enabled may also produce very different output on different processors. + halide_target_feature_soft_float_abi, ///< Enable soft float ABI. This only enables the soft float ABI calling convention, which does not necessarily use soft floats. + halide_target_feature_msan, ///< Enable hooks for MSAN support. + halide_target_feature_avx512, ///< Enable the base AVX512 subset supported by all AVX512 architectures. The specific feature sets are AVX-512F and AVX512-CD. See https://en.wikipedia.org/wiki/AVX-512 for a description of each AVX subset. + halide_target_feature_avx512_knl, ///< Enable the AVX512 features supported by Knight's Landing chips, such as the Xeon Phi x200. This includes the base AVX512 set, and also AVX512-CD and AVX512-ER. + halide_target_feature_avx512_skylake, ///< Enable the AVX512 features supported by Skylake Xeon server processors. This adds AVX512-VL, AVX512-BW, and AVX512-DQ to the base set. The main difference from the base AVX512 set is better support for small integer ops. Note that this does not include the Knight's Landing features. Note also that these features are not available on Skylake desktop and mobile processors. + halide_target_feature_avx512_cannonlake, ///< Enable the AVX512 features expected to be supported by future Cannonlake processors. This includes all of the Skylake features, plus AVX512-IFMA and AVX512-VBMI. + halide_target_feature_avx512_zen4, ///< Enable the AVX512 features supported by Zen4 processors. This include all of the Cannonlake features, plus AVX512-VNNI, AVX512-BF16, and more. + halide_target_feature_avx512_sapphirerapids, ///< Enable the AVX512 features supported by Sapphire Rapids processors. This include all of the Zen4 features, plus AVX-VNNI and AMX instructions. + halide_target_feature_trace_loads, ///< Trace all loads done by the pipeline. Equivalent to calling Func::trace_loads on every non-inlined Func. + halide_target_feature_trace_stores, ///< Trace all stores done by the pipeline. Equivalent to calling Func::trace_stores on every non-inlined Func. + halide_target_feature_trace_realizations, ///< Trace all realizations done by the pipeline. Equivalent to calling Func::trace_realizations on every non-inlined Func. + halide_target_feature_trace_pipeline, ///< Trace the pipeline. + halide_target_feature_hvx_v65, ///< Enable Hexagon v65 architecture. + halide_target_feature_hvx_v66, ///< Enable Hexagon v66 architecture. + halide_target_feature_hvx_v68, ///< Enable Hexagon v68 architecture. + halide_target_feature_cl_half, ///< Enable half support on OpenCL targets + halide_target_feature_strict_float, ///< Turn off all non-IEEE floating-point optimization. Currently applies only to LLVM targets. + halide_target_feature_tsan, ///< Enable hooks for TSAN support. + halide_target_feature_asan, ///< Enable hooks for ASAN support. + halide_target_feature_d3d12compute, ///< Enable Direct3D 12 Compute runtime. + halide_target_feature_check_unsafe_promises, ///< Insert assertions for promises. + halide_target_feature_hexagon_dma, ///< Enable Hexagon DMA buffers. + halide_target_feature_embed_bitcode, ///< Emulate clang -fembed-bitcode flag. + halide_target_feature_enable_llvm_loop_opt, ///< Enable loop vectorization + unrolling in LLVM. Overrides halide_target_feature_disable_llvm_loop_opt. (Ignored for non-LLVM targets.) + halide_target_feature_wasm_mvponly, ///< Disable all extensions to WebAssembly codegen (including +sign-ext and +nontrapping-fptoint, which are on by default). + halide_target_feature_wasm_simd128, ///< Enable +simd128 instructions for WebAssembly codegen. + halide_target_feature_wasm_threads, ///< Enable use of threads in WebAssembly codegen. Requires the use of a wasm runtime that provides pthread-compatible wrappers (typically, Emscripten with the -pthreads flag). Unsupported under WASI. + halide_target_feature_wasm_bulk_memory, ///< Enable +bulk-memory instructions for WebAssembly codegen. + halide_target_feature_webgpu, ///< Enable the WebGPU runtime. + halide_target_feature_sve, ///< Enable ARM Scalable Vector Extensions + halide_target_feature_sve2, ///< Enable ARM Scalable Vector Extensions v2 + halide_target_feature_egl, ///< Force use of EGL support. + halide_target_feature_arm_dot_prod, ///< Enable ARMv8.2-a dotprod extension (i.e. udot and sdot instructions) + halide_target_feature_arm_fp16, ///< Enable ARMv8.2-a half-precision floating point data processing + halide_llvm_large_code_model, ///< Use the LLVM large code model to compile + halide_target_feature_rvv, ///< Enable RISCV "V" Vector Extension + halide_target_feature_armv81a, ///< Enable ARMv8.1-a instructions + halide_target_feature_sanitizer_coverage, ///< Enable hooks for SanitizerCoverage support. + halide_target_feature_profile_by_timer, ///< Alternative to halide_target_feature_profile using timer interrupt for systems without threads or applicartions that need to avoid them. + halide_target_feature_spirv, ///< Enable SPIR-V code generation support. + halide_target_feature_vulkan, ///< Enable Vulkan runtime support. + halide_target_feature_vulkan_int8, ///< Enable Vulkan 8-bit integer support. + halide_target_feature_vulkan_int16, ///< Enable Vulkan 16-bit integer support. + halide_target_feature_vulkan_int64, ///< Enable Vulkan 64-bit integer support. + halide_target_feature_vulkan_float16, ///< Enable Vulkan 16-bit float support. + halide_target_feature_vulkan_float64, ///< Enable Vulkan 64-bit float support. + halide_target_feature_vulkan_version10, ///< Enable Vulkan v1.0 runtime target support. + halide_target_feature_vulkan_version12, ///< Enable Vulkan v1.2 runtime target support. + halide_target_feature_vulkan_version13, ///< Enable Vulkan v1.3 runtime target support. + halide_target_feature_semihosting, ///< Used together with Target::NoOS for the baremetal target built with semihosting library and run with semihosting mode where minimum I/O communication with a host PC is available. + halide_target_feature_avx10_1, ///< Intel AVX10 version 1 support. vector_bits is used to indicate width. + halide_target_feature_x86_apx, ///< Intel x86 APX support. Covers initial set of features released as APX: egpr,push2pop2,ppx,ndd . + halide_target_feature_end ///< A sentinel. Every target is considered to have this feature, and setting this feature does nothing. +} halide_target_feature_t; + +/** This function is called internally by Halide in some situations to determine + * if the current execution environment can support the given set of + * halide_target_feature_t flags. The implementation must do the following: + * + * -- If there are flags set in features that the function knows *cannot* be supported, return 0. + * -- Otherwise, return 1. + * -- Note that any flags set in features that the function doesn't know how to test should be ignored; + * this implies that a return value of 1 means "not known to be bad" rather than "known to be good". + * + * In other words: a return value of 0 means "It is not safe to use code compiled with these features", + * while a return value of 1 means "It is not obviously unsafe to use code compiled with these features". + * + * The default implementation simply calls halide_default_can_use_target_features. + * + * Note that `features` points to an array of `count` uint64_t; this array must contain enough + * bits to represent all the currently known features. Any excess bits must be set to zero. + */ +// @{ +extern int halide_can_use_target_features(int count, const uint64_t *features); +typedef int (*halide_can_use_target_features_t)(int count, const uint64_t *features); +extern halide_can_use_target_features_t halide_set_custom_can_use_target_features(halide_can_use_target_features_t); +// @} + +/** + * This is the default implementation of halide_can_use_target_features; it is provided + * for convenience of user code that may wish to extend halide_can_use_target_features + * but continue providing existing support, e.g. + * + * int halide_can_use_target_features(int count, const uint64_t *features) { + * if (features[halide_target_somefeature >> 6] & (1LL << (halide_target_somefeature & 63))) { + * if (!can_use_somefeature()) { + * return 0; + * } + * } + * return halide_default_can_use_target_features(count, features); + * } + */ +extern int halide_default_can_use_target_features(int count, const uint64_t *features); + +typedef struct halide_dimension_t { +#if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) + int32_t min = 0, extent = 0, stride = 0; + + // Per-dimension flags. None are defined yet (This is reserved for future use). + uint32_t flags = 0; + + HALIDE_ALWAYS_INLINE halide_dimension_t() = default; + HALIDE_ALWAYS_INLINE halide_dimension_t(int32_t m, int32_t e, int32_t s, uint32_t f = 0) + : min(m), extent(e), stride(s), flags(f) { + } + + HALIDE_ALWAYS_INLINE bool operator==(const halide_dimension_t &other) const { + return (min == other.min) && + (extent == other.extent) && + (stride == other.stride) && + (flags == other.flags); + } + + HALIDE_ALWAYS_INLINE bool operator!=(const halide_dimension_t &other) const { + return !(*this == other); + } +#else + int32_t min, extent, stride; + + // Per-dimension flags. None are defined yet (This is reserved for future use). + uint32_t flags; +#endif +} halide_dimension_t; + +#ifdef __cplusplus +} // extern "C" +#endif + +typedef enum { halide_buffer_flag_host_dirty = 1, + halide_buffer_flag_device_dirty = 2 } halide_buffer_flags; + +/** + * The raw representation of an image passed around by generated + * Halide code. It includes some stuff to track whether the image is + * not actually in main memory, but instead on a device (like a + * GPU). For a more convenient C++ wrapper, use Halide::Buffer. */ +typedef struct halide_buffer_t { + /** A device-handle for e.g. GPU memory used to back this buffer. */ + uint64_t device; + + /** The interface used to interpret the above handle. */ + const struct halide_device_interface_t *device_interface; + + /** A pointer to the start of the data in main memory. In terms of + * the Halide coordinate system, this is the address of the min + * coordinates (defined below). */ + uint8_t *host; + + /** flags with various meanings. */ + uint64_t flags; + + /** The type of each buffer element. */ + struct halide_type_t type; + + /** The dimensionality of the buffer. */ + int32_t dimensions; + + /** The shape of the buffer. Halide does not own this array - you + * must manage the memory for it yourself. */ + halide_dimension_t *dim; + + /** Pads the buffer up to a multiple of 8 bytes */ + void *padding; + +#if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) + /** Convenience methods for accessing the flags */ + // @{ + HALIDE_ALWAYS_INLINE bool get_flag(halide_buffer_flags flag) const { + return (flags & flag) != 0; + } + + HALIDE_ALWAYS_INLINE void set_flag(halide_buffer_flags flag, bool value) { + if (value) { + flags |= flag; + } else { + flags &= ~uint64_t(flag); + } + } + + HALIDE_MUST_USE_RESULT HALIDE_ALWAYS_INLINE bool host_dirty() const { + return get_flag(halide_buffer_flag_host_dirty); + } + + HALIDE_MUST_USE_RESULT HALIDE_ALWAYS_INLINE bool device_dirty() const { + return get_flag(halide_buffer_flag_device_dirty); + } + + HALIDE_ALWAYS_INLINE void set_host_dirty(bool v = true) { + set_flag(halide_buffer_flag_host_dirty, v); + } + + HALIDE_ALWAYS_INLINE void set_device_dirty(bool v = true) { + set_flag(halide_buffer_flag_device_dirty, v); + } + // @} + + /** The total number of elements this buffer represents. Equal to + * the product of the extents */ + HALIDE_ALWAYS_INLINE size_t number_of_elements() const { + size_t s = 1; + for (int i = 0; i < dimensions; i++) { + s *= dim[i].extent; + } + return s; + } + + /** Offset to the element with the lowest address. + * If all strides are positive, equal to zero. + * Offset is in elements, not bytes. + * Unlike begin(), this is ok to call on an unallocated buffer. */ + HALIDE_ALWAYS_INLINE ptrdiff_t begin_offset() const { + ptrdiff_t index = 0; + for (int i = 0; i < dimensions; i++) { + const int stride = dim[i].stride; + if (stride < 0) { + index += stride * (ptrdiff_t)(dim[i].extent - 1); + } + } + return index; + } + + /** An offset to one beyond the element with the highest address. + * Offset is in elements, not bytes. + * Unlike end(), this is ok to call on an unallocated buffer. */ + HALIDE_ALWAYS_INLINE ptrdiff_t end_offset() const { + ptrdiff_t index = 0; + for (int i = 0; i < dimensions; i++) { + const int stride = dim[i].stride; + if (stride > 0) { + index += stride * (ptrdiff_t)(dim[i].extent - 1); + } + } + index += 1; + return index; + } + + /** A pointer to the element with the lowest address. + * If all strides are positive, equal to the host pointer. + * Illegal to call on an unallocated buffer. */ + HALIDE_ALWAYS_INLINE uint8_t *begin() const { + return host + begin_offset() * type.bytes(); + } + + /** A pointer to one beyond the element with the highest address. + * Illegal to call on an unallocated buffer. */ + HALIDE_ALWAYS_INLINE uint8_t *end() const { + return host + end_offset() * type.bytes(); + } + + /** The total number of bytes spanned by the data in memory. */ + HALIDE_ALWAYS_INLINE size_t size_in_bytes() const { + return (size_t)(end_offset() - begin_offset()) * type.bytes(); + } + + /** A pointer to the element at the given location. */ + HALIDE_ALWAYS_INLINE uint8_t *address_of(const int *pos) const { + ptrdiff_t index = 0; + for (int i = 0; i < dimensions; i++) { + index += (ptrdiff_t)dim[i].stride * (pos[i] - dim[i].min); + } + return host + index * type.bytes(); + } + + /** Attempt to call device_sync for the buffer. If the buffer + * has no device_interface (or no device_sync), this is a quiet no-op. + * Calling this explicitly should rarely be necessary, except for profiling. */ + HALIDE_ALWAYS_INLINE int device_sync(void *ctx = nullptr) { + if (device_interface && device_interface->device_sync) { + return device_interface->device_sync(ctx, this); + } + return 0; + } + + /** Check if an input buffer passed extern stage is a querying + * bounds. Compared to doing the host pointer check directly, + * this both adds clarity to code and will facilitate moving to + * another representation for bounds query arguments. */ + HALIDE_ALWAYS_INLINE bool is_bounds_query() const { + return host == nullptr && device == 0; + } + +#endif +} halide_buffer_t; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HALIDE_ATTRIBUTE_DEPRECATED +#ifdef HALIDE_ALLOW_DEPRECATED +#define HALIDE_ATTRIBUTE_DEPRECATED(x) +#else +#ifdef _MSC_VER +#define HALIDE_ATTRIBUTE_DEPRECATED(x) __declspec(deprecated(x)) +#else +#define HALIDE_ATTRIBUTE_DEPRECATED(x) __attribute__((deprecated(x))) +#endif +#endif +#endif + +/** halide_scalar_value_t is a simple union able to represent all the well-known + * scalar values in a filter argument. Note that it isn't tagged with a type; + * you must ensure you know the proper type before accessing. Most user + * code will never need to create instances of this struct; its primary use + * is to hold def/min/max values in a halide_filter_argument_t. (Note that + * this is conceptually just a union; it's wrapped in a struct to ensure + * that it doesn't get anonymized by LLVM.) + */ +struct halide_scalar_value_t { + union { + bool b; + int8_t i8; + int16_t i16; + int32_t i32; + int64_t i64; + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + float f32; + double f64; + void *handle; + } u; +#ifdef __cplusplus + HALIDE_ALWAYS_INLINE halide_scalar_value_t() { + u.u64 = 0; + } +#endif +}; + +enum halide_argument_kind_t { + halide_argument_kind_input_scalar = 0, + halide_argument_kind_input_buffer = 1, + halide_argument_kind_output_buffer = 2 +}; + +/* + These structs must be robust across different compilers and settings; when + modifying them, strive for the following rules: + + 1) All fields are explicitly sized. I.e. must use int32_t and not "int" + 2) All fields must land on an alignment boundary that is the same as their size + 3) Explicit padding is added to make that so + 4) The sizeof the struct is padded out to a multiple of the largest natural size thing in the struct + 5) don't forget that 32 and 64 bit pointers are different sizes +*/ + +/** + * Obsolete version of halide_filter_argument_t; only present in + * code that wrote halide_filter_metadata_t version 0. + */ +struct halide_filter_argument_t_v0 { + const char *name; + int32_t kind; + int32_t dimensions; + struct halide_type_t type; + const struct halide_scalar_value_t *def, *min, *max; +}; + +/** + * halide_filter_argument_t is essentially a plain-C-struct equivalent to + * Halide::Argument; most user code will never need to create one. + */ +struct halide_filter_argument_t { + const char *name; // name of the argument; will never be null or empty. + int32_t kind; // actually halide_argument_kind_t + int32_t dimensions; // always zero for scalar arguments + struct halide_type_t type; + // These pointers should always be null for buffer arguments, + // and *may* be null for scalar arguments. (A null value means + // there is no def/min/max/estimate specified for this argument.) + const struct halide_scalar_value_t *scalar_def, *scalar_min, *scalar_max, *scalar_estimate; + // This pointer should always be null for scalar arguments, + // and *may* be null for buffer arguments. If not null, it should always + // point to an array of dimensions*2 pointers, which will be the (min, extent) + // estimates for each dimension of the buffer. (Note that any of the pointers + // may be null as well.) + int64_t const *const *buffer_estimates; +}; + +struct halide_filter_metadata_t { +#ifdef __cplusplus + static const int32_t VERSION = 1; +#endif + + /** version of this metadata; currently always 1. */ + int32_t version; + + /** The number of entries in the arguments field. This is always >= 1. */ + int32_t num_arguments; + + /** An array of the filters input and output arguments; this will never be + * null. The order of arguments is not guaranteed (input and output arguments + * may come in any order); however, it is guaranteed that all arguments + * will have a unique name within a given filter. */ + const struct halide_filter_argument_t *arguments; + + /** The Target for which the filter was compiled. This is always + * a canonical Target string (ie a product of Target::to_string). */ + const char *target; + + /** The function name of the filter. */ + const char *name; +}; + +/** halide_register_argv_and_metadata() is a **user-defined** function that + * must be provided in order to use the registration.cc files produced + * by Generators when the 'registration' output is requested. Each registration.cc + * file provides a static initializer that calls this function with the given + * filter's argv-call variant, its metadata, and (optionally) and additional + * textual data that the build system chooses to tack on for its own purposes. + * Note that this will be called at static-initializer time (i.e., before + * main() is called), and in an unpredictable order. Note that extra_key_value_pairs + * may be nullptr; if it's not null, it's expected to be a null-terminated list + * of strings, with an even number of entries. */ +void halide_register_argv_and_metadata( + int (*filter_argv_call)(void **), + const struct halide_filter_metadata_t *filter_metadata, + const char *const *extra_key_value_pairs); + +/** The functions below here are relevant for pipelines compiled with + * the -profile target flag, which runs a sampling profiler thread + * alongside the pipeline. */ + +/** Per-Func state tracked by the sampling profiler. */ +struct HALIDE_ATTRIBUTE_ALIGN(8) halide_profiler_func_stats { + /** Total time taken evaluating this Func (in nanoseconds). */ + uint64_t time; + + /** The current memory allocation of this Func. */ + uint64_t memory_current; + + /** The peak memory allocation of this Func. */ + uint64_t memory_peak; + + /** The total memory allocation of this Func. */ + uint64_t memory_total; + + /** The peak stack allocation of this Func's threads. */ + uint64_t stack_peak; + + /** The average number of thread pool worker threads active while computing this Func. */ + uint64_t active_threads_numerator, active_threads_denominator; + + /** The name of this Func. A global constant string. */ + const char *name; + + /** The total number of memory allocation of this Func. */ + int num_allocs; +}; + +/** Per-pipeline state tracked by the sampling profiler. These exist + * in a linked list. */ +struct HALIDE_ATTRIBUTE_ALIGN(8) halide_profiler_pipeline_stats { + /** Total time spent in this pipeline (in nanoseconds) */ + uint64_t time; + + /** The current memory allocation of funcs in this pipeline. */ + uint64_t memory_current; + + /** The peak memory allocation of funcs in this pipeline. */ + uint64_t memory_peak; + + /** The total memory allocation of funcs in this pipeline. */ + uint64_t memory_total; + + /** The average number of thread pool worker threads doing useful + * work while computing this pipeline. */ + uint64_t active_threads_numerator, active_threads_denominator; + + /** The name of this pipeline. A global constant string. */ + const char *name; + + /** An array containing states for each Func in this pipeline. */ + struct halide_profiler_func_stats *funcs; + + /** The next pipeline_stats pointer. It's a void * because types + * in the Halide runtime may not currently be recursive. */ + void *next; + + /** The number of funcs in this pipeline. */ + int num_funcs; + + /** The number of times this pipeline has been run. */ + int runs; + + /** The total number of samples taken inside of this pipeline. */ + int samples; + + /** The total number of memory allocation of funcs in this pipeline. */ + int num_allocs; +}; + +/** Per-invocation-of-a-pipeline state. Lives on the stack of the Halide + * code. Exists in a doubly-linked list to that it can be cleanly + * removed. */ +struct HALIDE_ATTRIBUTE_ALIGN(8) halide_profiler_instance_state { + /** Time billed to funcs in this instance by the sampling thread. */ + uint64_t billed_time; + + /** Wall clock time of the start of the instance. */ + uint64_t start_time; + + /** The current memory allocation of funcs in this instance. */ + uint64_t memory_current; + + /** The peak memory allocation of funcs in this instance. */ + uint64_t memory_peak; + + /** The total memory allocation of funcs in this instance. */ + uint64_t memory_total; + + /** The average number of thread pool worker threads doing useful + * work while computing this instance. */ + uint64_t active_threads_numerator, active_threads_denominator; + + /** A pointer to the next running instance, so that the running instances + * can exist in a linked list. */ + struct halide_profiler_instance_state *next; + + /** A pointer to the address of the next pointer of the previous instance, + * so that this can be removed from the linked list when the instance + * terminates. */ + struct halide_profiler_instance_state **prev_next; + + /** Information shared across all instances. The stats above are merged into + * it when the instance is retired. */ + struct halide_profiler_pipeline_stats *pipeline_stats; + + /** An array containing states for each Func in this instance of this pipeline. */ + struct halide_profiler_func_stats *funcs; + + /** The id of the current running Func. Set by the pipeline, read + * periodically by the profiler thread. */ + int current_func; + + /** The number of threads currently doing work on this pipeline instance. */ + int active_threads; + + /** The number of samples taken by this instance. */ + int samples; + + /** The total number of memory allocation of funcs in this instance. */ + int num_allocs; + + /** Whether or not this instance should count towards pipeline + * statistics. */ + int should_collect_statistics; +}; + +/** The global state of the profiler. */ +struct halide_profiler_state { + /** Guards access to the fields below. If not locked, the sampling + * profiler thread is free to modify things below (including + * reordering the linked list of pipeline stats). */ + struct halide_mutex lock; + + /** A linked list of stats gathered for each pipeline. */ + struct halide_profiler_pipeline_stats *pipelines; + + /** Retrieve remote profiler state. Used so that the sampling + * profiler can follow along with execution that occurs elsewhere, + * e.g. on a DSP. If null, it reads from the int above instead. */ + + /** Sampling thread reference to be joined at shutdown. */ + struct halide_thread *sampling_thread; + + /** The running instances of Halide pipelines. */ + struct halide_profiler_instance_state *instances; + + /** If this callback is defined, the profiler asserts that there is a single + * live instance, and then uses it to get the current func and number of + * active threads insted of reading the fields in the instance. This is used + * so that the profiler can follow along with execution that occurs + * elsewhere (e.g. on an accelerator). */ + void (*get_remote_profiler_state)(int *func, int *active_workers); + + /** The amount of time the profiler thread sleeps between samples in + * microseconds. Defaults to 1000. To change it call + * halide_profiler_get_state and mutate this field. */ + int sleep_time; + + /** Set to 1 when you want the profiler to wait for all running instances to + * finish and then stop gracefully. */ + int shutdown; +}; + +/** Get a pointer to the global profiler state for programmatic + * inspection. Lock it before using to pause the profiler. */ +extern struct halide_profiler_state *halide_profiler_get_state(void); + +/** Get a pointer to the pipeline state associated with pipeline_name. + * This function grabs the global profiler state's lock on entry. */ +extern struct halide_profiler_pipeline_stats *halide_profiler_get_pipeline_state(const char *pipeline_name); + +/** Collects profiling information. Intended to be called from a timer + * interrupt handler if timer based profiling is being used. + * State argument is acquired via halide_profiler_get_pipeline_state. + * prev_t argument is the previous time and can be used to set a more + * accurate time interval if desired. */ +extern int halide_profiler_sample(struct halide_profiler_state *s, uint64_t *prev_t); + +/** Reset profiler state cheaply. May leave threads running or some memory + * allocated but all accumulated statistics are reset. Blocks until all running + * profiled Halide pipelines exit. */ +extern void halide_profiler_reset(void); + +/** Reset all profiler state. Blocks until all running profiled Halide + * pipelines exit. */ +extern void halide_profiler_shutdown(void); + +/** Print out timing statistics for everything run since the last + * reset. Also happens at process exit. */ +extern void halide_profiler_report(void *user_context); + +/** These routines are called to temporarily disable and then reenable + * the profiler. */ +//@{ +extern void halide_profiler_lock(struct halide_profiler_state *); +extern void halide_profiler_unlock(struct halide_profiler_state *); +//@} + +/// \name "Float16" functions +/// These functions operate of bits (``uint16_t``) representing a half +/// precision floating point number (IEEE-754 2008 binary16). +//{@ + +/** Read bits representing a half precision floating point number and return + * the float that represents the same value */ +extern float halide_float16_bits_to_float(uint16_t); + +/** Read bits representing a half precision floating point number and return + * the double that represents the same value */ +extern double halide_float16_bits_to_double(uint16_t); + +// TODO: Conversion functions to half + +//@} + +// Allocating and freeing device memory is often very slow. The +// methods below give Halide's runtime permission to hold onto device +// memory to service future requests instead of returning it to the +// underlying device API. The API does not manage an allocation pool, +// all it does is provide access to a shared counter that acts as a +// limit on the unused memory not yet returned to the underlying +// device API. It makes callbacks to participants when memory needs to +// be released because the limit is about to be exceeded (either +// because the limit has been reduced, or because the memory owned by +// some participant becomes unused). + +/** Tell Halide whether or not it is permitted to hold onto device + * allocations to service future requests instead of returning them + * eagerly to the underlying device API. Many device allocators are + * quite slow, so it can be beneficial to set this to true. The + * default value for now is false. + * + * Note that if enabled, the eviction policy is very simplistic. The + * 32 most-recently used allocations are preserved, regardless of + * their size. Additionally, if a call to cuMalloc results in an + * out-of-memory error, the entire cache is flushed and the allocation + * is retried. See https://github.com/halide/Halide/issues/4093 + * + * If set to false, releases all unused device allocations back to the + * underlying device APIs. For finer-grained control, see specific + * methods in each device api runtime. + * + * Note that if the flag is set to true, this call *must* succeed and return + * a value of halide_error_code_success (i.e., zero); if you replace + * the implementation of this call in the runtime, you must honor this contract. + * */ +extern int halide_reuse_device_allocations(void *user_context, bool); + +/** Determines whether on device_free the memory is returned + * immediately to the device API, or placed on a free list for future + * use. Override and switch based on the user_context for + * finer-grained control. By default just returns the value most + * recently set by the method above. */ +extern bool halide_can_reuse_device_allocations(void *user_context); + +struct halide_device_allocation_pool { + int (*release_unused)(void *user_context); + struct halide_device_allocation_pool *next; +}; + +/** Register a callback to be informed when + * halide_reuse_device_allocations(false) is called, and all unused + * device allocations must be released. The object passed should have + * global lifetime, and its next field will be clobbered. */ +extern void halide_register_device_allocation_pool(struct halide_device_allocation_pool *); + +#ifdef __cplusplus +} // End extern "C" +#endif + +#if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) + +namespace { + +template +struct check_is_pointer { + static constexpr bool value = false; +}; + +template +struct check_is_pointer { + static constexpr bool value = true; +}; + +} // namespace + +/** Construct the halide equivalent of a C type */ +template +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + // Create a compile-time error if T is not a pointer (without + // using any includes - this code goes into the runtime). + // (Note that we can't have uninitialized variables in constexpr functions, + // even if those variables aren't used.) + static_assert(check_is_pointer::value, "Expected a pointer type here"); + return halide_type_t(halide_type_handle, 64); +} + +#ifdef HALIDE_CPP_COMPILER_HAS_FLOAT16 +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of<_Float16>() { + return halide_type_t(halide_type_float, 16); +} +#endif + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_float, 32); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_float, 64); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_uint, 1); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_uint, 8); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_uint, 16); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_uint, 32); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_uint, 64); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_int, 8); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_int, 16); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_int, 32); +} + +template<> +HALIDE_ALWAYS_INLINE constexpr halide_type_t halide_type_of() { + return halide_type_t(halide_type_int, 64); +} + +#ifndef COMPILING_HALIDE_RUNTIME + +// These structures are used by `function_info_header` files +// (generated by passing `-e function_info_header` to a Generator). +// The generated files contain documentation on the proper usage. +namespace HalideFunctionInfo { + +enum ArgumentKind { InputScalar = 0, + InputBuffer = 1, + OutputBuffer = 2 }; + +struct ArgumentInfo { + std::string_view name; + ArgumentKind kind; + int32_t dimensions; // always zero for scalar arguments + halide_type_t type; +}; + +} // namespace HalideFunctionInfo + +#endif // COMPILING_HALIDE_RUNTIME + +#endif // (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) + +#endif // HALIDE_HALIDERUNTIME_H + +#endif diff --git a/app/src/main/cpp/hdrplus2/armeabi-v7a/hdrplus_pipeline.registration.cpp b/app/src/main/cpp/hdrplus2/armeabi-v7a/hdrplus_pipeline.registration.cpp new file mode 100644 index 00000000..30760463 --- /dev/null +++ b/app/src/main/cpp/hdrplus2/armeabi-v7a/hdrplus_pipeline.registration.cpp @@ -0,0 +1,36 @@ + +// MACHINE GENERATED -- DO NOT EDIT + +extern "C" { +struct halide_filter_metadata_t; +void halide_register_argv_and_metadata( + int (*filter_argv_call)(void **), + const struct halide_filter_metadata_t *filter_metadata, + const char * const *extra_key_value_pairs +); +} + +extern "C" { +extern int hdrplus_pipeline_argv(void **args); +extern const struct halide_filter_metadata_t *hdrplus_pipeline_metadata(); +} + +#ifdef HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC +extern "C" const char * const *HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC(); +#endif // HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC + +namespace halide_nsreg_hdrplus_pipeline { +namespace { +struct Registerer { + Registerer() { +#ifdef HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC + halide_register_argv_and_metadata(::hdrplus_pipeline_argv, ::hdrplus_pipeline_metadata(), HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC()); +#else + halide_register_argv_and_metadata(::hdrplus_pipeline_argv, ::hdrplus_pipeline_metadata(), nullptr); +#endif // HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC + } +}; +static Registerer registerer; +} // namespace +} // halide_nsreg_hdrplus_pipeline + diff --git a/app/src/main/cpp/hdrplus2/include/HDRPlus.h b/app/src/main/cpp/hdrplus2/include/HDRPlus.h new file mode 100644 index 00000000..b1df3612 --- /dev/null +++ b/app/src/main/cpp/hdrplus2/include/HDRPlus.h @@ -0,0 +1,13 @@ +#ifndef __HDRPLUS__ +#define __HDRPLUS__ + +#include +#include + +#include // all opencv header + +int doHdrPlus(const std::string& dir_path, const std::string& out_name, const std::vector& in_names); + +bool doHdrPlus(const std::vector< std::vector >& images, cv::Mat& mat); + +#endif // __HDRPLUS__ \ No newline at end of file diff --git a/app/src/main/cpp/hdrplus2/include/stb_image_write.h b/app/src/main/cpp/hdrplus2/include/stb_image_write.h new file mode 100644 index 00000000..cef6a3d2 --- /dev/null +++ b/app/src/main/cpp/hdrplus2/include/stb_image_write.h @@ -0,0 +1,1178 @@ +/* stb_image_write - v1.02 - public domain - +http://nothings.org/stb/stb_image_write.h writes out PNG/BMP/TGA images to C +stdio - Sean Barrett 2010-2015 no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio. It could be + adapted to write to memory or a general streaming interface; let me know. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation. This library is designed + for source code compactness and simplicity, not optimal image file size + or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can define STBIW_MEMMOVE() to replace memmove() + +USAGE: + + There are four functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void +*data, int stride_in_bytes); int stbi_write_bmp(char const *filename, int w, int +h, int comp, const void *data); int stbi_write_tga(char const *filename, int w, +int h, int comp, const void *data); int stbi_write_hdr(char const *filename, int +w, int h, int comp, const float *data); + + There are also four equivalent functions that use an arbitrary write +function. You are expected to open/close your file-equivalent before and after +calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int +h, int comp, const void *data, int stride_in_bytes); int +stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int +comp, const void *data); int stbi_write_tga_to_func(stbi_write_func *func, void +*context, int w, int h, int comp, const void *data); int +stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int +comp, const float *data); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + +CREDITS: + + PNG/BMP/TGA + Sean Barrett + HDR + Baldur Karlsson + TGA monochrome: + Jean-Sebastien Guay + misc enhancements: + Tim Kelsey + TGA RLE + Alan Hickman + initial file IO callback implementation + Emmanuel Julien + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + +LICENSE + +This software is dual-licensed to the public domain and under the following +license: you are granted a perpetual, irrevocable license to copy, modify, +publish, and distribute this file as you see fit. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#define STBIWDEF extern +// STBIWDEF int stbi_write_tga_with_rle; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, + const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, + const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, + const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, + const float *data); +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, + int h, int comp, const void *data, + int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, + int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, + int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, + int h, int comp, const float *data); + +#ifdef __cplusplus +} +#endif + +#endif // INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + +#include +#include +#include +#include + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && \ + (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && \ + !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) +// ok +#else +#error \ + "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p, newsz) realloc(p, newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p, oldsz, newsz) STBIW_REALLOC(p, newsz) +#endif + +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a, b, sz) memmove(a, b, sz) +#endif + +#ifndef STBIW_ASSERT +#include +#define STBIW_ASSERT(x) assert(x) +#endif + +#define STBIW_UCHAR(x) (unsigned char)((x) & 0xff) + +typedef struct { + stbi_write_func *func; + void *context; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, + stbi_write_func *c, void *context) { + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) { + fwrite(data, 1, size, (FILE *)context); +} + +static int stbi__start_write_file(stbi__write_context *s, + const char *filename) { + FILE *f = fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *)f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) { + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32) == 4 ? 1 : -1]; + +#ifdef STB_IMAGE_WRITE_STATIC +#define stbi_write_tga_with_rle 1 +#else +#define stbi_write_tga_with_rle 1 +#endif + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) { + while (*fmt) { + switch (*fmt++) { + case ' ': + break; + case '1': { + unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context, &x, 1); + break; + } + case '2': { + int x = va_arg(v, int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x >> 8); + s->func(s->context, b, 2); + break; + } + case '4': { + stbiw_uint32 x = va_arg(v, int); + unsigned char b[4]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x >> 8); + b[2] = STBIW_UCHAR(x >> 16); + b[3] = STBIW_UCHAR(x >> 24); + s->func(s->context, b, 4); + break; + } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, + unsigned char b, unsigned char c) { + unsigned char arr[3]; + arr[0] = a, arr[1] = b, arr[2] = c; + s->func(s->context, arr, 3); +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, + int write_alpha, int expand_mono, + unsigned char *d) { + unsigned char bg[3] = {255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + s->func(s->context, &d[comp - 1], 1); + + switch (comp) { + case 1: + s->func(s->context, d, 1); + break; + case 2: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + s->func(s->context, d, 1); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + s->func(s->context, &d[comp - 1], 1); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, + int x, int y, int comp, void *data, + int write_alpha, int scanline_pad, + int expand_mono) { + stbiw_uint32 zero = 0; + int i, j, j_end; + + if (y <= 0) + return; + + if (vdir < 0) + j_end = -1, j = y - 1; + else + j_end = y, j = 0; + + for (; j != j_end; j += vdir) { + for (i = 0; i < x; ++i) { + unsigned char *d = (unsigned char *)data + (j * x + i) * comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, + int y, int comp, int expand_mono, void *data, + int alpha, int pad, const char *fmt, ...) { + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s, rgb_dir, vdir, x, y, comp, data, alpha, pad, + expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, + const void *data) { + int pad = (-x * 3) & 3; + return stbiw__outfile(s, -1, -1, x, y, comp, 1, (void *)data, 0, pad, + "11 4 22 4" + "4 44 22 444444", + 'B', 'M', 14 + 40 + (x * 3 + pad) * y, 0, 0, + 14 + 40, // file header + 40, x, y, 1, 24, 0, 0, 0, 0, 0, 0); // bitmap header +} + +STBIWDEF inline int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, + int y, int comp, const void *data) { + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF inline int stbi_write_bmp(char const *filename, int x, int y, int comp, + const void *data) { + stbi__write_context s; + if (stbi__start_write_file(&s, filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //! STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, + void *data) { + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp - 1 : comp; + int format = + colorbytes < 2 + ? 3 + : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *)data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, + (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i, j, k; + + stbiw__writef(s, "111 221 2222 11", 0, 0, format + 8, 0, 0, 0, 0, 0, x, y, + (colorbytes + has_alpha) * 8, has_alpha * 8); + + for (j = y - 1; j >= 0; --j) { + unsigned char *row = (unsigned char *)data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + s->func(s->context, &header, 1); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + s->func(s->context, &header, 1); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + } + return 1; +} + +inline int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, + int comp, const void *data) { + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *)data); +} + +#ifndef STBI_WRITE_NO_STDIO +inline int stbi_write_tga(char const *filename, int x, int y, int comp, + const void *data) { + stbi__write_context s; + if (stbi__start_write_file(&s, filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *)data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson +#ifndef STBI_WRITE_NO_STDIO + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) { + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32f) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float)frexp(maxcomp, &exponent) * 256.0f / maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +void stbiw__write_run_data(stbi__write_context *s, int length, + unsigned char databyte) { + unsigned char lengthbyte = STBIW_UCHAR(length + 128); + STBIW_ASSERT(length + 128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +void stbiw__write_dump_data(stbi__write_context *s, int length, + unsigned char *data) { + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= + 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, + unsigned char *scratch, float *scanline) { + unsigned char scanlineheader[4] = {2, 2, 0, 0}; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width & 0xff00) >> 8; + scanlineheader[3] = (width & 0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x = 0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: + linear[2] = scanline[x * ncomp + 2]; + linear[1] = scanline[x * ncomp + 1]; + linear[0] = scanline[x * ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x * ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c, r; + /* encode into scratch buffer */ + for (x = 0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: + linear[2] = scanline[x * ncomp + 2]; + linear[1] = scanline[x * ncomp + 1]; + linear[0] = scanline[x * ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x * ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width * 0] = rgbe[0]; + scratch[x + width * 1] = rgbe[1]; + scratch[x + width * 2] = rgbe[2]; + scratch[x + width * 3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c = 0; c < 4; c++) { + unsigned char *comp = &scratch[width * c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r + 2 < width) { + if (comp[r] == comp[r + 1] && comp[r] == comp[r + 2]) + break; + ++r; + } + if (r + 2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r - x; + if (len > 128) + len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r + 2 < width) { // same test as what we break out of in search + // loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r - x; + if (len > 127) + len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +inline int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, + float *data) { + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full + // output scanline. + unsigned char *scratch = (unsigned char *)STBIW_MALLOC(x * 4); + int i, len; + char buffer[128]; + char header[] = + "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header) - 1); + + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", + y, x); + s->func(s->context, buffer, len); + + for (i = 0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp * i * x); + STBIW_FREE(scratch); + return 1; + } +} + +inline int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, + int comp, const float *data) { + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *)data); +} + +inline int stbi_write_hdr(char const *filename, int x, int y, int comp, + const float *data) { + stbi__write_context s; + if (stbi__start_write_file(&s, filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *)data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() +// == vector<>::size() +#define stbiw__sbraw(a) ((int *)(a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a, n) ((a) == 0 || stbiw__sbn(a) + n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a, n) \ + (stbiw__sbneedgrow(a, (n)) ? stbiw__sbgrow(a, n) : 0) +#define stbiw__sbgrow(a, n) stbiw__sbgrowf((void **)&(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) \ + (stbiw__sbmaybegrow(a, 1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)), 0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) { + int m = *arr ? 2 * stbiw__sbm(*arr) + increment : increment + 1; + void *p = STBIW_REALLOC_SIZED( + *arr ? stbiw__sbraw(*arr) : 0, + *arr ? (stbiw__sbm(*arr) * itemsize + sizeof(int) * 2) : 0, + itemsize * m + sizeof(int) * 2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) + ((int *)p)[1] = 0; + *arr = (void *)((int *)p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, + unsigned int *bitbuffer, + int *bitcount) { + while (*bitcount >= 8) { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) { + int res = 0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, + int limit) { + int i; + for (i = 0; i < limit && i < 258; ++i) + if (a[i] != b[i]) + break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) { + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code, codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b, c) stbiw__zlib_add(stbiw__zlib_bitrev(b, c), c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n) - 144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n) - 256, 7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n) - 280, 8) +#define stbiw__zlib_huff(n) \ + ((n) <= 143 ? stbiw__zlib_huff1(n) \ + : (n) <= 255 ? stbiw__zlib_huff2(n) \ + : (n) <= 279 ? stbiw__zlib_huff3(n) \ + : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) \ + ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +unsigned char *stbi_zlib_compress(unsigned char *data, int data_len, + int *out_len, int quality) { + static unsigned short lengthc[] = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, + 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 259}; + static unsigned char lengtheb[] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 5, 5, 5, 5, 0}; + static unsigned short distc[] = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, + 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, + 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 32768}; + static unsigned char disteb[] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, + 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; + unsigned int bitbuf = 0; + int i, j, bitcount = 0; + unsigned char *out = NULL; + unsigned char ***hash_table = + (unsigned char ***)STBIW_MALLOC(stbiw__ZHASH * sizeof(char **)); + if (quality < 5) + quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1, 1); // BFINAL = 1 + stbiw__zlib_add(1, 2); // BTYPE = 1 -- fixed huffman + + for (i = 0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i = 0; + while (i < data_len - 3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data + i) & (stbiw__ZHASH - 1), best = 3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j = 0; j < n; ++j) { + if (hlist[j] - data > i - 32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data + i, data_len - i); + if (d >= best) + best = d, bestloc = hlist[j]; + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2 * quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h] + quality, + sizeof(hash_table[h][0]) * quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h], data + i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do + // cur byte as literal + h = stbiw__zhash(data + i + 1) & (stbiw__ZHASH - 1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j = 0; j < n; ++j) { + if (hlist[j] - data > i - 32767) { + int e = stbiw__zlib_countm(hlist[j], data + i + 1, data_len - i - 1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int)(data + i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j = 0; best > lengthc[j + 1] - 1; ++j) + ; + stbiw__zlib_huff(j + 257); + if (lengtheb[j]) + stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j = 0; d > distc[j + 1] - 1; ++j) + ; + stbiw__zlib_add(stbiw__zlib_bitrev(j, 5), 5); + if (disteb[j]) + stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (; i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0, 1); + + for (i = 0; i < stbiw__ZHASH; ++i) + (void)stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + { + // compute adler32 on input + unsigned int s1 = 1, s2 = 0; + int blocklen = (int)(data_len % 5552); + j = 0; + while (j < data_len) { + for (i = 0; i < blocklen; ++i) + s1 += data[j + i], s2 += s1; + s1 %= 65521, s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *)stbiw__sbraw(out); +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) { + static unsigned int crc_table[256] = { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, + 0xE963A535, 0x9E6495A3, 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, + 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, + 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, + 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, + 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, + 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, + 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, + 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, + 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, + 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, + 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, + 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, + 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, + 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, + 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, + 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, + 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, + 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D}; + + unsigned int crc = ~0u; + int i; + for (i = 0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +} + +#define stbiw__wpng4(o, a, b, c, d) \ + ((o)[0] = STBIW_UCHAR(a), (o)[1] = STBIW_UCHAR(b), (o)[2] = STBIW_UCHAR(c), \ + (o)[3] = STBIW_UCHAR(d), (o) += 4) +#define stbiw__wp32(data, v) \ + stbiw__wpng4(data, (v) >> 24, (v) >> 16, (v) >> 8, (v)); +#define stbiw__wptag(data, s) stbiw__wpng4(data, s[0], s[1], s[2], s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) { + unsigned int crc = stbiw__crc32(*data - len - 4, len + 4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) { + int p = a + b - c, pa = abs(p - a), pb = abs(p - b), pc = abs(p - c); + if (pa <= pb && pa <= pc) + return STBIW_UCHAR(a); + if (pb <= pc) + return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +inline unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, + int x, int y, int n, int *out_len) { + int ctype[5] = {-1, 0, 4, 2, 6}; + unsigned char sig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + unsigned char *out, *o, *filt, *zlib; + signed char *line_buffer; + int i, j, k, p, zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + filt = (unsigned char *)STBIW_MALLOC((x * n + 1) * y); + if (!filt) + return 0; + line_buffer = (signed char *)STBIW_MALLOC(x * n); + if (!line_buffer) { + STBIW_FREE(filt); + return 0; + } + for (j = 0; j < y; ++j) { + static int mapping[] = {0, 1, 2, 3, 4}; + static int firstmap[] = {0, 1, 0, 5, 6}; + int *mymap = j ? mapping : firstmap; + int best = 0, bestval = 0x7fffffff; + for (p = 0; p < 2; ++p) { + for (k = p ? best : 0; k < 5; ++k) { + int type = mymap[k], est = 0; + unsigned char *z = pixels + stride_bytes * j; + for (i = 0; i < n; ++i) + switch (type) { + case 0: + line_buffer[i] = z[i]; + break; + case 1: + line_buffer[i] = z[i]; + break; + case 2: + line_buffer[i] = z[i] - z[i - stride_bytes]; + break; + case 3: + line_buffer[i] = z[i] - (z[i - stride_bytes] >> 1); + break; + case 4: + line_buffer[i] = + (signed char)(z[i] - stbiw__paeth(0, z[i - stride_bytes], 0)); + break; + case 5: + line_buffer[i] = z[i]; + break; + case 6: + line_buffer[i] = z[i]; + break; + } + for (i = n; i < x * n; ++i) { + switch (type) { + case 0: + line_buffer[i] = z[i]; + break; + case 1: + line_buffer[i] = z[i] - z[i - n]; + break; + case 2: + line_buffer[i] = z[i] - z[i - stride_bytes]; + break; + case 3: + line_buffer[i] = z[i] - ((z[i - n] + z[i - stride_bytes]) >> 1); + break; + case 4: + line_buffer[i] = z[i] - stbiw__paeth(z[i - n], z[i - stride_bytes], + z[i - stride_bytes - n]); + break; + case 5: + line_buffer[i] = z[i] - (z[i - n] >> 1); + break; + case 6: + line_buffer[i] = z[i] - stbiw__paeth(z[i - n], 0, 0); + break; + } + } + if (p) + break; + for (i = 0; i < x * n; ++i) + est += abs((signed char)line_buffer[i]); + if (est < bestval) { + bestval = est; + best = k; + } + } + } + // when we get here, best contains the filter type, and line_buffer contains + // the data + filt[j * (x * n + 1)] = (unsigned char)best; + STBIW_MEMMOVE(filt + j * (x * n + 1) + 1, line_buffer, x * n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y * (x * n + 1), &zlen, + 8); // increase 8 to get smaller but use more memory + STBIW_FREE(filt); + if (!zlib) + return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *)STBIW_MALLOC(8 + 12 + 13 + 12 + zlen + 12); + if (!out) + return 0; + *out_len = 8 + 12 + 13 + 12 + zlen + 12; + + o = out; + STBIW_MEMMOVE(o, sig, 8); + o += 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o, 13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o, 0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o, 0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF inline int stbi_write_png(char const *filename, int x, int y, int comp, + const void *data, int stride_bytes) { + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *)data, + stride_bytes, x, y, comp, &len); + if (png == NULL) + return 0; + f = fopen(filename, "wb"); + if (!f) { + STBIW_FREE(png); + return 0; + } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF inline int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, + int y, int comp, const void *data, + int stride_bytes) { + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *)data, + stride_bytes, x, y, comp, &len); + if (png == NULL) + return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ diff --git a/app/src/main/cpp/hdrplus2/src/Burst.cpp b/app/src/main/cpp/hdrplus2/src/Burst.cpp new file mode 100644 index 00000000..513ef719 --- /dev/null +++ b/app/src/main/cpp/hdrplus2/src/Burst.cpp @@ -0,0 +1,39 @@ +#include "Burst.h" + +Halide::Runtime::Buffer Burst::ToBuffer() const { + if (Raws.empty()) { + return Halide::Runtime::Buffer(); + } + + Halide::Runtime::Buffer result(GetWidth(), GetHeight(), + Raws.size()); + for (int i = 0; i < Raws.size(); ++i) { + auto resultSlice = result.sliced(2, i); + Raws[i].CopyToBuffer(resultSlice); + } + return result; +} + +void Burst::CopyToBuffer(Halide::Runtime::Buffer &buffer) const { + buffer.copy_from(ToBuffer()); +} + +std::vector Burst::LoadRaws(const std::vector< std::vector >& images) { + std::vector result; + for (const auto &img : images) { + result.emplace_back(&img[0], img.size()); + } + return result; +} + +std::vector Burst::LoadRaws(const std::string &dirPath, + std::vector &inputs) { + std::vector result; + for (const auto &input : inputs) { + const std::string img_path = dirPath + "/" + input; + result.emplace_back(img_path); + } + return result; +} + +const RawImage &Burst::GetRaw(const size_t i) const { return this->Raws[i]; } diff --git a/app/src/main/cpp/hdrplus2/src/Burst.h b/app/src/main/cpp/hdrplus2/src/Burst.h new file mode 100644 index 00000000..74df5284 --- /dev/null +++ b/app/src/main/cpp/hdrplus2/src/Burst.h @@ -0,0 +1,76 @@ +#pragma once + +#include "InputSource.h" + +#include + +#include +#include + +class Burst { +public: + Burst(std::string dir_path, std::vector inputs) + : Dir(std::move(dir_path)), Inputs(std::move(inputs)), + Raws(LoadRaws(Dir, Inputs)) + + { + } + + Burst(const std::vector< std::vector >& images) + : Raws(LoadRaws(images)) + { + } + + ~Burst() = default; + + Burst(const Burst& src) + { + this->Dir = src.Dir; + this->Inputs = src.Inputs; + this->Raws = src.Raws; + int aa = 0; + } + + int GetWidth() const { return Raws.empty() ? -1 : Raws[0].GetWidth(); } + + int GetHeight() const { return Raws.empty() ? -1 : Raws[0].GetHeight(); } + + int GetBlackLevel() const + { + return Raws.empty() ? -1 : Raws[0].GetScalarBlackLevel(); + } + + int GetWhiteLevel() const { + return Raws.empty() ? -1 : Raws[0].GetWhiteLevel(); + } + + WhiteBalance GetWhiteBalance() const { + return Raws.empty() ? WhiteBalance{-1, -1, -1, -1} + : Raws[0].GetWhiteBalance(); + } + + CfaPattern GetCfaPattern() const { + return Raws.empty() ? CfaPattern::CFA_UNKNOWN : Raws[0].GetCfaPattern(); + } + + Halide::Runtime::Buffer GetColorCorrectionMatrix() const { + return Raws.empty() ? Halide::Runtime::Buffer() + : Raws[0].GetColorCorrectionMatrix(); + } + + Halide::Runtime::Buffer ToBuffer() const; + + void CopyToBuffer(Halide::Runtime::Buffer &buffer) const; + + const RawImage &GetRaw(const size_t i) const; + +private: + std::string Dir; + std::vector Inputs; + std::vector Raws; + +private: + static std::vector LoadRaws(const std::string &dirPath, + std::vector &inputs); + static std::vector LoadRaws(const std::vector< std::vector >& images); +}; diff --git a/app/src/main/cpp/hdrplus2/src/HDRPlus.cpp b/app/src/main/cpp/hdrplus2/src/HDRPlus.cpp new file mode 100644 index 00000000..3c979399 --- /dev/null +++ b/app/src/main/cpp/hdrplus2/src/HDRPlus.cpp @@ -0,0 +1,144 @@ +#include +#include +#include + +#ifdef _DEBUG +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include +#endif + +#include +#include "Burst.h" + +#include + +extern "C" void halide_register_argv_and_metadata( + int (*filter_argv_call)(void **), + const struct halide_filter_metadata_t *filter_metadata, + const char *const *extra_key_value_pairs) { +} +/* + * HDRPlus Class -- Houses file I/O, defines pipeline attributes and calls + * processes main stages of the pipeline. + */ +class HDRPlus { + const Burst &burst; + +public: + const Compression c; + const Gain g; + + HDRPlus(Burst& burst, const Compression c, const Gain g) + : burst(burst), c(c), g(g) + { + } + + Halide::Runtime::Buffer process() { + const int width = burst.GetWidth(); + const int height = burst.GetHeight(); + + Halide::Runtime::Buffer output_img(3, width, height); + +#ifdef _DEBUG + std::cerr << "Black point: " << burst.GetBlackLevel() << std::endl; + std::cerr << "White point: " << burst.GetWhiteLevel() << std::endl; +#endif + + const WhiteBalance wb = burst.GetWhiteBalance(); + std::cerr << "RGGB: " << wb.r << " " << wb.g0 << " " << wb.g1 << " " << wb.b + << std::endl; + + Halide::Runtime::Buffer imgs = burst.ToBuffer(); + if (imgs.dimensions() != 3 || imgs.extent(2) < 2) { + throw std::invalid_argument( + "The input of HDRPlus must be a 3-dimensional buffer with at least " + "two channels."); + } + + const int cfa_pattern = static_cast(burst.GetCfaPattern()); + auto ccm = burst.GetColorCorrectionMatrix(); + hdrplus_pipeline(imgs, burst.GetBlackLevel(), burst.GetWhiteLevel(), wb.r, + wb.g0, wb.g1, wb.b, cfa_pattern, ccm, c, g, output_img); + + // transpose to account for interleaved layout + output_img.transpose(0, 1); + output_img.transpose(1, 2); + + return output_img; + } + +#ifdef _DEBUG + static bool save_png(const std::string &dir_path, const std::string &img_name, + const Halide::Runtime::Buffer &img) { + const std::string img_path = dir_path + "/" + img_name; + + const int stride_in_bytes = img.width() * img.channels(); + if (!stbi_write_png(img_path.c_str(), img.width(), img.height(), + img.channels(), img.data(), stride_in_bytes)) { + std::cerr << "Unable to write output image '" << img_name << "'" + << std::endl; + return false; + } + return true; + } +#endif + +}; + +bool doHdrPlus(const std::vector< std::vector >& images, cv::Mat& mat) +{ + Compression c = 3.8f; + Gain g = 1.1f; + + Burst burst(images); + + HDRPlus hdr_plus(burst, c, g); + + Halide::Runtime::Buffer outputHdr = hdr_plus.process(); + +#ifdef _DEBUG + HDRPlus::save_png("/sdcard/com.xypower.mpapp/tmp", "2.png", outputHdr); +#endif + int width = outputHdr.width(); + int height = outputHdr.height(); + int channels = outputHdr.channels(); + int jch = 0; + mat = cv::Mat::zeros(height, width, CV_8UC3); + for (int i = 0; i < height; ++i) + { + jch = 0; + for (int j = 0; j < width; ++j) + { + for (int n = 0; n < channels; ++n) + { + mat.at(i, jch + n) = (uchar)outputHdr(j, i, n); + } + jch += channels; + } + } + + // if (!HDRPlus::save_png(dir_path, out_name, output)) { + + return true; +} + +#if 0 +int doHdrPlus(const std::string& dir_path, const std::string& out_name, const std::vector& in_names) { + + + Compression c = 3.8f; + Gain g = 1.1f; + + Burst burst(dir_path, in_names); + + HDRPlus hdr_plus(burst, c, g); + + Halide::Runtime::Buffer output = hdr_plus.process(); + + if (!HDRPlus::save_png(dir_path, out_name, output)) { + return EXIT_FAILURE; + } + + return 0; +} +#endif \ No newline at end of file diff --git a/app/src/main/cpp/hdrplus2/src/InputSource.cpp b/app/src/main/cpp/hdrplus2/src/InputSource.cpp new file mode 100644 index 00000000..cf82118d --- /dev/null +++ b/app/src/main/cpp/hdrplus2/src/InputSource.cpp @@ -0,0 +1,152 @@ +#include "InputSource.h" + +#include +#include + +#include "LibRaw2DngConverter.h" + +RawImage::RawImage(const std::string &path) + : Path(path), RawProcessor(std::make_shared()) { + // TODO: Check LibRaw parametres. + // RawProcessor->imgdata.params.X = Y; + + std::cerr << "Opening " << path << std::endl; + if (int err = RawProcessor->open_file(path.c_str())) { + std::cerr << "Cannot open file " << path + << " error: " << libraw_strerror(err) << std::endl; +#if 0 + throw std::runtime_error("Error opening " + path); +#endif + } + if (int err = RawProcessor->unpack()) { + std::cerr << "Cannot unpack file " << path + << " error: " << libraw_strerror(err) << std::endl; +#if 0 + throw std::runtime_error("Error opening " + path); +#endif + } + if (int ret = RawProcessor->raw2image()) { + std::cerr << "Cannot do raw2image on " << path + << " error: " << libraw_strerror(ret) << std::endl; +#if 0 + throw std::runtime_error("Error opening " + path); +#endif + } +} + +RawImage::RawImage(const uint8_t* data, size_t length) + : RawProcessor(std::make_shared()) +{ + std::cerr << "Opening raw from memory" << std::endl; + if (int err = RawProcessor->open_buffer((void *)data, length)) { + std::cerr << "Cannot open raw from memory" << " error: " << libraw_strerror(err) << std::endl; +#if 0 + throw std::runtime_error("Error opening raw"); +#endif + } + if (int err = RawProcessor->unpack()) { + std::cerr << "Cannot unpack raw from memory " << " error: " << libraw_strerror(err) << std::endl; +#if 0 + throw std::runtime_error("Error opening " + path); +#endif + } + if (int ret = RawProcessor->raw2image()) { + std::cerr << "Cannot do raw2image" << " error: " << libraw_strerror(ret) << std::endl; +#if 0 + throw std::runtime_error("Error opening " + path); +#endif + } +} + +WhiteBalance RawImage::GetWhiteBalance() const { + const auto coeffs = RawProcessor->imgdata.color.cam_mul; + // Scale multipliers to green channel + const float r = coeffs[0] / coeffs[1]; + const float g0 = 1.f; // same as coeffs[1] / coeffs[1]; + const float g1 = 1.f; + const float b = coeffs[2] / coeffs[1]; + return WhiteBalance{r, g0, g1, b}; +} + +void RawImage::CopyToBuffer(Halide::Runtime::Buffer &buffer) const { + const auto image_data = (uint16_t *)RawProcessor->imgdata.rawdata.raw_image; + const auto raw_width = RawProcessor->imgdata.rawdata.sizes.raw_width; + const auto raw_height = RawProcessor->imgdata.rawdata.sizes.raw_height; + const auto top = RawProcessor->imgdata.rawdata.sizes.top_margin; + const auto left = RawProcessor->imgdata.rawdata.sizes.left_margin; + Halide::Runtime::Buffer raw_buffer(image_data, raw_width, + raw_height); + buffer.copy_from(raw_buffer.translated({-left, -top})); +} + +void RawImage::WriteDng(const std::string &output_path, + const Halide::Runtime::Buffer &buffer) const { + LibRaw2DngConverter converter(*this); + converter.SetBuffer(buffer); + converter.Write(output_path); +} + +std::array RawImage::GetBlackLevel() const { + // See https://www.libraw.org/node/2471 + const auto raw_color = RawProcessor->imgdata.color; + const auto base_black_level = static_cast(raw_color.black); + + std::array black_level = { + base_black_level + static_cast(raw_color.cblack[0]), + base_black_level + static_cast(raw_color.cblack[1]), + base_black_level + static_cast(raw_color.cblack[2]), + base_black_level + static_cast(raw_color.cblack[3])}; + + if (raw_color.cblack[4] == 2 && raw_color.cblack[5] == 2) { + for (int x = 0; x < raw_color.cblack[4]; ++x) { + for (int y = 0; y < raw_color.cblack[5]; ++y) { + const auto index = y * 2 + x; + black_level[index] = raw_color.cblack[6 + index]; + } + } + } + + return black_level; +} + +int RawImage::GetScalarBlackLevel() const { + const auto black_level = GetBlackLevel(); + return static_cast( + *std::min_element(black_level.begin(), black_level.end())); +} + +std::string RawImage::GetCfaPatternString() const { + static const std::unordered_map CDESC_TO_CFA = { + {'R', 0}, {'G', 1}, {'B', 2}, {'r', 0}, {'g', 1}, {'b', 2}}; + const auto &cdesc = RawProcessor->imgdata.idata.cdesc; + return {CDESC_TO_CFA.at(cdesc[RawProcessor->COLOR(0, 0)]), + CDESC_TO_CFA.at(cdesc[RawProcessor->COLOR(0, 1)]), + CDESC_TO_CFA.at(cdesc[RawProcessor->COLOR(1, 0)]), + CDESC_TO_CFA.at(cdesc[RawProcessor->COLOR(1, 1)])}; +} + +CfaPattern RawImage::GetCfaPattern() const { + const auto cfa_pattern = GetCfaPatternString(); + if (cfa_pattern == std::string{0, 1, 1, 2}) { + return CfaPattern::CFA_RGGB; + } else if (cfa_pattern == std::string{1, 0, 2, 1}) { + return CfaPattern::CFA_GRBG; + } else if (cfa_pattern == std::string{2, 1, 1, 0}) { + return CfaPattern::CFA_BGGR; + } else if (cfa_pattern == std::string{1, 2, 0, 1}) { + return CfaPattern::CFA_GBRG; + } + throw std::invalid_argument("Unsupported CFA pattern: " + cfa_pattern); + return CfaPattern::CFA_UNKNOWN; +} + +Halide::Runtime::Buffer RawImage::GetColorCorrectionMatrix() const { + const auto raw_color = RawProcessor->imgdata.color; + Halide::Runtime::Buffer ccm(3, 3); + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + ccm(i, j) = raw_color.rgb_cam[j][i]; + } + } + return ccm; +} diff --git a/app/src/main/cpp/hdrplus2/src/InputSource.h b/app/src/main/cpp/hdrplus2/src/InputSource.h new file mode 100644 index 00000000..e3e1af13 --- /dev/null +++ b/app/src/main/cpp/hdrplus2/src/InputSource.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include + +#include + +#include "finish.h" +#include + +class RawImage { +public: + explicit RawImage(const std::string &path); + explicit RawImage(const uint8_t* data, size_t length); + + ~RawImage() = default; + + int GetWidth() const { return RawProcessor->imgdata.rawdata.sizes.width; } + + int GetHeight() const { return RawProcessor->imgdata.rawdata.sizes.height; } + + int GetScalarBlackLevel() const; + + std::array GetBlackLevel() const; + + int GetWhiteLevel() const { return RawProcessor->imgdata.color.maximum; } + + WhiteBalance GetWhiteBalance() const; + + std::string GetCfaPatternString() const; + CfaPattern GetCfaPattern() const; + + Halide::Runtime::Buffer GetColorCorrectionMatrix() const; + + void CopyToBuffer(Halide::Runtime::Buffer &buffer) const; + + // Writes current RawImage as DNG. If buffer was provided, then use it instead + // of internal buffer. + void WriteDng(const std::string &path, + const Halide::Runtime::Buffer &buffer = {}) const; + + std::shared_ptr GetRawProcessor() const { return RawProcessor; } + +private: + std::string Path; + std::shared_ptr RawProcessor; +}; diff --git a/app/src/main/cpp/hdrplus2/src/LibRaw2DngConverter.cpp b/app/src/main/cpp/hdrplus2/src/LibRaw2DngConverter.cpp new file mode 100644 index 00000000..5fb82087 --- /dev/null +++ b/app/src/main/cpp/hdrplus2/src/LibRaw2DngConverter.cpp @@ -0,0 +1,95 @@ +#include "LibRaw2DngConverter.h" + +#include + +#include + +#include "InputSource.h" + +LibRaw2DngConverter::LibRaw2DngConverter(const RawImage &raw) + : OutputStream(), Raw(raw), + Tiff(SetTiffFields( + TiffPtr(TIFFStreamOpen("", &OutputStream), TIFFClose))) {} + +LibRaw2DngConverter::TiffPtr +LibRaw2DngConverter::SetTiffFields(LibRaw2DngConverter::TiffPtr tiff_ptr) { + const auto RawProcessor = Raw.GetRawProcessor(); + const auto raw_color = RawProcessor->imgdata.color; + + const uint16_t bayer_pattern_dimensions[] = {2, 2}; + + const auto tiff = tiff_ptr.get(); + TIFFSetField(tiff, TIFFTAG_DNGVERSION, "\01\04\00\00"); + TIFFSetField(tiff, TIFFTAG_DNGBACKWARDVERSION, "\01\04\00\00"); + TIFFSetField(tiff, TIFFTAG_SUBFILETYPE, 0); + TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 16); + TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, 1); + TIFFSetField(tiff, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA); + TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 1); + TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + TIFFSetField(tiff, TIFFTAG_CFAREPEATPATTERNDIM, &bayer_pattern_dimensions); + + const std::string cfa = Raw.GetCfaPatternString(); + TIFFSetField(tiff, TIFFTAG_CFAPATTERN, cfa.c_str()); + + TIFFSetField(tiff, TIFFTAG_MAKE, "hdr-plus"); + TIFFSetField(tiff, TIFFTAG_UNIQUECAMERAMODEL, "hdr-plus"); + + const std::array color_matrix = { + raw_color.cam_xyz[0][0], raw_color.cam_xyz[0][1], raw_color.cam_xyz[0][2], + raw_color.cam_xyz[1][0], raw_color.cam_xyz[1][1], raw_color.cam_xyz[1][2], + raw_color.cam_xyz[2][0], raw_color.cam_xyz[2][1], raw_color.cam_xyz[2][2], + }; + TIFFSetField(tiff, TIFFTAG_COLORMATRIX1, 9, &color_matrix); + TIFFSetField(tiff, TIFFTAG_CALIBRATIONILLUMINANT1, 21); // D65 + + const std::array as_shot_neutral = { + 1.f / (raw_color.cam_mul[0] / raw_color.cam_mul[1]), 1.f, + 1.f / (raw_color.cam_mul[2] / raw_color.cam_mul[1])}; + TIFFSetField(tiff, TIFFTAG_ASSHOTNEUTRAL, 3, &as_shot_neutral); + + TIFFSetField(tiff, TIFFTAG_CFALAYOUT, 1); // Rectangular (or square) layout + TIFFSetField( + tiff, TIFFTAG_CFAPLANECOLOR, 3, + "\00\01\02"); // RGB + // https://www.awaresystems.be/imaging/tiff/tifftags/cfaplanecolor.html + + const std::array black_level = Raw.GetBlackLevel(); + TIFFSetField(tiff, TIFFTAG_BLACKLEVEL, 4, &black_level); + + static const uint32_t white_level = raw_color.maximum; + TIFFSetField(tiff, TIFFTAG_WHITELEVEL, 1, &white_level); + + if (RawProcessor->imgdata.sizes.flip > 0) { + // Seems that LibRaw uses LibTIFF notation. + TIFFSetField(tiff, TIFFTAG_ORIENTATION, RawProcessor->imgdata.sizes.flip); + } else { + TIFFSetField(tiff, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + } + return tiff_ptr; +} + +void LibRaw2DngConverter::SetBuffer( + const Halide::Runtime::Buffer &buffer) const { + const auto width = buffer.width(); + const auto height = buffer.height(); + const auto tiff = Tiff.get(); + TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height); + + uint16_t *row_pointer = buffer.data(); + for (int row = 0; row < height; row++) { + TIFFWriteScanline(tiff, row_pointer, row, 0); + row_pointer += width; + } +} + +void LibRaw2DngConverter::Write(const std::string &path) const { + TIFFCheckpointDirectory(Tiff.get()); + TIFFFlush(Tiff.get()); + std::ofstream output(path, std::ofstream::binary); + output << OutputStream.str(); +} diff --git a/app/src/main/cpp/hdrplus2/src/LibRaw2DngConverter.h b/app/src/main/cpp/hdrplus2/src/LibRaw2DngConverter.h new file mode 100644 index 00000000..2a7f316c --- /dev/null +++ b/app/src/main/cpp/hdrplus2/src/LibRaw2DngConverter.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +#include + +class RawImage; + +class LibRaw2DngConverter { + using TiffPtr = std::shared_ptr; + TiffPtr SetTiffFields(TiffPtr tiff_ptr); + +public: + explicit LibRaw2DngConverter(const RawImage &raw); + + void SetBuffer(const Halide::Runtime::Buffer &buffer) const; + + void Write(const std::string &path) const; + +private: + std::ostringstream OutputStream; + const RawImage &Raw; + std::shared_ptr Tiff; +}; diff --git a/app/src/main/cpp/hdrplus2/src/finish.h b/app/src/main/cpp/hdrplus2/src/finish.h new file mode 100644 index 00000000..63bb5f27 --- /dev/null +++ b/app/src/main/cpp/hdrplus2/src/finish.h @@ -0,0 +1,36 @@ +#ifndef HDRPLUS_FINISH_H_ +#define HDRPLUS_FINISH_H_ + +#include + +template struct TypedWhiteBalance { + template + explicit TypedWhiteBalance(const TypedWhiteBalance &other) + : r(other.r), g0(other.g0), g1(other.g1), b(other.b) {} + + TypedWhiteBalance(T r, T g0, T g1, T b) : r(r), g0(g0), g1(g1), b(b) {} + + T r; + T g0; + T g1; + T b; +}; + +using WhiteBalance = TypedWhiteBalance; + +typedef uint16_t BlackPoint; +typedef uint16_t WhitePoint; + +typedef float Compression; +typedef float Gain; + +enum class CfaPattern : int { + CFA_UNKNOWN = 0, + CFA_RGGB = 1, + CFA_GRBG = 2, + CFA_BGGR = 3, + CFA_GBRG = 4 +}; + + +#endif \ No newline at end of file diff --git a/app/src/main/java/com/xypower/mpapp/BridgeProvider.java b/app/src/main/java/com/xypower/mpapp/BridgeProvider.java index f5bd9f21..87ad2a67 100644 --- a/app/src/main/java/com/xypower/mpapp/BridgeProvider.java +++ b/app/src/main/java/com/xypower/mpapp/BridgeProvider.java @@ -5,6 +5,8 @@ import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.UriMatcher; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; @@ -18,7 +20,13 @@ import com.xypower.common.MicroPhotoContext; import org.json.JSONObject; +import java.io.BufferedReader; import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.List; public class BridgeProvider extends ContentProvider { @@ -34,6 +42,8 @@ public class BridgeProvider extends ContentProvider { private final static String PATH_TAKE_PHOTO = "/takePhoto"; private final static String PATH_TAKE_VIDEO = "/takeVideo"; + private final static String PATH_HDRPLUS = "/hdrplus"; + private final static String PATH_RECOG_PIC = "/recogPic"; public BridgeProvider() { @@ -106,6 +116,7 @@ public class BridgeProvider extends ContentProvider { matcher.addURI(AUTHORITY, PATH_GEN_CERT_REQ, 4); matcher.addURI(AUTHORITY, PATH_TAKE_PHOTO, 5); matcher.addURI(AUTHORITY, PATH_TAKE_VIDEO, 6); + matcher.addURI(AUTHORITY, PATH_HDRPLUS, 7); int res = 0; int matched = matcher.match(uri); @@ -128,6 +139,9 @@ public class BridgeProvider extends ContentProvider { case 6: res = takeVideo(uri, values); break; + case 7: + res = hdrPlus(uri, values); + break; default: break; } @@ -416,4 +430,73 @@ public class BridgeProvider extends ContentProvider { return 1; } + + private int hdrPlus(Uri uri, ContentValues values) { + int rotation = values.containsKey("rotation") ? values.getAsInteger("rotation").intValue() : -1; + int frontCamera = values.containsKey("front") ? values.getAsInteger("front").intValue() : 0; + String outputPath = values.containsKey("output") ? values.getAsString("output") : null; + int numberOfCaptures = values.containsKey("captures") ? values.getAsInteger("captures").intValue() : 0; + List paths = new ArrayList<>(); + for (int idx = 0; idx < numberOfCaptures; idx++) { + String key = "path" + Integer.toString(idx + 1); + String path = values.containsKey(key) ? values.getAsString(key) : null; + if (!TextUtils.isEmpty(path)) { + paths.add(path); + } + } + + + + ApplicationInfo applicationInfo = null; + Context context = getContext(); + try { + applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_SHARED_LIBRARY_FILES); + } catch (Exception ex) { + + } + + Log.d(TAG, "nativeLibraryDir= " + applicationInfo.nativeLibraryDir); + String exeFilePath = applicationInfo.nativeLibraryDir + '/' + "libhdrp.so"; + File hdrpFile = new File(exeFilePath); + if (!hdrpFile.exists()) { + return 0; + } + + String cmd = exeFilePath + " " + Integer.toString(rotation) + " "; + cmd += Integer.toString(frontCamera) + " "; + cmd += outputPath + " " + TextUtils.join(" ", paths); + + String[] params = new String[]{""}; + File workDir = context.getFilesDir(); + int exitCode = 0; + + try { + Process process = Runtime.getRuntime().exec(cmd, params, workDir.getAbsoluteFile()); + // Intrinsics.checkNotNullExpressionValue(process, "process"); + InputStream inputStream = process.getInputStream(); + BufferedReader reader = new BufferedReader((Reader)(new InputStreamReader(inputStream))); + + // StringBuilder stringBuilder = new StringBuilder(); + while(true) { + String line = reader.readLine(); + if (line == null) { + exitCode = process.exitValue(); + reader.close(); + process.destroy(); + break; + } + + if (line != null) { + // this.outputCallback.invoke(var5); + Log.d("HDRPlus", line); + // stringBuilder.append(line); + // stringBuilder.append("\r\n"); + } + } + } catch (Exception ex) { + ex.printStackTrace(); + } + + return 1; + } } \ No newline at end of file diff --git a/app/src/main/java/com/xypower/mpapp/MainActivity.java b/app/src/main/java/com/xypower/mpapp/MainActivity.java index ddd3bb23..e2ee7f05 100644 --- a/app/src/main/java/com/xypower/mpapp/MainActivity.java +++ b/app/src/main/java/com/xypower/mpapp/MainActivity.java @@ -115,7 +115,9 @@ public class MainActivity extends AppCompatActivity { } } + Log.d(TAG, "Start inflate"); binding = ActivityMainBinding.inflate(getLayoutInflater()); + Log.d(TAG, "Finish inflate"); setContentView(binding.getRoot()); // getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); getWindow().addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); @@ -163,6 +165,10 @@ public class MainActivity extends AppCompatActivity { Intent intent = getIntent(); final int noDelay = intent.getIntExtra("noDelay", 0); int rebootFlag = intent.getIntExtra("reboot", 0); + String reason = intent.getStringExtra("reason"); + if (!TextUtils.isEmpty(reason)) { + Log.w(TAG, "App Started with reason: " + reason); + } if (rebootFlag == 1) { Log.i(TAG, "After Reboot"); } @@ -386,7 +392,7 @@ public class MainActivity extends AppCompatActivity { @Override public void onClick(View v) { Context context = v.getContext().getApplicationContext(); - MicroPhotoService.restartApp(context, context.getPackageName()); + MicroPhotoService.restartApp(context, context.getPackageName(), "Manual Restart From MainActivity"); } }); diff --git a/app/src/main/java/com/xypower/mpapp/MicroPhotoService.java b/app/src/main/java/com/xypower/mpapp/MicroPhotoService.java index e848303c..125397c6 100644 --- a/app/src/main/java/com/xypower/mpapp/MicroPhotoService.java +++ b/app/src/main/java/com/xypower/mpapp/MicroPhotoService.java @@ -9,10 +9,13 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; +import android.content.ComponentCallbacks2; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.ImageDecoder; @@ -45,6 +48,7 @@ import androidx.core.app.NotificationCompat; import androidx.core.content.FileProvider; import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import android.provider.MediaStore; import android.telephony.SignalStrength; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; @@ -63,9 +67,17 @@ import com.xypower.mpapp.utils.DeviceUtil; import com.xypower.mpapp.v2.Camera2VideoActivity; import com.xypower.mpapp.video.RawActivity; +import java.io.BufferedReader; import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.RandomAccessFile; +import java.io.Reader; import java.lang.reflect.Method; import java.net.InetAddress; +import java.net.URI; +import java.nio.channels.FileLock; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -146,6 +158,9 @@ public class MicroPhotoService extends Service { public static boolean isRunning = false; + FileOutputStream mAppRunningFile; + FileLock mAppLock; + private Runnable delayedSleep = new Runnable() { @Override public void run() { @@ -156,6 +171,43 @@ public class MicroPhotoService extends Service { public MicroPhotoService() { } + + @Override + public void onTrimMemory(int level) { + Log.w(TAG, "onTrimMemory level=" + level); + if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) { + // Clear the caches. Note all pending requests will be removed too. + final Context context = getApplicationContext(); + try { + infoLog("Restart MpApp as for TrimMemory"); + mHander.postDelayed(new Runnable() { + @Override + public void run() { + restartApp(context, MicroPhotoContext.PACKAGE_NAME_MPAPP, "TrimMemory"); + } + }, 1000); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + + @Override + public void onLowMemory() { + final Context context = getApplicationContext(); + try { + Intent intent = new Intent(this, MainActivity.class); + int noDelay = 1; + intent.putExtra("noDelay", noDelay); + PendingIntent pi = PendingIntent.getActivity(this,0, intent,0); + AlarmManager alarmManager=(AlarmManager)getSystemService(ALARM_SERVICE); + alarmManager.set(AlarmManager.RTC_WAKEUP,System.currentTimeMillis() + 5000, pi); + infoLog("Restart MpApp after 5s as for LowMemory"); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. @@ -165,6 +217,30 @@ public class MicroPhotoService extends Service { public void onCreate() { super.onCreate(); + try { + final String appPath = MicroPhotoContext.buildMpAppDir(this); + File lockerFile = new File(appPath); + lockerFile = new File(lockerFile, "data/alive/running"); + mAppRunningFile = new FileOutputStream(lockerFile); + + for (int idx = 0; idx < 3; idx++) { + try { + mAppLock = mAppRunningFile.getChannel().tryLock(); + if (mAppLock != null && mAppLock.isValid()) { + break; + } + try { + Thread.sleep(16); + } catch (Exception ex) { + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } catch (Exception ex) { + ex.printStackTrace(); + } + mHander = new ServiceHandler(); mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); @@ -235,6 +311,18 @@ public class MicroPhotoService extends Service { @Override public void onDestroy() { + try { + + if (mAppLock != null) { + mAppLock.close(); + } + if (mAppRunningFile != null) { + mAppRunningFile.close(); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + mStateService = STATE_SERVICE.NOT_CONNECTED; Log.w(TAG, "MicroPhotoService::onDestroy called"); @@ -346,27 +434,6 @@ public class MicroPhotoService extends Service { Log.i(TAG, "PhotoTimer Fired: CH=" + channel + " PR=" + preset); mService.notifyToTakePhoto(mService.mNativeHandle, channel, preset, ts, photoOrVideo); } - - File cameraAdbCfg = new File(MicroPhotoContext.buildMpAppDir(mService.getApplication()), "data/cameraAdb.cfg"); - if (cameraAdbCfg.exists()) { - final String appPath = MicroPhotoContext.buildMpAppDir(context); - mService.mHander.postDelayed(new Runnable() { - @Override - public void run() { - final CameraAdb cameraAdb = new CameraAdb(context, appPath); - cameraAdb.setCallback(new Runnable() { - @Override - public void run() { - List targetPaths = cameraAdb.getTargetPaths(); - for (String targetPath : targetPaths) { - mService.sendExternalPhoto(mService.mNativeHandle, targetPath); - } - } - }); - cameraAdb.takePhoto(); - } - }, 10000 * cnt); - } } // Register Next Photo Timer @@ -397,7 +464,7 @@ public class MicroPhotoService extends Service { int restart = intent.getIntExtra("restart", 0); Log.i(TAG, "UPD CFG Fired ACTION=" + action + " restart=" + restart); if (restart != 0) { - MicroPhotoService.restartApp(context, context.getPackageName()); + MicroPhotoService.restartApp(context, context.getPackageName(), "Config Updated"); } else if (mService.mNativeHandle != 0) { mService.reloadConfigs(mService.mNativeHandle); } @@ -500,13 +567,16 @@ public class MicroPhotoService extends Service { String path = intent.getStringExtra("path"); String md5 = intent.getStringExtra("md5"); } else if (TextUtils.equals(ACTION_GPS_TIMEOUT, action)) { - mService.mPreviousGpsTimer = null; + Log.i(TAG, action); try { mService.mLocationManager.removeUpdates(mService.mLocationListener); + Log.i(TAG, "After removeUpdates"); } catch (Exception ex) { ex.printStackTrace(); } mService.enableGps(false); + Log.i(TAG, "After disable GPS"); + mService.mPreviousGpsTimer = null; } else if (TextUtils.equals(ACTION_RESTART, action)) { String reason = intent.getStringExtra("reason"); @@ -516,7 +586,7 @@ public class MicroPhotoService extends Service { } catch (Exception ex) { ex.printStackTrace(); } - MicroPhotoService.restartApp(context.getApplicationContext(), MicroPhotoContext.PACKAGE_NAME_MPAPP); + MicroPhotoService.restartApp(context.getApplicationContext(), MicroPhotoContext.PACKAGE_NAME_MPAPP, "Restart Cmd"); } } } @@ -838,9 +908,18 @@ public class MicroPhotoService extends Service { } String tfCardPath = MicroPhotoContext.getSecondaryStoragePath(context); + String nativeLibraryDir = null; + ApplicationInfo applicationInfo = null; + try { + applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_SHARED_LIBRARY_FILES); + nativeLibraryDir = applicationInfo.nativeLibraryDir; + } catch (Exception ex) { + ex.printStackTrace(); + } + service.mNativeHandle = init(appPath, server, port, cmdid, protocol, networkProtocol, encryptData, 0, service.getSignalLevel(), versionCode, - BuildConfig.BUILD_TIMESTAMP, simcard, tfCardPath); + BuildConfig.BUILD_TIMESTAMP, simcard, tfCardPath, nativeLibraryDir); if (service.mNativeHandle != 0) { isRunning = true; @@ -870,6 +949,7 @@ public class MicroPhotoService extends Service { } }; + Log.d(TAG, "Start Service from MicroPhotoService"); Thread th = new Thread(runnable); th.start(); } @@ -1225,14 +1305,14 @@ public class MicroPhotoService extends Service { return true; } - public void reboot(final int rebootType, final long timeout) { + public void reboot(final int rebootType, final long timeout, final String reason) { Runnable runnable = new Runnable() { @Override public void run() { if (rebootType == 0) { Context context = MicroPhotoService.this.getApplicationContext(); - restartApp(context, context.getPackageName()); + restartApp(context, context.getPackageName(), reason); } else { Log.w(TAG, "Recv REBOOT command"); @@ -1243,11 +1323,14 @@ public class MicroPhotoService extends Service { mHander.postDelayed(runnable, timeout > 0 ? timeout : 1000); } - public static void restartApp(Context context, String packageName) { + public static void restartApp(Context context, String packageName, String reason) { Intent intent = new Intent(context, MainActivity.class); int noDelay = 1; intent.putExtra("noDelay", noDelay); + if (!TextUtils.isEmpty(reason)) { + intent.putExtra("reason", reason); + } intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); context.startActivity(intent); @@ -1301,6 +1384,60 @@ public class MicroPhotoService extends Service { SysApi.enableGps(getApplicationContext(), enabled); } + private int execHdrplus(int rotation, int frontCamera, String outputPath, String pathsWithSpace) { + + ApplicationInfo applicationInfo = null; + Context context = getApplicationContext(); + try { + applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_SHARED_LIBRARY_FILES); + } catch (Exception ex) { + + } + + String exeFilePath = applicationInfo.nativeLibraryDir + '/' + "libhdrp.so"; + File hdrpFile = new File(exeFilePath); + if (!hdrpFile.exists()) { + return -1; + } + + String cmd = exeFilePath + " " + Integer.toString(rotation) + " "; + cmd += Integer.toString(frontCamera) + " "; + cmd += outputPath + " " + pathsWithSpace; + + String[] params = new String[]{""}; + File workDir = context.getFilesDir(); + int exitCode = 0; + + try { + Process process = Runtime.getRuntime().exec(cmd, params, workDir.getAbsoluteFile()); + // Intrinsics.checkNotNullExpressionValue(process, "process"); + InputStream inputStream = process.getInputStream(); + BufferedReader reader = new BufferedReader((Reader)(new InputStreamReader(inputStream))); + + // StringBuilder stringBuilder = new StringBuilder(); + while(true) { + String line = reader.readLine(); + if (line == null) { + exitCode = process.exitValue(); + reader.close(); + process.destroy(); + break; + } + + if (line != null) { + // this.outputCallback.invoke(var5); + Log.d("HDRPlus", line); + // stringBuilder.append(line); + // stringBuilder.append("\r\n"); + } + } + } catch (Exception ex) { + ex.printStackTrace(); + } + + return exitCode; + } + /* TelephonyManager telephonyManager = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE); // for example value of first element @@ -1311,7 +1448,7 @@ cellSignalStrengthGsm.getDbm(); protected native long init(String appPath, String ip, int port, String cmdid, int protocol, int networkProtocl, int encryptData, long netHandle, int signalLevel, - int versionCode, long buildTime, String simcard, String tfCardPath); + int versionCode, long buildTime, String simcard, String tfCardPath, String nativeLibraryDir); protected native long getHeartbeatDuration(long handler); protected native long[] getPhotoTimeData(long handler, long startTime); protected native long[] getPhotoTimeData2(long handler); @@ -1327,7 +1464,7 @@ cellSignalStrengthGsm.getDbm(); protected native void burstCaptureFinished(long handler, boolean result, int numberOfCaptures, String pathsJoinedByTab, boolean frontCamera, int rotation, long photoId); public static native long takePhoto(int channel, int preset, boolean photoOrVideo, String configFilePath, String path); public static native void releaseDeviceHandle(long deviceHandle); - public static native boolean sendExternalPhoto(long deviceHandle, String path); + public static native boolean sendExternalPhoto(long deviceHandle, String path, long photoInfo); public static native void infoLog(String log); public static native void setOtgState(boolean enabled); @@ -1417,6 +1554,58 @@ cellSignalStrengthGsm.getDbm(); } }; + + public void callSystemCamera(final int cameraId, final long photoId) { + Context context = getApplicationContext(); + + /* + Intent intent = null; + if (cameraId == 1) { + + intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + + intent.putExtra("android.intent.extras.CAMERA_FACING", android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT); + + intent.putExtra("android.intent.extras.LENS_FACING_FRONT", 1); + + intent.putExtra("android.intent.extra.USE_FRONT_CAMERA", true); + + } else{ + intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + } + + String appPath = MicroPhotoContext.buildMpAppDir(context); + File targetPath = new File(new File(appPath), "tmp/" + Long.toString(photoId) + ".jpg"); + + + // Uri uri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", targetPath); + + intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(targetPath)); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + if (intent.resolveActivity(getPackageManager()) != null) { + startActivity(intent); + } + */ + + final CameraAdb cameraAdb = new CameraAdb(context, MicroPhotoContext.buildMpAppDir(context)); + + cameraAdb.setCallback(new Runnable() { + @Override + public void run() { + List targetPaths = cameraAdb.getTargetPaths(); + if (targetPaths.isEmpty()) { + recordingFinished(mNativeHandle, true, false, null, photoId); + } else { + for (String targetPath : targetPaths) { + recordingFinished(mNativeHandle, true, true, targetPath, photoId); + } + } + } + }); + cameraAdb.takePhoto(cameraId); + + } ////////////////////////GPS//////////////////// private void setDefaultDataSubId(int subId) { diff --git a/app/src/main/java/com/xypower/mpapp/adb/CameraAdb.java b/app/src/main/java/com/xypower/mpapp/adb/CameraAdb.java index ee2a589c..2da62052 100644 --- a/app/src/main/java/com/xypower/mpapp/adb/CameraAdb.java +++ b/app/src/main/java/com/xypower/mpapp/adb/CameraAdb.java @@ -8,6 +8,7 @@ import android.util.Log; import com.dev.devapi.api.SysApi; import com.xypower.common.FilesUtils; import com.xypower.common.MicroPhotoContext; +import com.xypower.mpapp.BuildConfig; import com.xypower.mpapp.MicroPhotoService; import com.xypower.mpapp.v2.Camera2VideoActivity; @@ -78,19 +79,19 @@ public class CameraAdb { mAdbKeyPair = AdbKeyPair.read(priKeyFile, pubKeyFile); } - public void takePhoto() { + public void takePhoto(final int cameraId) { new Thread(new Runnable() { @Override public void run() { - takePhotoImpl(); + takePhotoImpl(cameraId); } }).start(); } - private void takePhotoImpl() { + private void takePhotoImpl(final int cameraId) { long requestTime = System.currentTimeMillis() / 1000; - takePhoto(false); + takePhoto(cameraId == 1); long takingTime = System.currentTimeMillis() / 1000; sleep(1500); String path = movePhoto(false, requestTime, takingTime); @@ -100,21 +101,6 @@ public class CameraAdb { sleep(100); SysApi.forceStopApp(mContext, "com.mediatek.camera"); - sleep(1000); - - requestTime = System.currentTimeMillis() / 1000; - takePhoto(true); - takingTime = System.currentTimeMillis() / 1000; - - sleep(200); - - SysApi.forceStopApp(mContext, "com.mediatek.camera"); - - sleep(250); - path = movePhoto(true, requestTime, takingTime); - if (!TextUtils.isEmpty(path)) { - mTargetPaths.add(path); - } if (mRunnable != null) { mRunnable.run(); } @@ -140,8 +126,12 @@ public class CameraAdb { File targetFile = new File(new File(photoPath), photoFile); try { File srcFile = opFile.get(); - - res = srcFile.renameTo(targetFile); + if (BuildConfig.DEBUG) { + FilesUtils.copyFile(srcFile, targetFile); + res = true; + } else { + res = srcFile.renameTo(targetFile); + } if (res) { targetPath = targetFile.getAbsolutePath(); break; @@ -168,7 +158,7 @@ public class CameraAdb { } } - public void takePhoto(final boolean frontCamera) { + protected void takePhoto(final boolean frontCamera) { Dadb adb = Dadb.create(mDeviceIp, 5555, mAdbKeyPair); if (adb == null) { diff --git a/app/src/main/res/layout-land/activity_main.xml b/app/src/main/res/layout-land/activity_main.xml index 99408fba..305f41a3 100644 --- a/app/src/main/res/layout-land/activity_main.xml +++ b/app/src/main/res/layout-land/activity_main.xml @@ -352,85 +352,6 @@ app:layout_constraintStart_toStartOf="@+id/btnCameraInfo" app:layout_constraintTop_toTopOf="@+id/btnCameraInfo" /> -