Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8277d26659 | ||
|
|
c83f36c976 | ||
|
|
29e0c1e9a8 | ||
|
|
7b07e7fbc1 | ||
|
|
b6ac86a134 | ||
|
|
6bbbb3c3ef | ||
|
|
e38da96e37 | ||
|
|
9ee028248d | ||
|
|
84cc693593 | ||
|
|
4cf1990b16 | ||
|
|
2130cf0e6b | ||
|
|
6717047d1d | ||
|
|
da2c97e412 | ||
|
|
db80a5dbbd | ||
|
|
634d22f3be | ||
|
|
835f768a4a | ||
|
|
1859706412 | ||
|
|
9c71e40c97 | ||
|
|
84d567674f | ||
|
|
43b00c8446 | ||
|
|
e23ccd75cd | ||
|
|
8623b71ab7 | ||
|
|
16efcbcff3 |
@@ -1,7 +1,8 @@
|
|||||||
plugins {
|
plugins {
|
||||||
//alias(libs.plugins.android.application)
|
//alias(libs.plugins.android.application)
|
||||||
alias(libs.plugins.android.library)
|
alias(libs.plugins.android.library)
|
||||||
id 'org.jetbrains.kotlin.android'
|
alias(libs.plugins.kotlin.android)
|
||||||
|
//id 'org.jetbrains.kotlin.android'
|
||||||
}
|
}
|
||||||
|
|
||||||
ext.vvl_version='1.4.321.0'
|
ext.vvl_version='1.4.321.0'
|
||||||
@@ -30,6 +31,17 @@ android {
|
|||||||
debug {
|
debug {
|
||||||
debuggable true
|
debuggable true
|
||||||
jniDebuggable true
|
jniDebuggable true
|
||||||
|
|
||||||
|
externalNativeBuild {
|
||||||
|
cmake {
|
||||||
|
arguments "-DCMAKE_BUILD_TYPE=Debug"
|
||||||
|
cppFlags "-DDEBUG -O0 -g"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ndk {
|
||||||
|
debugSymbolLevel 'FULL' // Debug 版本包含完整符号
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
release {
|
release {
|
||||||
@@ -43,13 +55,13 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 添加 Kotlin 编译选项
|
// 添加 Kotlin 编译选项
|
||||||
kotlinOptions {
|
// kotlinOptions {
|
||||||
jvmTarget = '17' // 与 Java 版本保持一致
|
// jvmTarget = '17' // 与 Java 版本保持一致
|
||||||
}
|
// }
|
||||||
|
|
||||||
kotlin {
|
// kotlin {
|
||||||
jvmToolchain(21)
|
// jvmToolchain(17)
|
||||||
}
|
// }
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
prefab true
|
prefab true
|
||||||
@@ -62,10 +74,10 @@ android {
|
|||||||
'libs/tasks-vision/jni',
|
'libs/tasks-vision/jni',
|
||||||
'libs/tasks-core/jni'
|
'libs/tasks-core/jni'
|
||||||
]
|
]
|
||||||
java.srcDirs += [
|
//java.srcDirs += [
|
||||||
'libs/tasks-vision/vision_classes',
|
// 'libs/tasks-vision/vision_classes',
|
||||||
'libs/tasks-core/core_classes'
|
// 'libs/tasks-core/core_classes'
|
||||||
]
|
//]
|
||||||
res.srcDirs += [
|
res.srcDirs += [
|
||||||
'libs/tasks-vision/res',
|
'libs/tasks-vision/res',
|
||||||
'libs/tasks-core/res'
|
'libs/tasks-core/res'
|
||||||
@@ -79,6 +91,13 @@ android {
|
|||||||
version '3.22.1'
|
version '3.22.1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
packagingOptions {
|
||||||
|
pickFirst '**/*.jar'
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = '17'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -86,16 +105,17 @@ dependencies {
|
|||||||
implementation libs.appcompat
|
implementation libs.appcompat
|
||||||
implementation libs.material
|
implementation libs.material
|
||||||
implementation libs.games.activity
|
implementation libs.games.activity
|
||||||
|
implementation libs.androidx.core.core.ktx
|
||||||
testImplementation libs.junit
|
testImplementation libs.junit
|
||||||
androidTestImplementation libs.ext.junit
|
androidTestImplementation libs.ext.junit
|
||||||
androidTestImplementation libs.espresso.core
|
androidTestImplementation libs.espresso.core
|
||||||
|
|
||||||
|
|
||||||
//implementation 'com.google.protobuf:protobuf-javalite:3.21.12'
|
implementation 'com.google.protobuf:protobuf-javalite:3.21.12'
|
||||||
//implementation files('libs/tasks-vision.jar', 'libs/tasks-core.jar')
|
implementation files('libs/tasks-vision.jar', 'libs/tasks-core.jar')
|
||||||
|
|
||||||
implementation fileTree(dir: 'libs/tasks-vision', include: ['*.jar'])
|
//implementation fileTree(dir: 'libs/tasks-vision', include: ['*.jar'])
|
||||||
implementation fileTree(dir: 'libs/tasks-core', include: ['*.jar'])
|
//implementation fileTree(dir: 'libs/tasks-core', include: ['*.jar'])
|
||||||
|
|
||||||
|
|
||||||
def camerax_version = '1.4.2'
|
def camerax_version = '1.4.2'
|
||||||
|
|||||||
|
After Width: | Height: | Size: 16 KiB |
@@ -5,12 +5,20 @@ layout (binding = 1) uniform sampler2D samplerColor;
|
|||||||
layout (location = 0) in vec2 inUV;
|
layout (location = 0) in vec2 inUV;
|
||||||
layout (location = 1) in vec3 inNormal;
|
layout (location = 1) in vec3 inNormal;
|
||||||
|
|
||||||
|
layout(push_constant) uniform PushConstants {
|
||||||
|
float myValue;
|
||||||
|
float ux;
|
||||||
|
float uy;
|
||||||
|
} pushConstants;
|
||||||
|
|
||||||
|
|
||||||
layout (location = 0) out vec4 outFragColor;
|
layout (location = 0) out vec4 outFragColor;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
vec4 color = texture(samplerColor, inUV);
|
vec2 pos = vec2(pushConstants.ux/2048.f, pushConstants.uy/2048.f);
|
||||||
|
vec2 uv = pos + vec2(inUV.x/8.f, inUV.y/8.f);
|
||||||
|
vec4 color = texture(samplerColor, uv);
|
||||||
|
|
||||||
outFragColor = color;
|
outFragColor = color;
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ layout (location = 1) out vec3 outNormal;
|
|||||||
|
|
||||||
layout(push_constant) uniform PushConstants {
|
layout(push_constant) uniform PushConstants {
|
||||||
float myValue;
|
float myValue;
|
||||||
|
float ux;
|
||||||
|
float uy;
|
||||||
} pushConstants;
|
} pushConstants;
|
||||||
|
|
||||||
out gl_PerVertex
|
out gl_PerVertex
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ android_app* g_android_app = nullptr;
|
|||||||
FaceApp* g_Application = nullptr;
|
FaceApp* g_Application = nullptr;
|
||||||
AAssetManager* g_assetManager = nullptr;
|
AAssetManager* g_assetManager = nullptr;
|
||||||
|
|
||||||
|
jobject g_callback = nullptr;
|
||||||
|
jobject g_callbackAnimationFinished = nullptr;
|
||||||
|
|
||||||
void handle_cmd(android_app *pApp, int32_t cmd) {
|
void handle_cmd(android_app *pApp, int32_t cmd) {
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case APP_CMD_INIT_WINDOW:
|
case APP_CMD_INIT_WINDOW:
|
||||||
@@ -80,7 +83,7 @@ void android_main(struct android_app *pApp) {
|
|||||||
auto frameTime = end_time - last_update_time;
|
auto frameTime = end_time - last_update_time;
|
||||||
last_update_time = end_time;
|
last_update_time = end_time;
|
||||||
auto function_time = (end_time - start_time);
|
auto function_time = (end_time - start_time);
|
||||||
aout << "function_time:" << function_time << " fps:" << (1000/frameTime) << std::endl;
|
//aout << "function_time:" << function_time << " fps:" << (1000/frameTime) << std::endl;
|
||||||
if(function_time < 35)
|
if(function_time < 35)
|
||||||
{
|
{
|
||||||
this_thread::sleep_for(chrono::milliseconds((35-function_time)));
|
this_thread::sleep_for(chrono::milliseconds((35-function_time)));
|
||||||
@@ -147,10 +150,194 @@ Java_com_hmwl_face_1sdk_FaceActivity_SetCppInitArg(JNIEnv *env, jobject thiz, js
|
|||||||
}
|
}
|
||||||
memcpy(g_InitArgString, nativeString, len);
|
memcpy(g_InitArgString, nativeString, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_com_hmwl_face_1sdk_FaceActivity_ChangeStateCpp(JNIEnv *env, jobject thiz, jstring json) {
|
Java_com_hmwl_face_1sdk_FaceActivity_StopRunning(JNIEnv *env, jobject thiz) {
|
||||||
// TODO: implement ChangeStateCpp()
|
g_Application->_running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JavaVM* g_jvm = nullptr;
|
||||||
|
|
||||||
|
// 添加 JNI_OnLoad 函数
|
||||||
|
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||||
|
g_jvm = vm; // 保存 JavaVM 指针,供其他线程使用
|
||||||
|
return JNI_VERSION_1_6;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CppCallback()
|
||||||
|
{
|
||||||
|
JNIEnv* env = nullptr;
|
||||||
|
bool needDetach = false;
|
||||||
|
|
||||||
|
// 获取当前线程的 JNIEnv
|
||||||
|
JavaVM* vm = g_jvm; // 你需要提前保存 JavaVM* 到全局变量 g_jvm
|
||||||
|
int status = vm->GetEnv((void**)&env, JNI_VERSION_1_6);
|
||||||
|
if (status == JNI_EDETACHED) {
|
||||||
|
// 当前线程未 attach,需 attach
|
||||||
|
if (vm->AttachCurrentThread(&env, nullptr) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
needDetach = true;
|
||||||
|
} else if (status != JNI_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_callback == nullptr) {
|
||||||
|
if (needDetach) vm->DetachCurrentThread();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 获取 CallbackInterface 的 class
|
||||||
|
jclass callbackClass = env->GetObjectClass(g_callback);
|
||||||
|
if (callbackClass == nullptr) {
|
||||||
|
if (needDetach) vm->DetachCurrentThread();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取 OnLoadActionFinished 方法 ID(注意方法名大小写必须一致!)
|
||||||
|
jmethodID methodId = env->GetMethodID(callbackClass, "OnLoadMotionListFinished", "(Ljava/lang/String;)V");
|
||||||
|
if (methodId == nullptr) {
|
||||||
|
env->ExceptionDescribe(); // 打印异常
|
||||||
|
env->ExceptionClear();
|
||||||
|
if (needDetach) vm->DetachCurrentThread();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 构造 jstring 参数
|
||||||
|
jstring jResult = env->NewStringUTF("ok");
|
||||||
|
|
||||||
|
// 4. 调用 Java 回调
|
||||||
|
env->CallVoidMethod(g_callback, methodId, jResult);
|
||||||
|
|
||||||
|
// 5. 检查是否抛出异常
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
env->ExceptionDescribe();
|
||||||
|
env->ExceptionClear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 释放局部引用(可选,但推荐)
|
||||||
|
env->DeleteLocalRef(jResult);
|
||||||
|
env->DeleteLocalRef(callbackClass);
|
||||||
|
|
||||||
|
// 7. 如果是临时 attach 的线程,要 detach
|
||||||
|
if (needDetach) {
|
||||||
|
vm->DetachCurrentThread();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CppAnimationFinishedCallback()
|
||||||
|
{
|
||||||
|
JNIEnv* env = nullptr;
|
||||||
|
bool needDetach = false;
|
||||||
|
|
||||||
|
// 获取当前线程的 JNIEnv
|
||||||
|
JavaVM* vm = g_jvm; // 你需要提前保存 JavaVM* 到全局变量 g_jvm
|
||||||
|
int status = vm->GetEnv((void**)&env, JNI_VERSION_1_6);
|
||||||
|
if (status == JNI_EDETACHED) {
|
||||||
|
// 当前线程未 attach,需 attach
|
||||||
|
if (vm->AttachCurrentThread(&env, nullptr) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
needDetach = true;
|
||||||
|
} else if (status != JNI_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_callbackAnimationFinished == nullptr) {
|
||||||
|
if (needDetach) vm->DetachCurrentThread();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 获取 CallbackInterface 的 class
|
||||||
|
jclass callbackClass = env->GetObjectClass(g_callbackAnimationFinished);
|
||||||
|
if (callbackClass == nullptr) {
|
||||||
|
if (needDetach) vm->DetachCurrentThread();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取 OnLoadActionFinished 方法 ID(注意方法名大小写必须一致!)
|
||||||
|
jmethodID methodId = env->GetMethodID(callbackClass, "OnPlayMotionListFinished", "()V");
|
||||||
|
if (methodId == nullptr) {
|
||||||
|
env->ExceptionDescribe(); // 打印异常
|
||||||
|
env->ExceptionClear();
|
||||||
|
if (needDetach) vm->DetachCurrentThread();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 调用 Java 回调
|
||||||
|
env->CallVoidMethod(g_callbackAnimationFinished, methodId);
|
||||||
|
|
||||||
|
// 5. 检查是否抛出异常
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
env->ExceptionDescribe();
|
||||||
|
env->ExceptionClear();
|
||||||
|
}
|
||||||
|
|
||||||
|
env->DeleteLocalRef(callbackClass);
|
||||||
|
|
||||||
|
// 7. 如果是临时 attach 的线程,要 detach
|
||||||
|
if (needDetach) {
|
||||||
|
vm->DetachCurrentThread();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jstring JNICALL
|
||||||
|
Java_com_hmwl_face_1sdk_FaceActivity_PreReadAction(JNIEnv *env, jobject thiz, jstring motion,
|
||||||
|
jobject callback) {
|
||||||
|
// 清理旧的全局引用
|
||||||
|
if (g_callback != nullptr) {
|
||||||
|
env->DeleteGlobalRef(g_callback);
|
||||||
|
g_callback = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存新的回调
|
||||||
|
if (callback != nullptr) {
|
||||||
|
g_callback = env->NewGlobalRef(callback);
|
||||||
|
} else {
|
||||||
|
g_callback = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *nativeString = env->GetStringUTFChars(motion, nullptr);
|
||||||
|
jsize len = env->GetStringUTFLength(motion);
|
||||||
|
if(len > ArgLen)
|
||||||
|
{
|
||||||
|
aout << "ArgLen to long:" << len << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ret = g_Application->preLoadMotionList(nativeString, CppCallback);
|
||||||
|
return env->NewStringUTF(ret.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
const char DELIMITER = 0x1F; // same as \u001F
|
||||||
|
|
||||||
|
std::vector<std::string> splitMotionString(const std::string& input) {
|
||||||
|
std::vector<std::string> result;
|
||||||
|
if (input.empty()) return result;
|
||||||
|
|
||||||
|
size_t start = 0;
|
||||||
|
size_t pos = input.find(DELIMITER);
|
||||||
|
|
||||||
|
while (pos != std::string::npos) {
|
||||||
|
result.push_back(input.substr(start, pos - start));
|
||||||
|
start = pos + 1;
|
||||||
|
pos = input.find(DELIMITER, start);
|
||||||
|
}
|
||||||
|
// Add last part
|
||||||
|
result.push_back(input.substr(start));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_com_hmwl_face_1sdk_FaceActivity_ChangeMotionCpp(JNIEnv *env, jobject thiz, jstring json,
|
||||||
|
jobject callback, jboolean loop) {
|
||||||
|
// TODO: implement ChangeMotionCpp()
|
||||||
const char *nativeString = env->GetStringUTFChars(json, nullptr);
|
const char *nativeString = env->GetStringUTFChars(json, nullptr);
|
||||||
jsize len = env->GetStringUTFLength(json);
|
jsize len = env->GetStringUTFLength(json);
|
||||||
if(len > ArgLen)
|
if(len > ArgLen)
|
||||||
@@ -158,8 +345,28 @@ Java_com_hmwl_face_1sdk_FaceActivity_ChangeStateCpp(JNIEnv *env, jobject thiz, j
|
|||||||
aout << "ArgLen to long:" << len << std::endl;
|
aout << "ArgLen to long:" << len << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 保存新的回调
|
||||||
|
if (callback != nullptr) {
|
||||||
|
g_callbackAnimationFinished = env->NewGlobalRef(callback);
|
||||||
|
} else {
|
||||||
|
g_callbackAnimationFinished = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if(g_Application->isInited())
|
if(g_Application->isInited())
|
||||||
{
|
{
|
||||||
g_Application->changeMotion(nativeString);
|
std::vector<std::string> motions = splitMotionString(nativeString);
|
||||||
|
g_Application->changeMotionList(motions, CppAnimationFinishedCallback, loop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_com_hmwl_face_1sdk_FaceActivity_StopMotionNative(JNIEnv *env, jobject thiz) {
|
||||||
|
g_Application->StopMotion();
|
||||||
|
}
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_com_hmwl_face_1sdk_FaceActivity_ResumeMotionNative(JNIEnv *env, jobject thiz) {
|
||||||
|
// TODO: implement ResumeMotionNative()
|
||||||
|
g_Application->ResumeMotion();
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@ import android.Manifest;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -31,7 +30,6 @@ import java.util.List;
|
|||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class FaceActivity extends GameActivity implements FaceLandmarkerHelper.LandmarkerListener {
|
public class FaceActivity extends GameActivity implements FaceLandmarkerHelper.LandmarkerListener {
|
||||||
private static final String TAG = "FaceActivity";
|
private static final String TAG = "FaceActivity";
|
||||||
@@ -50,6 +48,21 @@ public class FaceActivity extends GameActivity implements FaceLandmarkerHelper.L
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
|
||||||
|
if (requestCode == REQUEST_CAMERA_PERMISSION) {
|
||||||
|
// 检查权限是否被授予
|
||||||
|
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
// 用户授予了权限,启动相机
|
||||||
|
startCamera();
|
||||||
|
} else {
|
||||||
|
requestCameraPermission();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void startCamera() {
|
private void startCamera() {
|
||||||
ListenableFuture<ProcessCameraProvider> cameraProviderFuture =
|
ListenableFuture<ProcessCameraProvider> cameraProviderFuture =
|
||||||
ProcessCameraProvider.getInstance(this);
|
ProcessCameraProvider.getInstance(this);
|
||||||
@@ -122,6 +135,22 @@ public class FaceActivity extends GameActivity implements FaceLandmarkerHelper.L
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Stop(){
|
||||||
|
StopRunning();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void StopMotionNative();
|
||||||
|
public void StopMotion(){
|
||||||
|
StopMotionNative();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void ResumeMotionNative();
|
||||||
|
public void ResumeMotion(){
|
||||||
|
ResumeMotionNative();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void StopRunning();
|
||||||
|
|
||||||
// Native 方法
|
// Native 方法
|
||||||
private native void processImageNative(ByteBuffer buffer, int width, int height,
|
private native void processImageNative(ByteBuffer buffer, int width, int height,
|
||||||
int format, int rowStride, int pixelStride,
|
int format, int rowStride, int pixelStride,
|
||||||
@@ -261,21 +290,45 @@ public class FaceActivity extends GameActivity implements FaceLandmarkerHelper.L
|
|||||||
|
|
||||||
protected native void SetCppInitArg(String json);
|
protected native void SetCppInitArg(String json);
|
||||||
|
|
||||||
protected void SetInitArg(String json)
|
public void SetInitArg(String json)
|
||||||
{
|
{
|
||||||
SetCppInitArg(json);
|
SetCppInitArg(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected native void ChangeStateCpp(String json);
|
public interface LoadMotionListFinishedCallback {
|
||||||
protected void ChangeState(String json){
|
void OnLoadMotionListFinished(String result);
|
||||||
Log.i("FaceActivity", "ChangeState:" + json);
|
}
|
||||||
ChangeStateCpp(json);
|
|
||||||
|
public interface PlayMotionListFinishedCallback {
|
||||||
|
void OnPlayMotionListFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected native String PreReadAction(String motion, LoadMotionListFinishedCallback callback);
|
||||||
|
public String PreLoadAction(String json, LoadMotionListFinishedCallback callback){
|
||||||
|
return PreReadAction(json, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected native void ChangeMotionCpp(String json, PlayMotionListFinishedCallback callback, boolean loop);
|
||||||
|
public void PlayMotionList(List<String> motionList, boolean loop, PlayMotionListFinishedCallback callback)
|
||||||
|
{
|
||||||
|
if(callback == null)
|
||||||
|
{
|
||||||
|
callback = new PlayMotionListFinishedCallback() {
|
||||||
|
@Override
|
||||||
|
public void OnPlayMotionListFinished() {
|
||||||
|
Log.i(TAG, "OnPlayMotionListFinished: ");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
final String DELIMITER = "\u001F"; // ASCII Unit Separator
|
||||||
|
String motion_list_str = String.join(DELIMITER, motionList);
|
||||||
|
ChangeMotionCpp(motion_list_str, callback, loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加这个缺失的方法
|
// 添加这个缺失的方法
|
||||||
@Override
|
@Override
|
||||||
public void onEmpty() {
|
public void onEmpty() {
|
||||||
// 当没有检测到人脸时的处理逻辑
|
// 当没有检测到人脸时的处理逻辑
|
||||||
Log.d(TAG, "No face detected in the current frame");
|
//Log.d(TAG, "No face detected in the current frame");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,6 +67,9 @@ class FaceLandmarkerHelper(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
minFaceDetectionConfidence = 0.1f;
|
||||||
|
minFaceTrackingConfidence = 0.1f;
|
||||||
|
minFacePresenceConfidence = 0.1f;
|
||||||
try {
|
try {
|
||||||
val baseOptions = baseOptionBuilder.build()
|
val baseOptions = baseOptionBuilder.build()
|
||||||
// Create an option builder with base options and specific
|
// Create an option builder with base options and specific
|
||||||
@@ -238,32 +241,33 @@ class FaceLandmarkerHelper(
|
|||||||
val frameId = result.timestampMs()
|
val frameId = result.timestampMs()
|
||||||
if( result.faceLandmarks().size > 0 ) {
|
if( result.faceLandmarks().size > 0 ) {
|
||||||
// 计算各种延时
|
// 计算各种延时
|
||||||
val timings = frameTimings[frameId]
|
// val timings = frameTimings[frameId]
|
||||||
if (timings != null) {
|
// if (timings != null) {
|
||||||
val totalLatency = resultTime - timings.captureTime
|
// val totalLatency = resultTime - timings.captureTime
|
||||||
val preprocessLatency = timings.preprocessEndTime - timings.preprocessStartTime
|
// val preprocessLatency = timings.preprocessEndTime - timings.preprocessStartTime
|
||||||
val detectionLatency = resultTime - timings.detectionStartTime
|
// val detectionLatency = resultTime - timings.detectionStartTime
|
||||||
val captureToDetectionLatency = timings.detectionStartTime - timings.captureTime
|
// val captureToDetectionLatency = timings.detectionStartTime - timings.captureTime
|
||||||
|
//
|
||||||
Log.i("TimeLatency",
|
// Log.i("TimeLatency",
|
||||||
"总延时: ${totalLatency}ms | " +
|
// "总延时: ${totalLatency}ms | " +
|
||||||
"预处理: ${preprocessLatency}ms | " +
|
// "预处理: ${preprocessLatency}ms | " +
|
||||||
"检测: ${detectionLatency}ms | " +
|
// "检测: ${detectionLatency}ms | " +
|
||||||
"捕获到检测: ${captureToDetectionLatency}ms | " +
|
// "捕获到检测: ${captureToDetectionLatency}ms | " +
|
||||||
"FPS: ${String.format("%.1f", currentFps)}"
|
// "FPS: ${String.format("%.1f", currentFps)}"
|
||||||
)
|
// )
|
||||||
|
//
|
||||||
val finishTimeMs = SystemClock.uptimeMillis()
|
// val finishTimeMs = SystemClock.uptimeMillis()
|
||||||
val inferenceTime = finishTimeMs - result.timestampMs()
|
// val inferenceTime = finishTimeMs - result.timestampMs()
|
||||||
faceLandmarkerHelperListener?.onResults(
|
// faceLandmarkerHelperListener?.onResults(
|
||||||
ResultBundle(
|
// ResultBundle(
|
||||||
result,
|
// result,
|
||||||
inferenceTime, // 主要是检测时间
|
// inferenceTime, // 主要是检测时间
|
||||||
input.height,
|
// input.height,
|
||||||
input.width,
|
// input.width,
|
||||||
)
|
// )
|
||||||
)
|
// )
|
||||||
} else {
|
// } else
|
||||||
|
//{
|
||||||
val finishTimeMs = SystemClock.uptimeMillis()
|
val finishTimeMs = SystemClock.uptimeMillis()
|
||||||
val inferenceTime = finishTimeMs - result.timestampMs()
|
val inferenceTime = finishTimeMs - result.timestampMs()
|
||||||
Log.i("TimeLatency",
|
Log.i("TimeLatency",
|
||||||
@@ -277,7 +281,7 @@ class FaceLandmarkerHelper(
|
|||||||
input.width
|
input.width
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
faceLandmarkerHelperListener?.onEmpty()
|
faceLandmarkerHelperListener?.onEmpty()
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.hmwl.face_sdk;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
public class Frame {
|
||||||
|
public String name;
|
||||||
|
public float x;
|
||||||
|
public float y;
|
||||||
|
public Frame(String name, float x, float y)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
public JSONObject toJsonObject(){
|
||||||
|
try {
|
||||||
|
JSONObject jsonObject = new JSONObject();
|
||||||
|
jsonObject.put("name", name);
|
||||||
|
jsonObject.put("x", x);
|
||||||
|
jsonObject.put("y", y);
|
||||||
|
return jsonObject;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,13 +4,12 @@ import org.json.JSONObject;
|
|||||||
public class InitArg {
|
public class InitArg {
|
||||||
public int action_fps;
|
public int action_fps;
|
||||||
public float zoom;
|
public float zoom;
|
||||||
public Motion motion;
|
|
||||||
public String toJson() {
|
public String toJson() {
|
||||||
try {
|
try {
|
||||||
JSONObject jsonObject = new JSONObject();
|
JSONObject jsonObject = new JSONObject();
|
||||||
jsonObject.put("action_fps", action_fps);
|
jsonObject.put("action_fps", action_fps);
|
||||||
jsonObject.put("zoom", zoom);
|
jsonObject.put("zoom", zoom);
|
||||||
jsonObject.put("motion", motion.toJsonObject());
|
|
||||||
return jsonObject.toString();
|
return jsonObject.toString();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
@@ -5,32 +5,29 @@ import org.json.JSONObject;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
public class Motion {
|
public class Motion {
|
||||||
public String type = "no";
|
public String name = "no";
|
||||||
public List<String> png_names = new ArrayList<>();
|
public List<Frame> frames = new ArrayList<>();
|
||||||
|
public Motion(String name, List<Frame> frames)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
this.frames = frames;
|
||||||
|
}
|
||||||
|
|
||||||
public JSONObject toJsonObject() {
|
public JSONObject toJsonObject() {
|
||||||
try {
|
try {
|
||||||
JSONObject jsonObject = new JSONObject();
|
JSONObject jsonObject = new JSONObject();
|
||||||
jsonObject.put("type", type);
|
jsonObject.put("name", name);
|
||||||
JSONArray ja = new JSONArray();
|
JSONArray jframes = new JSONArray();
|
||||||
for(int i = 0; i < png_names.size(); ++i){
|
for(int i = 0; i < frames.size(); ++i){
|
||||||
ja.put(png_names.get(i));
|
jframes.put(frames.get(i).toJsonObject());
|
||||||
}
|
}
|
||||||
jsonObject.put("png_names", ja);
|
jsonObject.put("frames", jframes);
|
||||||
return jsonObject;
|
return jsonObject;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toJson() {
|
|
||||||
try {
|
|
||||||
return toJsonObject().toString();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.hmwl.face_sdk;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MotionList {
|
||||||
|
public List<Motion> motions = new ArrayList<>();
|
||||||
|
public JSONObject toJsonObject() {
|
||||||
|
try {
|
||||||
|
JSONObject jsonObject = new JSONObject();
|
||||||
|
JSONArray jMotions = new JSONArray();
|
||||||
|
for(int i = 0; i < motions.size(); ++i){
|
||||||
|
jMotions.put(motions.get(i).toJsonObject());
|
||||||
|
}
|
||||||
|
jsonObject.put("motions", jMotions);
|
||||||
|
return jsonObject;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return new JSONObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public String toJsonString(){
|
||||||
|
return toJsonObject().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,19 @@
|
|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:8.3.2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application) apply false
|
alias(libs.plugins.android.application) apply false
|
||||||
alias(libs.plugins.android.library) apply false // 添加这行
|
alias(libs.plugins.android.library) apply false
|
||||||
id 'org.jetbrains.kotlin.android' version '1.9.0' apply false
|
alias(libs.plugins.kotlin.android) apply false // 添加这行
|
||||||
|
//alias(libs.plugins.kotlin.android) apply false
|
||||||
|
//id 'org.jetbrains.kotlin.android' version '2.2.0' apply false
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,9 @@ glslangValidator -V app/src/main/assets/shaders/bg.vert -o app/src/main/assets/s
|
|||||||
glslangValidator -V app/src/main/assets/shaders/texture.frag -o app/src/main/assets/shaders/texture.frag.spv
|
glslangValidator -V app/src/main/assets/shaders/texture.frag -o app/src/main/assets/shaders/texture.frag.spv
|
||||||
glslangValidator -V app/src/main/assets/shaders/texture.vert -o app/src/main/assets/shaders/texture.vert.spv
|
glslangValidator -V app/src/main/assets/shaders/texture.vert -o app/src/main/assets/shaders/texture.vert.spv
|
||||||
|
|
||||||
glslangValidator -V app/src/main/assets/shaders/texture_thick.frag -o app/src/main/assets/shaders/texture_thick.frag.spv
|
|
||||||
glslangValidator -V app/src/main/assets/shaders/texture_thick.vert -o app/src/main/assets/shaders/texture_thick.vert.spv
|
|
||||||
|
|
||||||
glslangValidator -V app/src/main/assets/shaders/simple_shader.frag -o app/src/main/assets/shaders/simple_shader.frag.spv
|
glslangValidator -V app/src/main/assets/shaders/simple_shader.frag -o app/src/main/assets/shaders/simple_shader.frag.spv
|
||||||
glslangValidator -V app/src/main/assets/shaders/simple_shader.vert -o app/src/main/assets/shaders/simple_shader.vert.spv
|
glslangValidator -V app/src/main/assets/shaders/simple_shader.vert -o app/src/main/assets/shaders/simple_shader.vert.spv
|
||||||
|
|
||||||
|
glslangValidator -V app/src/main/assets/shaders/texture_thick.frag -o app/src/main/assets/shaders/texture_thick.frag.spv
|
||||||
|
rem glslangValidator -V app/src/main/assets/shaders/texture_thick.vert -o app/src/main/assets/shaders/texture_thick.vert.spv
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application)
|
alias(libs.plugins.android.application)
|
||||||
|
alias(libs.plugins.kotlin.android)
|
||||||
|
//alias(libs.plugins.kotlin.android)
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace 'com.hmwl.example'
|
namespace 'com.inewme.uvmirror'
|
||||||
compileSdk 36
|
compileSdk 36
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.hmwl.f20251110"
|
applicationId "com.inewme.uvmirror.f20260420"
|
||||||
minSdk 30
|
minSdk 30
|
||||||
targetSdk 36
|
targetSdk 36
|
||||||
versionCode 1
|
versionCode 1
|
||||||
@@ -18,6 +20,7 @@ android {
|
|||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
|
signingConfig signingConfigs.debug
|
||||||
minifyEnabled false
|
minifyEnabled false
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
@@ -26,6 +29,20 @@ android {
|
|||||||
sourceCompatibility JavaVersion.VERSION_17
|
sourceCompatibility JavaVersion.VERSION_17
|
||||||
targetCompatibility JavaVersion.VERSION_17
|
targetCompatibility JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
compose true
|
||||||
|
}
|
||||||
|
|
||||||
|
composeOptions {
|
||||||
|
kotlinCompilerExtensionVersion '1.5.1'
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = '17'
|
||||||
|
}
|
||||||
|
// kotlinOptions {
|
||||||
|
// jvmTarget = '17'
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -33,11 +50,21 @@ dependencies {
|
|||||||
implementation libs.appcompat
|
implementation libs.appcompat
|
||||||
implementation libs.material
|
implementation libs.material
|
||||||
implementation libs.games.activity
|
implementation libs.games.activity
|
||||||
|
implementation libs.core.ktx
|
||||||
|
implementation libs.androidx.core.ktx
|
||||||
testImplementation libs.junit
|
testImplementation libs.junit
|
||||||
androidTestImplementation libs.ext.junit
|
androidTestImplementation libs.ext.junit
|
||||||
androidTestImplementation libs.espresso.core
|
androidTestImplementation libs.espresso.core
|
||||||
|
|
||||||
implementation project(':app')
|
implementation project(':app')
|
||||||
|
// Jetpack Compose
|
||||||
|
implementation "androidx.compose.ui:ui:1.5.0"
|
||||||
|
implementation "androidx.compose.material3:material3:1.0.0"
|
||||||
|
implementation "androidx.compose.foundation:foundation:1.5.0"
|
||||||
|
implementation "androidx.compose.runtime:runtime:1.5.0"
|
||||||
|
implementation "androidx.activity:activity-compose:1.7.2" // �ؼ����ṩ setContent
|
||||||
|
|
||||||
|
implementation "androidx.compose.ui:ui-tooling:1.5.4"
|
||||||
|
|
||||||
def camerax_version = '1.4.2'
|
def camerax_version = '1.4.2'
|
||||||
implementation "androidx.camera:camera-core:$camerax_version"
|
implementation "androidx.camera:camera-core:$camerax_version"
|
||||||
@@ -53,4 +80,7 @@ dependencies {
|
|||||||
implementation 'com.google.flogger:flogger-system-backend:0.7.4'
|
implementation 'com.google.flogger:flogger-system-backend:0.7.4'
|
||||||
|
|
||||||
implementation 'com.google.guava:guava:31.1-android'
|
implementation 'com.google.guava:guava:31.1-android'
|
||||||
|
|
||||||
|
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") // 提供 lifecycleScope
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0")
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,10 @@
|
|||||||
android:name="android.app.lib_name"
|
android:name="android.app.lib_name"
|
||||||
android:value="face_sdk" />
|
android:value="face_sdk" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".MakeupActivity"
|
||||||
|
android:exported="false" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.2 KiB |