From d3a9cd0f262a9690d433ab8cb80714b794ea0f58 Mon Sep 17 00:00:00 2001
From: SuperMonster003
Date: Wed, 14 May 2025 23:59:13 +0800
Subject: [PATCH] =?UTF-8?q?6.6.3=20-=20Alpha4=20-=20=E6=96=B0=E5=A2=9E=20i?=
=?UTF-8?q?mages.matchFeatures/detectAndComputeFeatures=20=E6=96=B9?=
=?UTF-8?q?=E6=B3=95=20(issue=20#366)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.changelog/lang_zh-Hans.json | 5 +-
.readme/README-ar.md | 2 +-
.readme/README-en.md | 2 +-
.readme/README-es.md | 2 +-
.readme/README-fr.md | 2 +-
.readme/README-ja.md | 24 +-
.readme/README-ko.md | 28 +-
.readme/README-ru.md | 2 +-
.readme/README-zh-Hans.md | 7 +-
.readme/README-zh-Hant-HK.md | 2 +-
.readme/README-zh-Hant-TW.md | 2 +-
.readme/lang_ja.json | 22 +-
.readme/lang_ko.json | 28 +-
README.md | 7 +-
app/build.gradle.kts | 8 +-
.../main/assets-app/doc/CHANGELOG-zh-Hans.md | 5 +-
app/src/main/assets-app/doc/CHANGELOG.md | 5 +-
.../autojs/autojs/core/automator/UiObject.kt | 98 +++--
.../runtime/api/ImageFeatureMatching.java | 355 ++++++++++++++----
.../autojs/runtime/api/augment/Augmentable.kt | 7 +-
.../runtime/api/augment/images/Images.kt | 64 ++--
.../runtime/api/augment/images/ObjectFrame.kt | 14 +
.../java/org/autojs/autojs/util/ImageUtils.kt | 46 ++-
.../org/autojs/autojs/util/StringUtils.kt | 26 ++
version.properties | 8 +-
25 files changed, 535 insertions(+), 236 deletions(-)
diff --git a/.changelog/lang_zh-Hans.json b/.changelog/lang_zh-Hans.json
index 001814d3..514be08c 100644
--- a/.changelog/lang_zh-Hans.json
+++ b/.changelog/lang_zh-Hans.json
@@ -1,12 +1,13 @@
{
"$data": {
"v6.6.3": {
- "released_date": "2025/05/13",
+ "released_date": "2025/05/14",
"feature": [
"版本历史功能, 可查看发行版本历史更新记录 (多语言) 与统计数据",
"timers.keepAlive 方法 (已全局化), 用于保持脚本活跃状态",
"engines.on('start/stop/error', callback) 等事件监听方法, 用于监听脚本引擎全局事件",
- "images.detectMultiColors 方法, 用于多点颜色校验 _[`issue #374`](http://issues.autojs6.com/374)_"
+ "images.detectMultiColors 方法, 用于多点颜色校验 _[`issue #374`](http://issues.autojs6.com/374)_",
+ "images.matchFeatures/detectAndComputeFeatures 方法, 支持全分辨率找图 (Ref to [Auto.js Pro](https://g.pro.autojs.org/)) _[`issue #366`](http://issues.autojs6.com/366)_"
],
"fix": [
"主页文档标签显示在线文档时部分内容被系统导航栏遮挡的问题",
diff --git a/.readme/README-ar.md b/.readme/README-ar.md
index 5e9d096a..e8d045cd 100644
--- a/.readme/README-ar.md
+++ b/.readme/README-ar.md
@@ -389,7 +389,7 @@ SDK Tools (أدوات SDK) (في النافذة اليمنى)
يعتمد مشروع AutoJs6 على إصدار `JDK (مجموعة تطوير جافا)` بإصدار لا يقل عن `17`، ولكن يفضل الإصدار الذي لا يقل عن `19`.
-اعتبارًا من 13 May 2025، الإصدار الأقصى المدعوم من JDK لمشروع AutoJs6 هو `24`.
+اعتبارًا من 14 May 2025، الإصدار الأقصى المدعوم من JDK لمشروع AutoJs6 هو `24`.
> ملاحظة: إذا كان نظام الكمبيوتر يحتوي على JDK والإصدار يفي بالمتطلبات المذكورة أعلاه، فيمكنك تخطي هذا القسم.
diff --git a/.readme/README-en.md b/.readme/README-en.md
index ee825b98..c58bcd68 100644
--- a/.readme/README-en.md
+++ b/.readme/README-en.md
@@ -389,7 +389,7 @@ Check `Show Package Details`, click NDK and CMake respectively to ensure the cor
The `JDK (Java Development Kit)` version required for the AutoJs6 project should be at least `17`, but `19` or higher is recommended.
-As of May 13, 2025, AutoJs6 supports up to version `24` of the JDK.
+As of May 14, 2025, AutoJs6 supports up to version `24` of the JDK.
> Note: If the JDK is already installed on the computer system and the version meets the above requirements, this section can be skipped.
diff --git a/.readme/README-es.md b/.readme/README-es.md
index 6562bdcd..f8aec382 100644
--- a/.readme/README-es.md
+++ b/.readme/README-es.md
@@ -389,7 +389,7 @@ Marca `Show Package Details (Mostrar detalles del paquete)`, haz clic en NDK y C
La versión mínima requerida del `JDK (Kit de Desarrollo de Java)` para el proyecto AutoJs6 es `17`, pero se recomienda usar una versión no inferior a `19`.
-Hasta el 13 de May de 2025, AutoJs6 soporta hasta la versión `24` de JDK.
+Hasta el 14 de May de 2025, AutoJs6 soporta hasta la versión `24` de JDK.
> Nota: Si el sistema ya tiene instalado JDK y cumple con los requisitos anteriores, puedes omitir esta sección.
diff --git a/.readme/README-fr.md b/.readme/README-fr.md
index c8b6b314..13645cbe 100644
--- a/.readme/README-fr.md
+++ b/.readme/README-fr.md
@@ -389,7 +389,7 @@ Cochez `Show Package Details (afficher les détails du package)`, puis cliquez r
La version de JDK (Kit de développement Java) requise pour le projet AutoJs6 doit être au moins `17`, mais il est recommandé d'avoir au moins `19`.
-À partir du 13 May 2025, AutoJs6 prend en charge la version maximale de JDK `24`.
+À partir du 14 May 2025, AutoJs6 prend en charge la version maximale de JDK `24`.
> Note : Si le JDK est déjà installé sur le système informatique et que la version répond aux exigences ci-dessus, vous pouvez ignorer cette section.
diff --git a/.readme/README-ja.md b/.readme/README-ja.md
index 299ba000..4ef5dab0 100644
--- a/.readme/README-ja.md
+++ b/.readme/README-ja.md
@@ -353,7 +353,7 @@ AutoJs6 オープンソース プロジェクトのデバッグや開発が必
> 注: コンピュータシステムに既にAndroid SDK (Androidソフトウェア開発キット)がインストールされている場合, この節の内容をスキップできます.
-Android Studioソフトウェアで`CTRL + ALT + S`のショートカットキーを使用して設定ページを開く:
+Android Studioソフトウェアで `CTRL + ALT + S` のショートカットキーを使用して設定ページを開く:
```text
Appearance & Behavior (外観と動作) ->
@@ -365,7 +365,7 @@ Android SDK (Android ソフトウェア開発キット)
> 注: プロセス中に1つまたは複数の関連契約に同意する必要がある場合があります.
-関連するリソースのダウンロードとインストールが完了したら, `完了` ボタンをクリックします.
+関連するリソースのダウンロードとインストールが完了したら, `完了` ボタンをクリックします.
上記の `Android SDK場所` の欄が自動的に入力され, SDKの準備が完了します.
#### Android SDKツールの準備
@@ -374,7 +374,7 @@ AutoJs6では一部のSDKツール(NDKやCMakeなど)が必要です.
> 注: コンピュータシステムにAutoJs6ですべて必要なAndroid SDKツールがインストールされている場合, この節の内容をスキップできます.
-Android Studioソフトウェアで`CTRL + ALT + S`のショートカットキーを使用して設定ページを開く:
+Android Studioソフトウェアで `CTRL + ALT + S` のショートカットキーを使用して設定ページを開く:
```text
Appearance & Behavior (外観と動作) ->
@@ -383,13 +383,13 @@ Android SDK (Android ソフトウェア開発キット) ->
SDK Tools (SDK ツール) (右側のウィンドウにあります)
```
-`パッケージの詳細を表示`を選択し, NDKおよびCMakeをそれぞれクリックして, 対応するバージョンのツールが選択されていることを確認してください, SDKツールのバージョン情報は, AutoJs6プロジェクトのルートディレクトリにある`version.properties`ファイルに記載されています.
+`パッケージの詳細を表示` を選択し, NDKおよびCMakeをそれぞれクリックして, 対応するバージョンのツールが選択されていることを確認してください, SDKツールのバージョン情報は, AutoJs6プロジェクトのルートディレクトリにある `version.properties` ファイルに記載されています.
#### JDKの準備
AutoJs6プロジェクトが依存する `JDK (Java開発キット)` のリリースバージョンは少なくとも `17` で, それ以下のバージョンは推奨されませんが, 最低でも `19` の使用を推奨します.
-2025 年 5 月 13 日 現在, AutoJs6がサポートする最大のJDKバージョンは `24` です.
+2025 年 5 月 14 日 現在, AutoJs6がサポートする最大のJDKバージョンは `24` です.
> 注: コンピュータシステムに適切なバージョンのJDKがインストールされている場合, この節の内容をスキップできます.
@@ -427,23 +427,23 @@ Android Studio のメインページで `Get from VCS (バージョン管理シ
ビルドが自動的に進行しなかった場合, またはプロジェクトを再ビルドする必要がある場合は, 次の手順に従ってください:
- ツールバーの `Run/Debug Configurations` メニューから `app` を選択します. これは AutoJs6 のメインアプリです
-- `Build Project`(ハンマーアイコン)のボタンを左側でクリックして, プロジェクトのビルドを開始します. デフォルトのショートカットは CTRL + F9 です
+- `Build Project` (ハンマーアイコン) のボタンを左側でクリックして, プロジェクトのビルドを開始します. デフォルトのショートカットは CTRL + F9 です
- ビルドが完了するのを待ちます. `Builder Output` タブに "BUILD SUCCESSFUL in 1h 17m 34s" のようなメッセージが表示されます
ビルドに失敗した場合は, エラーメッセージを AutoJs6 プロジェクトの [Issues](http://issues.autojs6.com) ページに報告できます.
> 注: アプリのビルドおよび実行方法の詳細については, [Android Docs](https://developer.android.com/studio/run?hl=en) を参照してください
-#### AutoJs6 プロジェクトのデプロイ(アプリの実行)
+#### AutoJs6 プロジェクトのデプロイ (アプリの実行)
-プロジェクトが正常にビルドされた後, 実機(例: スマートフォン/タブレット)や仮想デバイス(例: Android エミュレーター)にアプリとしてデプロイできます.
+プロジェクトが正常にビルドされた後, 実機 (例: スマートフォン/タブレット) や仮想デバイス (例: Android エミュレーター) にアプリとしてデプロイできます.
- ツールバーで `Run/Debug Configurations` メニューから `app` を選択します. これが AutoJs6 のメインアプリです
-- 右側の `Available Devices` ドロップダウンから, プロジェクトアプリを実行するデバイス(実機または仮想デバイス)を選択します
-- 右側の `Run`(三角形アイコン)のボタンをクリックして, アプリを起動します. デフォルトのショートカットは `F10` です
+- 右側の `Available Devices` ドロップダウンから, プロジェクトアプリを実行するデバイス (実機または仮想デバイス) を選択します
+- 右側の `Run` (三角形アイコン) のボタンをクリックして, アプリを起動します. デフォルトのショートカットは `F10` です
- デプロイが完了するのを待ちます. 選択したデバイスが自動的にアプリをインストールして実行します
-#### AutoJs6 プロジェクトのデプロイ(APKの生成)
+#### AutoJs6 プロジェクトのデプロイ (APKの生成)
プロジェクトをパッケージして, Android デバイスにインストール可能な APK ファイルを生成します:
@@ -483,7 +483,7 @@ AutoJs6 プロジェクトにはさまざまなビルドバリアントがあり
デバッグビルドプロセスの成果物をパッケージします. これは主に [AutoJs6 型定義](http://dts-project.autojs6.com) プロジェクトに使用されます.
-タスク実行後, AutoJs6 プロジェクトの最新の JAR ファイルが生成されます. [Android d.ts Generator](https://github.com/NativeScript/android-dts-generator)([NativeScript](https://github.com/NativeScript) 作)のツールを使用して, AutoJs6 メインアプリの型定義(d.ts 形式のファイル)を生成できます.
+タスク実行後, AutoJs6 プロジェクトの最新の JAR ファイルが生成されます. [Android d.ts Generator](https://github.com/NativeScript/android-dts-generator) ([NativeScript](https://github.com/NativeScript) 作) のツールを使用して, AutoJs6 メインアプリの型定義 (d.ts 形式のファイル) を生成できます.
- AutoJs6 [app:digest]
diff --git a/.readme/README-ko.md b/.readme/README-ko.md
index 10d03369..3fb0d3b0 100644
--- a/.readme/README-ko.md
+++ b/.readme/README-ko.md
@@ -48,9 +48,9 @@
[Auto.js](https://github.com/hyb1996/Auto.js) Android 플랫폼에서 [접근성 서비스](https://developer.android.com/guide/topics/ui/accessibility/service?hl=zh-cn)를 지원하는 JavaScript 자동화 도구 소프트웨어입니다.
-Auto.js는 [hyb1996](https://github.com/hyb1996)에 의해 `2017/01/27` 처음 출시되었으며, `2020/03/13` 에 유지보수가 중단되었고, 최종 버전 이름은 `4.1.1 Alpha2`, 빌드 버전 번호는 `461`입니다.
+Auto.js는 [hyb1996](https://github.com/hyb1996)에 의해 `2017/01/27` 처음 출시되었으며, `2020/03/13` 에 유지보수가 중단되었고, 최종 버전 이름은 `4.1.1 Alpha2`, 빌드 버전 번호는 `461` 입니다.
-AutoJs6는 Auto.js 최종 프로젝트를 기반으로 `2021/12/01`에 다시 개발되어 계속해서 오픈 소스로 무료로 유지됩니다.
+AutoJs6는 Auto.js 최종 프로젝트를 기반으로 `2021/12/01` 에 다시 개발되어 계속해서 오픈 소스로 무료로 유지됩니다.
다음 표는 일부 Auto.js 관련 프로젝트를 나열한 것입니다 (개발 날짜 순으로 정렬됨):
@@ -116,7 +116,7 @@ AutoJs6는 Auto.js 최종 프로젝트를 기반으로 `2021/12/01`에 다시
******
-Auto.js 최종 오픈 소스 버전 `4.1.1 Alpha2`와 비교하여 AutoJs6는 주요한 다음과 같은 업그레이드 및 변동사항이 있습니다:
+Auto.js 최종 오픈 소스 버전 `4.1.1 Alpha2` 와 비교하여 AutoJs6는 주요한 다음과 같은 업그레이드 및 변동사항이 있습니다:
* [Shizuku](https://shizuku.rikka.app/introduction/)를 통해 ADB 특권을 얻고 시스템 API 사용 지원
* [WebSocket](https://docs.autojs6.com/#/webSocketType) 인스턴스를 생성하여 [WebSocket 프로토콜](https://zh.wikipedia.org/wiki/WebSocket)에 기반한 네트워크 요청 지원
@@ -361,12 +361,12 @@ System Settings (시스템 설정) ->
Android SDK (안드로이드 소프트웨어 개발 키트)
```
-`Android SDK Location`(안드로이드 소프트웨어 개발 키트 위치) 필드가 비어 있는 경우, 오른쪽의 `Edit`(편집) 버튼을 클릭하고, 팝업 창에서 여러 번 `Next`(다음)를 클릭합니다.
+`Android SDK Location` (안드로이드 소프트웨어 개발 키트 위치) 필드가 비어 있는 경우, 오른쪽의 `Edit` (편집) 버튼을 클릭하고, 팝업 창에서 여러 번 `Next` (다음)를 클릭합니다.
> 메모: 과정 중 하나 이상의 관련 계약을 동의해야 계속 진행할 수 있습니다.
-관련 리소스가 다운로드 및 설치를 완료하면, `Finish`(완료) 버튼을 클릭합니다.
-위의 `Android SDK Location`(안드로이드 소프트웨어 개발 키트 위치) 필드에 경로가 자동으로 작성되고, SDK 준비 작업이 완료됩니다.
+관련 리소스가 다운로드 및 설치를 완료하면, `Finish` (완료) 버튼을 클릭합니다.
+위의 `Android SDK Location` (안드로이드 소프트웨어 개발 키트 위치) 필드에 경로가 자동으로 작성되고, SDK 준비 작업이 완료됩니다.
#### Android SDK 도구 준비
@@ -389,7 +389,7 @@ SDK Tools (SDK 도구) (오른쪽 창에 위치)
AutoJs6 프로젝트는 `JDK (Java 개발 도구 키트)` 버전이 `17` 이상이어야 하지만, `19` 이상을 권장합니다.
-2025 년 5 월 13 일 기준으로, AutoJs6이 지원하는 최대 JDK 버전은 `24`입니다.
+2025 년 5 월 14 일 기준으로, AutoJs6이 지원하는 최대 JDK 버전은 `24` 입니다.
> 메모: 시스템에 JDK가 설치되어 있고, 버전이 위의 요구 사항을 충족하는 경우, 이 섹션을 건너뛸 수 있습니다.
@@ -406,14 +406,14 @@ Gradle
`Gradle JDK` 에서 다양한 버전의 JDK를 선택하거나 추가할 수 있습니다.
목록에 적합한 버전의 JDK(>= `17`)가 있는 경우, 직접 선택하십시오.
-그렇지 않으면 `Download JDK (JDK 다운로드)`를 선택하여 적절한 JDK를 다운로드하고, `Download (다운로드)` 버튼을 클릭하여 완료될 때까지 기다립니다.
-`Add JDK (JDK 추가)`를 선택하여 이미 존재하는 로컬 JDK를 추가하고 디렉토리를 지정하여 JDK 추가를 완료할 수 있습니다.
+그렇지 않으면 `Download JDK (JDK 다운로드)` 를 선택하여 적절한 JDK를 다운로드하고, `Download (다운로드)` 버튼을 클릭하여 완료될 때까지 기다립니다.
+`Add JDK (JDK 추가)` 를 선택하여 이미 존재하는 로컬 JDK를 추가하고 디렉토리를 지정하여 JDK 추가를 완료할 수 있습니다.
#### AutoJs6 자원 복제
Android Studio 메인 화면에서 `Get from VCS (버전 관리 시스템에서 가져오기)` 버튼을 클릭합니다.
-`URL (통합 자원 위치)`에 `https://github.com/SuperMonster003/AutoJs6.git`을 입력합니다,
-`Directory (디렉토리)`에서 필요한 경우 특정 경로로 수정할 수 있습니다.
+`URL (통합 자원 위치)` 에 `https://github.com/SuperMonster003/AutoJs6.git` 을 입력합니다,
+`Directory (디렉토리)` 에서 필요한 경우 특정 경로로 수정할 수 있습니다.
`Clone (복제)` 버튼을 클릭하고 AutoJs6 프로젝트 리소스를 장치에 로컬로 복제될 때까지 기다립니다.
> 메모: 과정 중 [Git (분산 버전 관리 시스템)](https://git-scm.com/download)을 설치해야 할 수도 있습니다.
@@ -426,7 +426,7 @@ Android Studio 메인 화면에서 `Get from VCS (버전 관리 시스템에서
빌드가 자동으로 진행되지 않거나 프로젝트를 다시 빌드해야 하는 경우, 다음 단계를 따르십시오:
-- 툴바의 `Run/Debug Configurations` 메뉴에서 `app`을 선택합니다. 이는 AutoJs6의 메인 앱입니다
+- 툴바의 `Run/Debug Configurations` 메뉴에서 `app` 을 선택합니다. 이는 AutoJs6의 메인 앱입니다
- 좌측의 `Build Project` (망치 아이콘) 버튼을 클릭하여 프로젝트 빌드를 시작합니다. 기본 단축키는 CTRL + F9입니다
- 빌드가 완료될 때까지 기다립니다. `Builder Output` 탭에 "BUILD SUCCESSFUL in 1h 17m 34s"와 같은 메시지가 표시됩니다
@@ -438,9 +438,9 @@ Android Studio 메인 화면에서 `Get from VCS (버전 관리 시스템에서
프로젝트가 성공적으로 빌드된 후, 이를 실제 디바이스(예: 휴대폰/태블릿)나 가상 디바이스(예: Android 에뮬레이터)에 앱으로 배포할 수 있습니다.
-- 툴바에서 `Run/Debug Configurations` 메뉴의 `app`을 선택합니다. 이는 AutoJs6의 메인 앱입니다
+- 툴바에서 `Run/Debug Configurations` 메뉴의 `app` 을 선택합니다. 이는 AutoJs6의 메인 앱입니다
- 우측의 `Available Devices` 드롭다운 목록에서 프로젝트 앱을 실행할 디바이스(실제 또는 가상)를 선택합니다
-- 우측의 `Run` (삼각형 아이콘) 버튼을 클릭하여 앱을 실행합니다. 기본 단축키는 `F10`입니다
+- 우측의 `Run` (삼각형 아이콘) 버튼을 클릭하여 앱을 실행합니다. 기본 단축키는 `F10` 입니다
- 배포가 완료될 때까지 기다리면 선택된 디바이스가 자동으로 앱을 설치하고 실행합니다
#### AutoJs6 프로젝트 배포 (APK 생성)
diff --git a/.readme/README-ru.md b/.readme/README-ru.md
index cb36aecd..3e28eb65 100644
--- a/.readme/README-ru.md
+++ b/.readme/README-ru.md
@@ -389,7 +389,7 @@ SDK Tools (SDK Инструменты) (находится в правом ок
Для проекта AutoJs6 требуется версия `JDK (Java Development Kit)` не ниже `17`, но рекомендуется не ниже `19`.
-По состоянию на 13 May 2025 года AutoJs6 поддерживает JDK максимальной версии `24`.
+По состоянию на 14 May 2025 года AutoJs6 поддерживает JDK максимальной версии `24`.
> Примечание: Если на компьютере уже установлен JDK и версия соответствует указанным требованиям, этот раздел можно пропустить.
diff --git a/.readme/README-zh-Hans.md b/.readme/README-zh-Hans.md
index fca7fe0a..8b741637 100644
--- a/.readme/README-zh-Hans.md
+++ b/.readme/README-zh-Hans.md
@@ -157,12 +157,13 @@ AutoJs6 在 Auto.js 最终项目的基础上, 于 `2021/12/01` 进行二次开
# v6.6.3
-###### 2025/05/13
+###### 2025/05/14
* `新增` 版本历史功能, 可查看发行版本历史更新记录 (多语言) 与统计数据
* `新增` timers.keepAlive 方法 (已全局化), 用于保持脚本活跃状态
* `新增` engines.on('start/stop/error', callback) 等事件监听方法, 用于监听脚本引擎全局事件
* `新增` images.detectMultiColors 方法, 用于多点颜色校验 _[`issue #374`](http://issues.autojs6.com/374)_
+* `新增` images.matchFeatures/detectAndComputeFeatures 方法, 支持全分辨率找图 (Ref to [Auto.js Pro](https://g.pro.autojs.org/)) _[`issue #366`](http://issues.autojs6.com/366)_
* `修复` 主页文档标签显示在线文档时部分内容被系统导航栏遮挡的问题
* `修复` 部分设备代码编辑器空行显示方框字符的问题
* `修复` 主题色设置页面调色盘对话框可能无限叠加的问题
@@ -176,7 +177,7 @@ AutoJs6 在 Auto.js 最终项目的基础上, 于 `2021/12/01` 进行二次开
* `修复` console.setContentBackgroundColor 方法无法接受颜色名称参数的问题 _[`issue #384`](http://issues.autojs6.com/384)_
* `修复` README.md 中部分语言日期格式不正确的问题
* `优化` 布局分析支持控件隐藏 (by [TonyJiangWJ](https://github.com/TonyJiangWJ)) _[`pr #371`](http://pr.autojs6.com/371)_ _[`issue #355`](http://issues.autojs6.com/355)_
-* `优化` 布局分析菜单使用渐变分隔线实现一定程度的功能分组
+* `优化` 布局分析菜单添加渐变分隔线实现一定程度的功能分组
* `优化` 主题色扩充适配范围并支持更多控件类型
* `优化` 主页抽屉在横向屏幕或超宽屏幕的宽度适应性
* `优化` 关于应用与开发者页面增加水平布局及小屏布局适配
@@ -333,7 +334,7 @@ SDK Tools (SDK 工具) (位于右侧窗口)
AutoJs6 项目依赖的 `JDK (Java 开发工具包)` 发行版本不低于 `17`, 但建议不低于 `19`.
-截至 2025 年 5 月 13 日, AutoJs6 可支持 JDK 最高版本为 `24`.
+截至 2025 年 5 月 14 日, AutoJs6 可支持 JDK 最高版本为 `24`.
> 注: 如果计算机系统已安装 JDK 且版本满足上述要求, 则可跳过此小节内容.
diff --git a/.readme/README-zh-Hant-HK.md b/.readme/README-zh-Hant-HK.md
index ac7de974..50bfa500 100644
--- a/.readme/README-zh-Hant-HK.md
+++ b/.readme/README-zh-Hant-HK.md
@@ -389,7 +389,7 @@ SDK Tools (SDK 工具) (位於右側窗口)
AutoJs6 項目依賴的 `JDK (Java 開發工具包)` 發行版本不低於 `17`, 但建議不低於 `19`.
-截至 2025 年 5 月 13 日, AutoJs6 可支持 JDK 最高版本為 `24`.
+截至 2025 年 5 月 14 日, AutoJs6 可支持 JDK 最高版本為 `24`.
> 注: 如果計算機系統已安裝 JDK 且版本滿足上述要求, 則可跳過此小節內容.
diff --git a/.readme/README-zh-Hant-TW.md b/.readme/README-zh-Hant-TW.md
index 49a8a05e..bc405b91 100644
--- a/.readme/README-zh-Hant-TW.md
+++ b/.readme/README-zh-Hant-TW.md
@@ -389,7 +389,7 @@ SDK Tools (SDK 工具) (位於右側視窗)
AutoJs6 專案依賴的 `JDK (Java 開發工具包)` 發行版本不低於 `17`, 但建議不低於 `19`.
-截至 2025 年 5 月 13 日, AutoJs6 可支援 JDK 最高版本為 `24`.
+截至 2025 年 5 月 14 日, AutoJs6 可支援 JDK 最高版本為 `24`.
> 注: 如果計算機系統已安裝 JDK 且版本滿足上述要求, 則可跳過此小節內容.
diff --git a/.readme/lang_ja.json b/.readme/lang_ja.json
index 9626f55c..bf8887dd 100644
--- a/.readme/lang_ja.json
+++ b/.readme/lang_ja.json
@@ -25,16 +25,16 @@
"h4_android_studio_preparation": "Android Studioの準備",
"h4_autojs6_gradle_tasks": "AutoJs6 用の定義済み Gradle ビルドタスク",
"h4_autojs6_project_building": "AutoJs6プロジェクトのビルド",
- "h4_autojs6_project_deploy_apk": "AutoJs6 プロジェクトのデプロイ(APKの生成)",
- "h4_autojs6_project_deploy_run": "AutoJs6 プロジェクトのデプロイ(アプリの実行)",
+ "h4_autojs6_project_deploy_apk": "AutoJs6 プロジェクトのデプロイ (APKの生成)",
+ "h4_autojs6_project_deploy_run": "AutoJs6 プロジェクトのデプロイ (アプリの実行)",
"h4_autojs6_resources_cloning": "AutoJs6リソースのクローン",
"h4_jdk_preparation": "JDKの準備",
"h5_for_more_version_history": "他のバージョン履歴については以下を参照してください",
"label_after": "後",
"label_before": "前",
- "li_deploy_run_step_click_run": "右側の `Run`(三角形アイコン)のボタンをクリックして, アプリを起動します. デフォルトのショートカットは `F10` です",
+ "li_deploy_run_step_click_run": "右側の `Run` (三角形アイコン) のボタンをクリックして, アプリを起動します. デフォルトのショートカットは `F10` です",
"li_deploy_run_step_select_app": "ツールバーで `Run/Debug Configurations` メニューから `app` を選択します. これが AutoJs6 のメインアプリです",
- "li_deploy_run_step_select_device": "右側の `Available Devices` ドロップダウンから, プロジェクトアプリを実行するデバイス(実機または仮想デバイス)を選択します",
+ "li_deploy_run_step_select_device": "右側の `Available Devices` ドロップダウンから, プロジェクトアプリを実行するデバイス (実機または仮想デバイス) を選択します",
"li_deploy_run_step_wait_install": "デプロイが完了するのを待ちます. 選択したデバイスが自動的にアプリをインストールして実行します",
"li_development_assistance_autojs6_api_reference_docs": "AutoJs6に関するAPIおよび使用方法については, 常にアプリケーションドキュメントを参照できます",
"li_development_assistance_code_completion_assist_development": "開発ツールを使用してコードを記述する際, コードのインテリセンス機能が開発者のコード記述を支援します",
@@ -74,7 +74,7 @@
"li_manual_documentation": "アプリケーションドキュメント",
"li_manual_troubleshooting": "トラブルシューティング",
"li_manual_user_manual": "ユーザーマニュアル (作成中)",
- "li_project_build_step_click_build_project": "`Build Project`(ハンマーアイコン)のボタンを左側でクリックして, プロジェクトのビルドを開始します. デフォルトのショートカットは CTRL + F9 です",
+ "li_project_build_step_click_build_project": "`Build Project` (ハンマーアイコン) のボタンを左側でクリックして, プロジェクトのビルドを開始します. デフォルトのショートカットは CTRL + F9 です",
"li_project_build_step_toolbar": "ツールバーの `Run/Debug Configurations` メニューから `app` を選択します. これは AutoJs6 のメインアプリです",
"li_project_build_step_wait_build_success": "ビルドが完了するのを待ちます. `Builder Output` タブに \"BUILD SUCCESSFUL in 1h 17m 34s\" のようなメッセージが表示されます",
"li_rhino_feature_array_includes_support": "[Array.prototype.includes()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/includes)のサポート",
@@ -86,11 +86,11 @@
"li_rhino_feature_unicode_code_point_escapes_support": "Unicode [コードポイント](https://developer.mozilla.org/ja/docs/Glossary/Code_point)エスケープのサポート [補助平面](https://ja.wikipedia.org/wiki/多言語面#補助平面)文字",
"p_android_sdk_preparation_auto_sdk_location_filled": "上記の `Android SDK場所` の欄が自動的に入力され, SDKの準備が完了します",
"p_android_sdk_preparation_edit_sdk_location": "`Android SDK場所` の欄が空白の場合は, 右側の `編集` ボタンをクリックし, 表示されるウィンドウで `次へ` ボタンを数回クリックします",
- "p_android_sdk_preparation_finish_sdk_installation": "関連するリソースのダウンロードとインストールが完了したら, `完了` ボタンをクリックします",
- "p_android_sdk_preparation_open_settings_android_studio": "Android Studioソフトウェアで`CTRL + ALT + S`のショートカットキーを使用して設定ページを開く",
- "p_android_sdk_tools_preparation_sdk_tools": "Android Studioソフトウェアで`CTRL + ALT + S`のショートカットキーを使用して設定ページを開く",
+ "p_android_sdk_preparation_finish_sdk_installation": "関連するリソースのダウンロードとインストールが完了したら, `完了` ボタンをクリックします",
+ "p_android_sdk_preparation_open_settings_android_studio": "Android Studioソフトウェアで `CTRL + ALT + S` のショートカットキーを使用して設定ページを開く",
+ "p_android_sdk_tools_preparation_sdk_tools": "Android Studioソフトウェアで `CTRL + ALT + S` のショートカットキーを使用して設定ページを開く",
"p_android_sdk_tools_preparation_sdk_tools_required": "AutoJs6では一部のSDKツール(NDKやCMakeなど)が必要です",
- "p_android_sdk_tools_preparation_show_package_details": "`パッケージの詳細を表示`を選択し, NDKおよびCMakeをそれぞれクリックして, 対応するバージョンのツールが選択されていることを確認してください, SDKツールのバージョン情報は, AutoJs6プロジェクトのルートディレクトリにある`version.properties`ファイルに記載されています",
+ "p_android_sdk_tools_preparation_show_package_details": "`パッケージの詳細を表示` を選択し, NDKおよびCMakeをそれぞれクリックして, 対応するバージョンのツールが選択されていることを確認してください, SDKツールのバージョン情報は, AutoJs6プロジェクトのルートディレクトリにある `version.properties` ファイルに記載されています",
"p_android_studio_preparation_download_android_studio": "`{{ android_studio_latest_recommended_version_name }}` バージョンをダウンロードしてください (いずれかを選択)",
"p_android_studio_preparation_install_android_studio": "上記のファイルをインストールまたは解凍し, Android Studio ソフトウェアを実行します (例: `\"D:\\android-studio\\bin\\studio64.exe\"`)",
"p_autojs6_gradle_tasks_intro": "プロジェクトのアプリをビルドする以外にも, AutoJs6 はいくつかの定義済み Gradle ビルドタスクをサポートしており, これらは開発者がプロジェクト関連の便利な操作を行うのに役立ちます",
@@ -101,7 +101,7 @@
"p_autojs6_project_cloning_build_successful": "ビルドが完了すると, Android Studio の `Build` タブに `BUILD SUCCESSFUL in 1h 17m 34s` のようなメッセージが表示されます",
"p_autojs6_project_cloning_debug_version_apk": "プロジェクトをパッケージ化して, Android デバイスにインストール可能な APK ファイルを生成します",
"p_autojs6_project_deploy_apk_overview": "プロジェクトをパッケージして, Android デバイスにインストール可能な APK ファイルを生成します",
- "p_autojs6_project_deploy_run_overview": "プロジェクトが正常にビルドされた後, 実機(例: スマートフォン/タブレット)や仮想デバイス(例: Android エミュレーター)にアプリとしてデプロイできます",
+ "p_autojs6_project_deploy_run_overview": "プロジェクトが正常にビルドされた後, 実機 (例: スマートフォン/タブレット) や仮想デバイス (例: Android エミュレーター) にアプリとしてデプロイできます",
"p_autojs6_resources_cloning_click_clone_button": "`Clone (クローン)` ボタンをクリックし, AutoJs6 プロジェクト リソースがデバイスにローカルにクローンされるのを待ちます",
"p_autojs6_resources_cloning_clone_from_vcs": "Android Studio のメインページで `Get from VCS (バージョン管理システムから取得)` ボタンをクリックします",
"p_autojs6_resources_cloning_fill_directory": "`Directory (ディレクトリ)` の欄を必要に応じて特定のパスに変更できます",
@@ -111,7 +111,7 @@
"p_contribution_table_new_pull_requests_pending_processing": "新たに発行された未処理のプルリクエストは, マージされた後にデータ統計に含まれます",
"p_contribution_table_some_contributors_not_appearing": "一部の貢献者は [GitHub Contributors](https://github.com/SuperMonster003/AutoJs6/graphs/contributors) に正常に表示されず, コミット記録が空のままです. 貢献履歴は [プルリクエスト](https://github.com/SuperMonster003/AutoJs6/pulls) から確認できます",
"p_contribution_table_thank_all_contributors": "AutoJs6 プロジェクト開発に参加したすべての貢献者に感謝します",
- "p_gradle_task_app_bundle_description": "タスク実行後, AutoJs6 プロジェクトの最新の JAR ファイルが生成されます. [Android d.ts Generator](https://github.com/NativeScript/android-dts-generator)([NativeScript](https://github.com/NativeScript) 作)のツールを使用して, AutoJs6 メインアプリの型定義(d.ts 形式のファイル)を生成できます",
+ "p_gradle_task_app_bundle_description": "タスク実行後, AutoJs6 プロジェクトの最新の JAR ファイルが生成されます. [Android d.ts Generator](https://github.com/NativeScript/android-dts-generator) ([NativeScript](https://github.com/NativeScript) 作) のツールを使用して, AutoJs6 メインアプリの型定義 (d.ts 形式のファイル) を生成できます",
"p_gradle_task_app_bundle_summary": "デバッグビルドプロセスの成果物をパッケージします. これは主に [AutoJs6 型定義](http://dts-project.autojs6.com) プロジェクトに使用されます",
"p_gradle_task_app_digest_filename_comparison": "以下は, ダイジェスト情報を付加する前後のファイル名の比較です",
"p_gradle_task_app_digest_summary": "リリースビルドで生成された APK ファイルにダイジェスト情報を付加します. これは, GitHub に最新バージョンの AutoJs6 を公開する前に, 最終的なファイル名を確定するためのものです",
diff --git a/.readme/lang_ko.json b/.readme/lang_ko.json
index a21eb907..a897b415 100644
--- a/.readme/lang_ko.json
+++ b/.readme/lang_ko.json
@@ -32,8 +32,8 @@
"h5_for_more_version_history": "더 많은 버전 기록은 참조하십시오",
"label_after": "후",
"label_before": "전",
- "li_deploy_run_step_click_run": "우측의 `Run` (삼각형 아이콘) 버튼을 클릭하여 앱을 실행합니다. 기본 단축키는 `F10`입니다",
- "li_deploy_run_step_select_app": "툴바에서 `Run/Debug Configurations` 메뉴의 `app`을 선택합니다. 이는 AutoJs6의 메인 앱입니다",
+ "li_deploy_run_step_click_run": "우측의 `Run` (삼각형 아이콘) 버튼을 클릭하여 앱을 실행합니다. 기본 단축키는 `F10` 입니다",
+ "li_deploy_run_step_select_app": "툴바에서 `Run/Debug Configurations` 메뉴의 `app` 을 선택합니다. 이는 AutoJs6의 메인 앱입니다",
"li_deploy_run_step_select_device": "우측의 `Available Devices` 드롭다운 목록에서 프로젝트 앱을 실행할 디바이스(실제 또는 가상)를 선택합니다",
"li_deploy_run_step_wait_install": "배포가 완료될 때까지 기다리면 선택된 디바이스가 자동으로 앱을 설치하고 실행합니다",
"li_development_assistance_autojs6_api_reference_docs": "코드를 작성할 때, AutoJs6 관련 API 및 사용 방법을 문서에서 조회할 수 있습니다",
@@ -75,7 +75,7 @@
"li_manual_troubleshooting": "문제 해결",
"li_manual_user_manual": "사용 설명서 (작성 중)",
"li_project_build_step_click_build_project": "좌측의 `Build Project` (망치 아이콘) 버튼을 클릭하여 프로젝트 빌드를 시작합니다. 기본 단축키는 CTRL + F9입니다",
- "li_project_build_step_toolbar": "툴바의 `Run/Debug Configurations` 메뉴에서 `app`을 선택합니다. 이는 AutoJs6의 메인 앱입니다",
+ "li_project_build_step_toolbar": "툴바의 `Run/Debug Configurations` 메뉴에서 `app` 을 선택합니다. 이는 AutoJs6의 메인 앱입니다",
"li_project_build_step_wait_build_success": "빌드가 완료될 때까지 기다립니다. `Builder Output` 탭에 \"BUILD SUCCESSFUL in 1h 17m 34s\"와 같은 메시지가 표시됩니다",
"li_rhino_feature_array_includes_support": "[Array.prototype.includes()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/includes) 지원",
"li_rhino_feature_bigint_support": "[BigInt](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/BigInt) 지원",
@@ -84,9 +84,9 @@
"li_rhino_feature_rhino_engine_new_features": "Rhino 엔진의 [더 많은 새로운 기능](http://project.autojs6.com/blob/master/app/src/main/assets-app/doc/RHINO.md) 보기",
"li_rhino_feature_template_strings_support": "[템플릿 문자열](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Template_literals) 지원",
"li_rhino_feature_unicode_code_point_escapes_support": "유니코드 [코드 포인트](https://developer.mozilla.org/zh-CN/docs/Glossary/Code_point) 이스케이프 지원 [보조 평면](https://zh.wikipedia.org/wiki/Unicode%E5%AD%97%E7%AC%A6%E5%B9%B3%E9%9D%A2%E6%98%A0%E5%B0%84#%E7%AC%AC%E4%B8%80%E8%BC%94%E5%8A%A9%E5%B9%B3%E9%9D%A2) 문자",
- "p_android_sdk_preparation_auto_sdk_location_filled": "위의 `Android SDK Location`(안드로이드 소프트웨어 개발 키트 위치) 필드에 경로가 자동으로 작성되고, SDK 준비 작업이 완료됩니다",
- "p_android_sdk_preparation_edit_sdk_location": "`Android SDK Location`(안드로이드 소프트웨어 개발 키트 위치) 필드가 비어 있는 경우, 오른쪽의 `Edit`(편집) 버튼을 클릭하고, 팝업 창에서 여러 번 `Next`(다음)를 클릭합니다",
- "p_android_sdk_preparation_finish_sdk_installation": "관련 리소스가 다운로드 및 설치를 완료하면, `Finish`(완료) 버튼을 클릭합니다",
+ "p_android_sdk_preparation_auto_sdk_location_filled": "위의 `Android SDK Location` (안드로이드 소프트웨어 개발 키트 위치) 필드에 경로가 자동으로 작성되고, SDK 준비 작업이 완료됩니다",
+ "p_android_sdk_preparation_edit_sdk_location": "`Android SDK Location` (안드로이드 소프트웨어 개발 키트 위치) 필드가 비어 있는 경우, 오른쪽의 `Edit` (편집) 버튼을 클릭하고, 팝업 창에서 여러 번 `Next` (다음)를 클릭합니다",
+ "p_android_sdk_preparation_finish_sdk_installation": "관련 리소스가 다운로드 및 설치를 완료하면, `Finish` (완료) 버튼을 클릭합니다",
"p_android_sdk_preparation_open_settings_android_studio": "Android Studio 소프트웨어에서 `CTRL + ALT + S` 단축키를 사용하여 설정 페이지를 엽니다",
"p_android_sdk_tools_preparation_sdk_tools": "Android Studio 소프트웨어에서 `CTRL + ALT + S` 단축키를 사용하여 설정 페이지를 엽니다",
"p_android_sdk_tools_preparation_sdk_tools_required": "AutoJs6은 일부 SDK 도구 (예: NDK 및 CMake)를 사용해야 합니다",
@@ -104,8 +104,8 @@
"p_autojs6_project_deploy_run_overview": "프로젝트가 성공적으로 빌드된 후, 이를 실제 디바이스(예: 휴대폰/태블릿)나 가상 디바이스(예: Android 에뮬레이터)에 앱으로 배포할 수 있습니다",
"p_autojs6_resources_cloning_click_clone_button": "`Clone (복제)` 버튼을 클릭하고 AutoJs6 프로젝트 리소스를 장치에 로컬로 복제될 때까지 기다립니다",
"p_autojs6_resources_cloning_clone_from_vcs": "Android Studio 메인 화면에서 `Get from VCS (버전 관리 시스템에서 가져오기)` 버튼을 클릭합니다",
- "p_autojs6_resources_cloning_fill_directory": "`Directory (디렉토리)`에서 필요한 경우 특정 경로로 수정할 수 있습니다",
- "p_autojs6_resources_cloning_fill_url": "`URL (통합 자원 위치)`에 `https://github.com/SuperMonster003/AutoJs6.git`을 입력합니다",
+ "p_autojs6_resources_cloning_fill_directory": "`Directory (디렉토리)` 에서 필요한 경우 특정 경로로 수정할 수 있습니다",
+ "p_autojs6_resources_cloning_fill_url": "`URL (통합 자원 위치)` 에 `https://github.com/SuperMonster003/AutoJs6.git` 을 입력합니다",
"p_contribution_table_data_entries_sorted_by_recent_submissions": "데이터 항목은 `최근 제출` 순으로 내림차순 정렬됩니다",
"p_contribution_table_data_updated_on": "데이터 갱신일: {{ var_date_contribution_table_data_updated }}",
"p_contribution_table_new_pull_requests_pending_processing": "처리되지 않은 새로 발행된 Pull Request는 병합 처리 후 데이터 통계에 추가될 것입니다",
@@ -119,22 +119,22 @@
"p_gradle_task_inrt_exception": "패키징 기능 사용 시 다음과 같은 오류 메시지가 나타날 수 있습니다",
"p_gradle_task_inrt_exception_explanation": "이는 템플릿 파일이 존재하지 않음을 나타냅니다. 템플릿 파일을 생성하기 위해 `inrt:assemble` 작업을 수동으로 실행한 후, 앱을 다시 빌드하고 배포하십시오",
"p_gradle_task_inrt_summary": "\"inrt\" 빌드 변형을 어셈블합니다. 이는 주로 AutoJs6 스크립트 패키징 기능을 위한 것입니다",
- "p_introduction_autojs6_is_based_on_autojs": "AutoJs6는 Auto.js 최종 프로젝트를 기반으로 `2021/12/01`에 다시 개발되어 계속해서 오픈 소스로 무료로 유지됩니다",
+ "p_introduction_autojs6_is_based_on_autojs": "AutoJs6는 Auto.js 최종 프로젝트를 기반으로 `2021/12/01` 에 다시 개발되어 계속해서 오픈 소스로 무료로 유지됩니다",
"p_introduction_autojs_definition": "[Auto.js](https://github.com/hyb1996/Auto.js) Android 플랫폼에서 [접근성 서비스](https://developer.android.com/guide/topics/ui/accessibility/service?hl=zh-cn)를 지원하는 JavaScript 자동화 도구 소프트웨어입니다",
- "p_introduction_autojs_released_information": "Auto.js는 [hyb1996](https://github.com/hyb1996)에 의해 `2017/01/27` 처음 출시되었으며, `2020/03/13` 에 유지보수가 중단되었고, 최종 버전 이름은 `4.1.1 Alpha2`, 빌드 버전 번호는 `461`입니다",
+ "p_introduction_autojs_released_information": "Auto.js는 [hyb1996](https://github.com/hyb1996)에 의해 `2017/01/27` 처음 출시되었으며, `2020/03/13` 에 유지보수가 중단되었고, 최종 버전 이름은 `4.1.1 Alpha2`, 빌드 버전 번호는 `461` 입니다",
"p_introduction_related_autojs_table_announce": "다음 표는 일부 Auto.js 관련 프로젝트를 나열한 것입니다 (개발 날짜 순으로 정렬됨)",
"p_introduction_related_autojs_table_app_is_free_or_not": "표에 나열된 Auto.js Pro 7/8/9 는 유료 버전이며, 그 외는 무료 오픈 소스 버전입니다",
"p_introduction_related_autojs_table_date_is_estimated": "표에 표시된 날짜는 예상 날짜로, 실제 날짜와 다를 수 있습니다",
- "p_jdk_preparation_add_existing_jdk": "`Add JDK (JDK 추가)`를 선택하여 이미 존재하는 로컬 JDK를 추가하고 디렉토리를 지정하여 JDK 추가를 완료할 수 있습니다",
+ "p_jdk_preparation_add_existing_jdk": "`Add JDK (JDK 추가)` 를 선택하여 이미 존재하는 로컬 JDK를 추가하고 디렉토리를 지정하여 JDK 추가를 완료할 수 있습니다",
"p_jdk_preparation_download_jdk": "JDK는 IDE에서 직접 다운로드하거나 [Oracle 웹사이트](https://www.oracle.com/java/technologies/downloads/) 에서 다운로드할 수 있습니다",
- "p_jdk_preparation_download_new_jdk": "그렇지 않으면 `Download JDK (JDK 다운로드)`를 선택하여 적절한 JDK를 다운로드하고, `Download (다운로드)` 버튼을 클릭하여 완료될 때까지 기다립니다",
- "p_jdk_preparation_jdk_max_supported_version": "{{ var_date_jdk_max_supported }} 기준으로, AutoJs6이 지원하는 최대 JDK 버전은 `{{ jdk_max_supported }}`입니다",
+ "p_jdk_preparation_download_new_jdk": "그렇지 않으면 `Download JDK (JDK 다운로드)` 를 선택하여 적절한 JDK를 다운로드하고, `Download (다운로드)` 버튼을 클릭하여 완료될 때까지 기다립니다",
+ "p_jdk_preparation_jdk_max_supported_version": "{{ var_date_jdk_max_supported }} 기준으로, AutoJs6이 지원하는 최대 JDK 버전은 `{{ jdk_max_supported }}` 입니다",
"p_jdk_preparation_jdk_version_requirements": "AutoJs6 프로젝트는 `JDK (Java 개발 도구 키트)` 버전이 `{{ jdk_min_supported }}` 이상이어야 하지만, `{{ jdk_min_suggested }}` 이상을 권장합니다",
"p_jdk_preparation_open_jdk_settings": "Android Studio 소프트웨어에서 단축키 `CTRL + ALT + S` 를 사용하여 설정 페이지를 여십시오",
"p_jdk_preparation_select_existing_jdk": "목록에 적합한 버전의 JDK(>= `{{ jdk_min_supported }}`)가 있는 경우, 직접 선택하십시오",
"p_jdk_preparation_select_gradle_jdk": "`Gradle JDK` 에서 다양한 버전의 JDK를 선택하거나 추가할 수 있습니다",
"p_languages_all_supported_for_readme": "현재 README.md 파일에서 지원하는 언어",
- "p_major_changes_autojs6_version_updates": "Auto.js 최종 오픈 소스 버전 `4.1.1 Alpha2`와 비교하여 AutoJs6는 주요한 다음과 같은 업그레이드 및 변동사항이 있습니다",
+ "p_major_changes_autojs6_version_updates": "Auto.js 최종 오픈 소스 버전 `4.1.1 Alpha2` 와 비교하여 AutoJs6는 주요한 다음과 같은 업그레이드 및 변동사항이 있습니다",
"p_project_compilation_and_build_android_studio_build_methods": "이 섹션에서는 Android Studio를 사용하여 AutoJs6 오픈 소스 프로젝트를 컴파일하고 빌드하는 방법에 대해 설명합니다. IntelliJ IDEA도 이와 유사합니다",
"p_project_compilation_and_build_android_studio_intellij_idea": "AutoJs6 오픈 소스 프로젝트를 디버깅하거나 개발하려면 [Android Studio](https://pro.autojs.org/) ([Google](https://www.google.com/) 제품) 또는 [IntelliJ IDEA](https://www.jetbrains.com/idea/) ([Jetbrains](https://www.jetbrains.com/) 제품)를 사용할 수 있습니다",
"step_android_sdk_location": "Android SDK (안드로이드 소프트웨어 개발 키트)",
diff --git a/README.md b/README.md
index fca7fe0a..8b741637 100644
--- a/README.md
+++ b/README.md
@@ -157,12 +157,13 @@ AutoJs6 在 Auto.js 最终项目的基础上, 于 `2021/12/01` 进行二次开
# v6.6.3
-###### 2025/05/13
+###### 2025/05/14
* `新增` 版本历史功能, 可查看发行版本历史更新记录 (多语言) 与统计数据
* `新增` timers.keepAlive 方法 (已全局化), 用于保持脚本活跃状态
* `新增` engines.on('start/stop/error', callback) 等事件监听方法, 用于监听脚本引擎全局事件
* `新增` images.detectMultiColors 方法, 用于多点颜色校验 _[`issue #374`](http://issues.autojs6.com/374)_
+* `新增` images.matchFeatures/detectAndComputeFeatures 方法, 支持全分辨率找图 (Ref to [Auto.js Pro](https://g.pro.autojs.org/)) _[`issue #366`](http://issues.autojs6.com/366)_
* `修复` 主页文档标签显示在线文档时部分内容被系统导航栏遮挡的问题
* `修复` 部分设备代码编辑器空行显示方框字符的问题
* `修复` 主题色设置页面调色盘对话框可能无限叠加的问题
@@ -176,7 +177,7 @@ AutoJs6 在 Auto.js 最终项目的基础上, 于 `2021/12/01` 进行二次开
* `修复` console.setContentBackgroundColor 方法无法接受颜色名称参数的问题 _[`issue #384`](http://issues.autojs6.com/384)_
* `修复` README.md 中部分语言日期格式不正确的问题
* `优化` 布局分析支持控件隐藏 (by [TonyJiangWJ](https://github.com/TonyJiangWJ)) _[`pr #371`](http://pr.autojs6.com/371)_ _[`issue #355`](http://issues.autojs6.com/355)_
-* `优化` 布局分析菜单使用渐变分隔线实现一定程度的功能分组
+* `优化` 布局分析菜单添加渐变分隔线实现一定程度的功能分组
* `优化` 主题色扩充适配范围并支持更多控件类型
* `优化` 主页抽屉在横向屏幕或超宽屏幕的宽度适应性
* `优化` 关于应用与开发者页面增加水平布局及小屏布局适配
@@ -333,7 +334,7 @@ SDK Tools (SDK 工具) (位于右侧窗口)
AutoJs6 项目依赖的 `JDK (Java 开发工具包)` 发行版本不低于 `17`, 但建议不低于 `19`.
-截至 2025 年 5 月 13 日, AutoJs6 可支持 JDK 最高版本为 `24`.
+截至 2025 年 5 月 14 日, AutoJs6 可支持 JDK 最高版本为 `24`.
> 注: 如果计算机系统已安装 JDK 且版本满足上述要求, 则可跳过此小节内容.
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index bc227bd6..b1611a59 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -863,18 +863,18 @@ class Versions(filePath: String) {
}
var versionInt = javaVersionRaw.toInt()
- var isFallback = false
+ var isJvmCoercive = false
while (versionInt > javaVersionMinSupported) {
if (JvmTarget.values().any { it.name.contains(Regex("_$versionInt$")) }) {
break
}
versionInt -= 1
- isFallback = true
+ isJvmCoercive = true
}
- if (isFallback) {
- javaVersionInfoSuffix += " [fallback]"
+ if (isJvmCoercive) {
+ javaVersionInfoSuffix += " [coercive-jvm-downgraded]"
}
if (versionInt > currentVersionInt) {
diff --git a/app/src/main/assets-app/doc/CHANGELOG-zh-Hans.md b/app/src/main/assets-app/doc/CHANGELOG-zh-Hans.md
index 913690bb..9d543ef4 100644
--- a/app/src/main/assets-app/doc/CHANGELOG-zh-Hans.md
+++ b/app/src/main/assets-app/doc/CHANGELOG-zh-Hans.md
@@ -6,12 +6,13 @@
# v6.6.3
-###### 2025/05/13
+###### 2025/05/14
* `新增` 版本历史功能, 可查看发行版本历史更新记录 (多语言) 与统计数据
* `新增` timers.keepAlive 方法 (已全局化), 用于保持脚本活跃状态
* `新增` engines.on('start/stop/error', callback) 等事件监听方法, 用于监听脚本引擎全局事件
* `新增` images.detectMultiColors 方法, 用于多点颜色校验 _[`issue #374`](http://issues.autojs6.com/374)_
+* `新增` images.matchFeatures/detectAndComputeFeatures 方法, 支持全分辨率找图 (Ref to [Auto.js Pro](https://g.pro.autojs.org/)) _[`issue #366`](http://issues.autojs6.com/366)_
* `修复` 主页文档标签显示在线文档时部分内容被系统导航栏遮挡的问题
* `修复` 部分设备代码编辑器空行显示方框字符的问题
* `修复` 主题色设置页面调色盘对话框可能无限叠加的问题
@@ -25,7 +26,7 @@
* `修复` console.setContentBackgroundColor 方法无法接受颜色名称参数的问题 _[`issue #384`](http://issues.autojs6.com/384)_
* `修复` README.md 中部分语言日期格式不正确的问题
* `优化` 布局分析支持控件隐藏 (by [TonyJiangWJ](https://github.com/TonyJiangWJ)) _[`pr #371`](http://pr.autojs6.com/371)_ _[`issue #355`](http://issues.autojs6.com/355)_
-* `优化` 布局分析菜单使用渐变分隔线实现一定程度的功能分组
+* `优化` 布局分析菜单添加渐变分隔线实现一定程度的功能分组
* `优化` 主题色扩充适配范围并支持更多控件类型
* `优化` 主页抽屉在横向屏幕或超宽屏幕的宽度适应性
* `优化` 关于应用与开发者页面增加水平布局及小屏布局适配
diff --git a/app/src/main/assets-app/doc/CHANGELOG.md b/app/src/main/assets-app/doc/CHANGELOG.md
index 913690bb..9d543ef4 100644
--- a/app/src/main/assets-app/doc/CHANGELOG.md
+++ b/app/src/main/assets-app/doc/CHANGELOG.md
@@ -6,12 +6,13 @@
# v6.6.3
-###### 2025/05/13
+###### 2025/05/14
* `新增` 版本历史功能, 可查看发行版本历史更新记录 (多语言) 与统计数据
* `新增` timers.keepAlive 方法 (已全局化), 用于保持脚本活跃状态
* `新增` engines.on('start/stop/error', callback) 等事件监听方法, 用于监听脚本引擎全局事件
* `新增` images.detectMultiColors 方法, 用于多点颜色校验 _[`issue #374`](http://issues.autojs6.com/374)_
+* `新增` images.matchFeatures/detectAndComputeFeatures 方法, 支持全分辨率找图 (Ref to [Auto.js Pro](https://g.pro.autojs.org/)) _[`issue #366`](http://issues.autojs6.com/366)_
* `修复` 主页文档标签显示在线文档时部分内容被系统导航栏遮挡的问题
* `修复` 部分设备代码编辑器空行显示方框字符的问题
* `修复` 主题色设置页面调色盘对话框可能无限叠加的问题
@@ -25,7 +26,7 @@
* `修复` console.setContentBackgroundColor 方法无法接受颜色名称参数的问题 _[`issue #384`](http://issues.autojs6.com/384)_
* `修复` README.md 中部分语言日期格式不正确的问题
* `优化` 布局分析支持控件隐藏 (by [TonyJiangWJ](https://github.com/TonyJiangWJ)) _[`pr #371`](http://pr.autojs6.com/371)_ _[`issue #355`](http://issues.autojs6.com/355)_
-* `优化` 布局分析菜单使用渐变分隔线实现一定程度的功能分组
+* `优化` 布局分析菜单添加渐变分隔线实现一定程度的功能分组
* `优化` 主题色扩充适配范围并支持更多控件类型
* `优化` 主页抽屉在横向屏幕或超宽屏幕的宽度适应性
* `优化` 关于应用与开发者页面增加水平布局及小屏布局适配
diff --git a/app/src/main/java/org/autojs/autojs/core/automator/UiObject.kt b/app/src/main/java/org/autojs/autojs/core/automator/UiObject.kt
index c8e85b66..1f2b6aa4 100644
--- a/app/src/main/java/org/autojs/autojs/core/automator/UiObject.kt
+++ b/app/src/main/java/org/autojs/autojs/core/automator/UiObject.kt
@@ -19,6 +19,7 @@ import org.autojs.autojs.runtime.ScriptRuntime
import org.autojs.autojs.util.DisplayUtils.toRoundIntX
import org.autojs.autojs.util.DisplayUtils.toRoundIntY
import org.autojs.autojs.util.RhinoUtils
+import org.autojs.autojs.util.StringUtils
import org.autojs.autojs.util.StringUtils.str
import org.autojs.autojs6.R
import org.mozilla.javascript.BaseFunction
@@ -436,70 +437,55 @@ open class UiObject(
}
}
- fun summary(): String {
- val separatorLv0 = "\n"
- val separatorLv1 = "$separatorLv0 "
- val separatorLv2 = "$separatorLv1 "
-
- val dataList = listOf Any?>>(
+ fun summary(): String = listOf(
- /* Common */
+ /* Common */
- "packageName" to { packageName() },
- "parent" to { parent?.className },
- "id" to { id() },
- "fullId" to { fullId() },
- "idHex" to { idHex() },
- "desc" to { desc() },
- "text" to { text() },
- "bounds" to { bounds() },
- "center" to { center() },
- "className" to { className() },
- "clickable" to { clickable() },
- "longClickable" to { longClickable() },
- "scrollable" to { scrollable() },
- "indexInParent" to { indexInParent() },
- "childCount" to { childCount() },
- "depth" to { depth() },
+ "packageName" to { packageName() },
+ "parent" to { parent?.className },
+ "id" to { id() },
+ "fullId" to { fullId() },
+ "idHex" to { idHex() },
+ "desc" to { desc() },
+ "text" to { text() },
+ "bounds" to { bounds() },
+ "center" to { center() },
+ "className" to { className() },
+ "clickable" to { clickable() },
+ "longClickable" to { longClickable() },
+ "scrollable" to { scrollable() },
+ "indexInParent" to { indexInParent() },
+ "childCount" to { childCount() },
+ "depth" to { depth() },
- /* Regular */
+ /* Regular */
- "checked" to { checked() },
- "enabled" to { enabled() },
- "editable" to { editable() },
- "focusable" to { focusable() },
- "checkable" to { checkable() },
- "selected" to { selected() },
- "dismissable" to { isDismissable },
- "visibleToUser" to { visibleToUser() },
+ "checked" to { checked() },
+ "enabled" to { enabled() },
+ "editable" to { editable() },
+ "focusable" to { focusable() },
+ "checkable" to { checkable() },
+ "selected" to { selected() },
+ "dismissable" to { isDismissable },
+ "visibleToUser" to { visibleToUser() },
- /* Rare */
+ /* Rare */
- "contextClickable" to { isContextClickable },
- "focused" to { focused() },
- "accessibilityFocused" to { isAccessibilityFocused },
- "rowCount" to { rowCount() },
- "columnCount" to { columnCount() },
- "row" to { row() },
- "column" to { column() },
- "rowSpan" to { rowSpan() },
- "columnSpan" to { columnSpan() },
- "drawingOrder" to { drawingOrder },
+ "contextClickable" to { isContextClickable },
+ "focused" to { focused() },
+ "accessibilityFocused" to { isAccessibilityFocused },
+ "rowCount" to { rowCount() },
+ "columnCount" to { columnCount() },
+ "row" to { row() },
+ "column" to { column() },
+ "rowSpan" to { rowSpan() },
+ "columnSpan" to { columnSpan() },
+ "drawingOrder" to { drawingOrder },
- /* List */
+ /* List */
- "actions" to { actionNames() },
- )
- return dataList.joinToString(prefix = "{$separatorLv1", separator = separatorLv1, postfix = "$separatorLv0}") { (name, action) ->
- val value = when (val actionResult = action()) {
- is CharSequence -> "\"$actionResult\""
- is Iterable<*> -> actionResult.joinToString(prefix = "[$separatorLv2", separator = separatorLv2, postfix = "$separatorLv1]")
- is Array<*> -> actionResult.joinToString(prefix = "[$separatorLv2", separator = separatorLv2, postfix = "$separatorLv1]")
- else -> actionResult
- }
- "$name=$value"
- }
- }
+ "actions" to { actionNames() },
+ ).let { StringUtils.toFormattedSummary(it) }
override fun toString(): String {
val simpledClassName = "$className".substringAfterLast(".")
diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/ImageFeatureMatching.java b/app/src/main/java/org/autojs/autojs/runtime/api/ImageFeatureMatching.java
index c120ad44..c0e2b693 100644
--- a/app/src/main/java/org/autojs/autojs/runtime/api/ImageFeatureMatching.java
+++ b/app/src/main/java/org/autojs/autojs/runtime/api/ImageFeatureMatching.java
@@ -8,7 +8,9 @@ import org.autojs.autojs.core.cleaner.Cleaner;
import org.autojs.autojs.core.cleaner.ICleaner;
import org.autojs.autojs.core.ref.MonitorResource;
import org.autojs.autojs.core.ref.NativeObjectReference;
+import org.autojs.autojs.util.ImageUtils;
import org.opencv.calib3d.Calib3d;
+import org.opencv.core.Core;
import org.opencv.core.DMatch;
import org.opencv.core.KeyPoint;
import org.opencv.core.Mat;
@@ -26,9 +28,10 @@ import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import java.util.ArrayList;
-import java.util.LinkedList;
+import java.util.Comparator;
import java.util.List;
import java.util.Objects;
+import java.util.stream.Stream;
/**
* Created by SuperMonster003 on Jan 7, 2024.
@@ -44,122 +47,261 @@ public final class ImageFeatureMatching {
@ScriptInterface
public static int FEATURE_MATCHING_METHOD_ORB = 2;
- // FIXME by SuperMonster003 on Nov 23, 2024.
- // ! This function needs to be corrected or improved.
- // ! zh-CN: 此函数功能需纠正或完善.
- public static FeatureMatchingDescriptor createFeatureMatchingDescriptor(Mat mat, int cvtColorFlag, float scale, int method) {
- if (mat == null || mat.empty()) {
- throw new IllegalArgumentException("Input Mat cannot be null or empty");
+ // @Hint by SuperMonster003 on May 14, 2025.
+ // ! This method was corrected and improved by JetBrains AI Assistant.
+ // ! zh-CN: 此方法由 JetBrains AI Assistant 纠正并完善.
+ /**
+ * Pre-computes a feature-matching descriptor for an image. The pipeline:
+ *
+ * - Optional color-space conversion controlled by {@code cvtColorFlag}
+ * - Optional uniform scaling specified by {@code scale}
+ * - Key-point detection and descriptor extraction according to {@code method}
+ * (SIFT / ORB)
+ * - Wrap everything into a {@link FeatureMatchingDescriptor} so that it can be
+ * reused later by {@link #featureMatching}
+ *
+ *
+ * zh-CN
+ *
+ * 创建 (预计算) 特征匹配描述符.
+ * 典型流程:
+ * 1. (可选) 颜色空间转换 {@code cvtColorFlag}
+ * 2. (可选) 缩放 {@code scale}
+ * 3. 依据 {@code method} (SIFT / ORB) 检测关键点并计算描述子
+ * 4. 封装为 {@link FeatureMatchingDescriptor} 供后续匹配复用
+ *
+ *
+ * 典型使用场景: 模板图/场景图分别调用一次当前方法, 随后在多次 {@link #featureMatching} 中复用, 以避免重复提特征带来的性能损耗.
+ *
+ * @param src The source Mat. The content will not be modified.
+ * zh-CN: 原始 {@link Mat}. 输入图像内容不会被修改.
+ * @param cvtColorFlag OpenCV cvtColor flag; -1 means "no conversion".
+ * zh-CN: OpenCV 颜色空间转换标志; 传 -1 表示跳过转换.
+ * @param scale Scale factor; >0 to resize, ≤0 or 1 to keep original size.
+ * zh-CN: 缩放因子; >0 表示按比例缩放, ≤0 或 1 表示保持原尺寸.
+ * @param method Extraction method, see {@code FEATURE_MATCHING_METHOD_*}.
+ * zh-CN: 特征提取方法; 取值见 {@code FEATURE_MATCHING_METHOD_*}.
+ *
+ * @return The generated descriptor. Release via
+ * {@link FeatureMatchingDescriptor#release()} or rely on GC.
+ * zh-CN: 生成的 {@link FeatureMatchingDescriptor}; 仅当显式调用
+ * {@link FeatureMatchingDescriptor#release()} 或对象被 GC 时才会释放其底层资源.
+ *
+ * @throws IllegalArgumentException Thrown if {@code src} is empty or {@code method} is unsupported.
+ * zh-CN: 当 {@code src} 为空或不支持的 {@code method} 时抛出.
+ */
+ @ScriptInterface
+ public static FeatureMatchingDescriptor createFeatureMatchingDescriptor(
+ @NonNull Mat src,
+ int cvtColorFlag,
+ float scale,
+ int method
+ ) {
+ if (src.empty()) {
+ throw new IllegalArgumentException("Input Mat is null or empty.");
}
- // Add this part to ensure the image is loaded correctly
- if (mat.empty()) {
- throw new RuntimeException("Failed to load image. The image file might be missing or the path is incorrect.");
+ // Color space conversion (zh-CN: 颜色空间转换)
+ Mat processed = src;
+ if (cvtColorFlag >= 0) {
+ processed = new Mat();
+ Imgproc.cvtColor(src, processed, cvtColorFlag);
}
- // Convert color if needed
- Mat convertedMat = new Mat();
- if (cvtColorFlag != -1) {
- Imgproc.cvtColor(mat, convertedMat, cvtColorFlag);
- } else {
- convertedMat = mat.clone();
+ // Scaling (zh-CN: 缩放)
+ if (scale > 0f && Math.abs(scale - 1f) > 1e-3) {
+ Size newSize = new Size(processed.cols() * scale, processed.rows() * scale);
+ Mat resized = new Mat();
+ Imgproc.resize(processed, resized, newSize);
+ processed = resized;
}
- // Resize the image if scaling is required
- Mat scaledMat = new Mat();
- if (scale > 0 && scale != 1.0f) {
- Imgproc.resize(convertedMat, scaledMat, new Size(mat.cols() * scale, mat.rows() * scale));
- } else {
- scaledMat = convertedMat.clone();
- }
-
- // Select feature detector and descriptor extractor based on method
+ // Choose detector (zh-CN: 选择算子)
Feature2D detector;
if (method == FEATURE_MATCHING_METHOD_SIFT) {
detector = SIFT.create();
} else if (method == FEATURE_MATCHING_METHOD_ORB) {
- detector = ORB.create();
+ detector = ORB.create(
+ /* nFeatures = */ 5000, /* 500 -> 5000, increase feature count (zh-CN: 增加特征数量) */
+ /* scaleFactor = */ 1.2f,
+ /* nLevels = */ 8,
+ /* edgeThreshold = */ 31,
+ /* firstLevel = */ 0,
+ /* WTA_K = */ 4, /* 2 -> 4, produces 256 bit descriptors, increases discrimination (zh-CN: 产生 256 bit 描述子, 增加区分度) */
+ /* scoreType = */ ORB.HARRIS_SCORE,
+ /* patchSize = */ 31,
+ /* fastThreshold = */ 20);
} else {
throw new IllegalArgumentException("Unsupported feature matching method: " + method);
}
- // Detect keypoints and compute descriptors
- MatOfKeyPoint keyPoints = new MatOfKeyPoint();
+ // Calculate KeyPoint & Descriptor (zh-CN: 计算 KeyPoint & Descriptor)
+ MatOfKeyPoint kps = new MatOfKeyPoint();
Mat descriptors = new Mat();
- detector.detect(scaledMat, keyPoints);
- detector.compute(scaledMat, keyPoints, descriptors);
+ detector.detectAndCompute(processed, new Mat(), kps, descriptors);
- return new FeatureMatchingDescriptor(descriptors, keyPoints);
+ // Build reference image corner points (zh-CN: 构建参考图像的四角点)
+ int w = src.width();
+ int h = src.height();
+ MatOfPoint2f corners = new MatOfPoint2f(
+ new Point(0, 0),
+ new Point(w, 0),
+ new Point(0, h),
+ new Point(w, h)
+ );
+
+ return new FeatureMatchingDescriptor(descriptors, kps, corners);
}
-
- // FIXME by SuperMonster003 on Nov 23, 2024.
- // ! This function needs to be corrected or improved.
- // ! zh-CN: 此函数功能需纠正或完善.
+ // @Hint by SuperMonster003 on May 14, 2025.
+ // ! This method was corrected and improved by JetBrains AI Assistant.
+ // ! zh-CN: 此方法由 JetBrains AI Assistant 纠正并完善.
+ /**
+ * It matches two pre-computed feature descriptors and optionally estimates a homography
+ * to locate where the object image appears inside the scene image.
+ *
+ * Internal steps:
+ *
+ * - Create a suitable {@link org.opencv.features2d.DescriptorMatcher} based on {@code matcherType}
+ * - Perform KNN matching (k=2) and filter using Lowe's ratio test with threshold {@code threshold}
+ * - If good matches ≥4, use {@code Calib3d.findHomography(..., RANSAC)} to further eliminate outliers
+ * while calculating projection (quad) of object image corners in scene image
+ * - If caller sets {@code debugMatchesImagePath}, save visualization of match lines to that path
+ *
+ *
+ * zh-CN
+ *
+ * 在两张图的特征描述符之间执行匹配, 并 (可选) 估算单应矩阵以获得被 object 图在 scene 图中的投影区域.
+ *
内部流程:
+ *
+ * - 根据 {@code matcherType} 创建合适的 {@link org.opencv.features2d.DescriptorMatcher}
+ * - 执行 KNN 匹配 (k=2) 并用 Lowe 比例测试 (阈值为 {@code threshold}) 过滤
+ * - 若 good matches ≥4, 则通过 {@code Calib3d.findHomography(..., RANSAC)} 进一步剔除离群点,
+ * 同时计算 object 图四角在 scene 图中的投影 (quad)
+ * - 如调用者设置了 {@code debugMatchesImagePath}, 会将匹配连线可视化保存到该路径
+ *
+ *
+ * @param sceneDesc Descriptor for the scene image.
+ * zh-CN: 目标 / 场景图的描述符.
+ * @param objectDesc Descriptor for the object image.
+ * zh-CN: 模板 / 待检测对象图的描述符.
+ * @param matcherType Type constant of DescriptorMatcher.
+ * zh-CN: {@link org.opencv.features2d.DescriptorMatcher} 的类型常量.
+ * @param debugMatchesImagePath Optional path to save a debug image showing matches; {@code null} to skip.
+ * zh-CN: 可选调试路径; 非空时将匹配结果绘制到该文件.
+ * @param threshold Lowe ratio threshold (0,1); higher = looser filtering.
+ * zh-CN: Lowe Ratio 测试阈值 (0,1). 值越大匹配越宽松.
+ *
+ * @return {@link FeatureMatchingResult}:
+ * • {@code getPoints()} - Filtered matching point pairs (in scene coordinates)
+ * • {@code getQuad()} - 4-point quadrilateral if homography estimation succeeded, otherwise {@code null}
+ * • {@code getMatches()} - Rendered Mat if debug enabled, otherwise {@code null}
+ * • zh-CN:
+ * • {@code getPoints()} - 过滤后的匹配点对 (scene 坐标系)
+ * • {@code getQuad()} - 若成功估算单应矩阵则为 4 点四边形, 否则为 {@code null}
+ * • {@code getMatches()} - 若开启调试则为绘制后的 Mat, 否则为 {@code null}
+ *
+ * @throws IllegalStateException Thrown if descriptors are incompatible or already released.
+ * zh-CN: 当两侧描述子维度不兼容或资源已释放时抛出.
+ */
@ScriptInterface
public static FeatureMatchingResult featureMatching(
- FeatureMatchingDescriptor sceneDescriptor,
- FeatureMatchingDescriptor objectDescriptor,
+ @NonNull FeatureMatchingDescriptor sceneDesc,
+ @NonNull FeatureMatchingDescriptor objectDesc,
int matcherType,
- @Nullable String matchesImageToDrawPath,
+ @Nullable String debugMatchesImagePath,
float threshold
) {
- // Step 1: Extract key points and descriptors
- Mat sceneDescriptors = sceneDescriptor.getDescriptors();
- Mat objectDescriptors = objectDescriptor.getDescriptors();
- MatOfKeyPoint sceneKeyPoints = sceneDescriptor.getKeyPoint();
- MatOfKeyPoint objectKeyPoints = objectDescriptor.getKeyPoint();
-
- List knnMatches = new LinkedList<>();
DescriptorMatcher matcher = DescriptorMatcher.create(matcherType);
+ List knnMatches = new ArrayList<>();
// @Reference to Sakura小败狗 (https://blog.csdn.net/qq_42670220) by SuperMonster003 on Feb 26, 2024.
// ! https://blog.csdn.net/qq_42670220/article/details/108623752
// !
+ // ! knnMatch method finds the best matches in given feature descriptor sets.
+ // ! Using KNN-matching algorithm with k = 2, each match gets 2 closest descriptors,
+ // ! keeps match as final when ratio of closest distance to second closest is greater than threshold.
+ // !
+ // ! zh-CN:
+ // !
// ! knnMatch 方法, 在给定特征描述集合中寻找最佳匹配.
// ! 使用 KNN-matching 算法, k = 2, 每个 match 得到 2 个最接近的 descriptor,
// ! 最接近距离和次接近距离的比值大于既定值时, 作为最终 match.
- matcher.knnMatch(sceneDescriptors, objectDescriptors, knnMatches, 2);
-
- LinkedList niceMatches = new LinkedList<>();
+ matcher.knnMatch(objectDesc.getDescriptors(), sceneDesc.getDescriptors(), knnMatches, 2);
+ // Lowe's ratio test
+ // zh-CN: Lowe 比例测试
+ List goodMatches = new ArrayList<>();
for (MatOfDMatch matOfDMatch : knnMatches) {
DMatch[] matches = matOfDMatch.toArray();
+ if (matches.length < 2) {
+ continue;
+ }
if (matches[0].distance < threshold * matches[1].distance) {
- niceMatches.add(matches[0]);
+ goodMatches.add(matches[0]);
}
}
- List sceneKeyPointsList = sceneKeyPoints.toList();
- List scenePoints = new ArrayList<>(niceMatches.size());
- for (DMatch match : niceMatches) {
- scenePoints.add(sceneKeyPointsList.get(match.queryIdx).pt);
+ // Draw matches visualization
+ // zh-CN: 绘制匹配图
+ Mat matchesImg = null;
+ if (debugMatchesImagePath != null && !debugMatchesImagePath.isEmpty()) {
+ matchesImg = new Mat();
+ Features2d.drawMatches(
+ /* img1 = */ ImageUtils.to8UC3(objectDesc.getDescriptors()), objectDesc.getKeyPoint(),
+ /* img2 = */ ImageUtils.to8UC3(sceneDesc.getDescriptors()), sceneDesc.getKeyPoint(),
+ new MatOfDMatch(goodMatches.toArray(new DMatch[0])),
+ matchesImg);
+ Imgcodecs.imwrite(debugMatchesImagePath, matchesImg);
}
- // Optional: Draw matches
- // if (matchesImageToDrawPath != null && !matchesImageToDrawPath.isEmpty()) {
- // Mat imgMatches = new Mat();
- // Features2d.drawMatches(new Mat(), sceneKeyPoints, new Mat(), objectKeyPoints, new MatOfDMatch(niceMatches.toArray(new DMatch[0])), imgMatches);
- // Imgcodecs.imwrite(matchesImageToDrawPath, imgMatches);
- // }
+ // Extract matched object / scene coordinates
+ // zh-CN: 提取配对的 object / scene 坐标
+ List objPts = new ArrayList<>();
+ List scenePts = new ArrayList<>();
- // Step 2: Compute homography if any good matches found
- Mat homography = null;
- if (!niceMatches.isEmpty()) {
- MatOfPoint2f obj = new MatOfPoint2f();
- MatOfPoint2f scene = new MatOfPoint2f();
+ KeyPoint[] objKpsArr = objectDesc.getKeyPoint().toArray();
+ KeyPoint[] sceneKpsArr = sceneDesc.getKeyPoint().toArray();
- List objectPoints = new ArrayList<>(niceMatches.size());
- for (DMatch match : niceMatches) {
- objectPoints.add(sceneKeyPointsList.get(match.trainIdx).pt);
+ for (DMatch gm : goodMatches) {
+ objPts.add(objKpsArr[gm.queryIdx].pt);
+ scenePts.add(sceneKpsArr[gm.trainIdx].pt);
+ }
+
+ // Calculate homography matrix (requires at least 4 matched coordinate pairs)
+ // zh-CN: 计算单应矩阵 (需至少 4 个配对的坐标点)
+ List quad = null;
+ Log.d(TAG, "objPts: " + objPts.size() + ", scenePts: " + scenePts.size() + ", goodMatches: " + goodMatches.size());
+ if (objPts.size() >= 4) {
+ MatOfPoint2f objMat = new MatOfPoint2f();
+ MatOfPoint2f sceneMat = new MatOfPoint2f();
+ objMat.fromList(objPts);
+ sceneMat.fromList(scenePts);
+
+ Mat H = Calib3d.findHomography(objMat, sceneMat, Calib3d.RANSAC, 3);
+
+ if (!H.empty()) {
+ MatOfPoint2f objCorners = objectDesc.getCorners();
+ MatOfPoint2f sceneCorners = new MatOfPoint2f();
+ Core.perspectiveTransform(objCorners, sceneCorners, H);
+ quad = sortClockwise(sceneCorners.toList());
}
-
- obj.fromList(objectPoints);
- scene.fromList(scenePoints);
-
- homography = Calib3d.findHomography(obj, scene, Calib3d.RANSAC, 3);
}
- return new FeatureMatchingResult(scenePoints, homography);
+ return new FeatureMatchingResult(scenePts, quad, matchesImg);
+ }
+
+ // Sort four points clockwise as TL, TR, BL, BR
+ // zh-CN: 四点按 TL, TR, BL, BR 顺时针排序
+ private static List sortClockwise(List pts) {
+ if (pts.size() != 4) return pts;
+ // Use centroid as reference (zh-CN: 以质心为参考)
+ double cx = pts.stream().mapToDouble(p -> p.x).average().orElse(0);
+ double cy = pts.stream().mapToDouble(p -> p.y).average().orElse(0);
+ // Sort by polar angle (counter-clockwise), then manually adjust to start from TL
+ // zh-CN: 按极角排序 (逆时针), 然后手动调到 TL 开头
+ Stream sorted = pts.stream()
+ .sorted(Comparator.comparingDouble(a -> Math.atan2(a.y - cy, a.x - cx)));
+ return List.of(sorted.toArray(Point[]::new));
}
private static void releaseFeatureMatchingDescriptor(long pointer) {
@@ -168,17 +310,40 @@ public final class ImageFeatureMatching {
mat.release();
}
+ /**
+ * A reusable bundle of feature-extraction results for a single image.
+ * zh-CN: 单张图片的特征提取结果集合.
+ */
public static class FeatureMatchingDescriptor implements MonitorResource {
private final Mat mDescriptors;
private long mNativePtr;
private NativeObjectReference mRef;
private final MatOfKeyPoint mKeyPoint;
+ private final MatOfPoint2f mCorners;
- public FeatureMatchingDescriptor(Mat descriptors, MatOfKeyPoint keyPoint) {
+ /**
+ * Constructs a descriptor object. Usually created internally by
+ * {@link #createFeatureMatchingDescriptor(Mat, int, float, int)}.
+ * Encapsulates feature detection results (keypoints and descriptors) of a single image
+ * for later matching operations.
+ *
+ * zh-CN
+ * 构造一个描述符对象. 通常由 {@link #createFeatureMatchingDescriptor(Mat, int, float, int)}
+ * 内部调用创建. 封装单个图像的特征检测结果 (关键点和描述子) 以供后续匹配.
+ *
+ * @param descriptors Feature descriptor matrix computed by feature detector.
+ * zh-CN: 特征检测器计算出的描述子矩阵.
+ * @param keyPoint Detected key points in the image.
+ * zh-CN: 在图像中检测到的关键点集.
+ * @param corners Virtual corners of the image after scale, always [TL, TR, BL, BR]. Used for homography calculation.
+ * zh-CN: 缩放后图像的虚拟四角点坐标, 按 [左上,右上,左下,右下] 顺序, 用于单应矩阵计算.
+ */
+ public FeatureMatchingDescriptor(Mat descriptors, MatOfKeyPoint keyPoint, MatOfPoint2f corners) {
mDescriptors = descriptors;
mNativePtr = descriptors.nativeObj;
mKeyPoint = keyPoint;
+ mCorners = corners;
Cleaner.instance.cleanup(this, SelfCleaner.INSTANCE);
}
@@ -220,6 +385,10 @@ public final class ImageFeatureMatching {
mNativePtr = nativePtr;
}
+ public MatOfPoint2f getCorners() {
+ return mCorners;
+ }
+
public static class SelfCleaner implements ICleaner {
public static SelfCleaner INSTANCE;
@@ -239,20 +408,48 @@ public final class ImageFeatureMatching {
}
}
+ /**
+ * Immutable container holding the output of a single {@link #featureMatching} operation.
+ *
+ * Depending on whether homography estimation succeeds, the {@code quad} field may be {@code null}.
+ * The {@code matches} image is only generated when the caller passes a non-null debug path.
+ *
+ * zh-CN
+ * 表示一次 {@link #featureMatching} 调用结果的不可变对象.
+ * 若单应矩阵估算失败, {@code quad} 为 {@code null}.
+ * 若未开启 debug 输出, {@code matches} 为 {@code null}.
+ */
public static class FeatureMatchingResult {
private final List mPoints;
+ private final List mQuad;
private final Mat mMatches;
- public FeatureMatchingResult(List points, @Nullable Mat matches) {
- mPoints = points;
+ /**
+ * @param pts Inlier points in scene image after ratio test and RANSAC.
+ * zh-CN: 终态内点 (场景座标系).
+ * @param quad Projected quadrilateral of the object image in scene image.
+ * zh-CN: 物体投影四边形.
+ * @param matches Debug visualization image.
+ * zh-CN: 匹配连接图 (仅 debug 时生成).
+ */
+ public FeatureMatchingResult(@NonNull List pts, @Nullable List quad, @Nullable Mat matches) {
+ mPoints = pts;
+ mQuad = quad;
mMatches = matches;
}
+ @Nullable
public Mat getMatches() {
- return mMatches;
+ return mMatches != null ? ImageUtils.to8UC3(mMatches) : null;
}
+ @Nullable
+ public List getQuad() {
+ return mQuad;
+ }
+
+ @NonNull
public List getPoints() {
return mPoints;
}
@@ -260,8 +457,8 @@ public final class ImageFeatureMatching {
@Override
public boolean equals(Object o) {
return o == this || o instanceof FeatureMatchingResult featureMatchingResult
- && Objects.equals(mPoints, featureMatchingResult.mPoints)
- && Objects.equals(mMatches, featureMatchingResult.mMatches);
+ && Objects.equals(mPoints, featureMatchingResult.mPoints)
+ && Objects.equals(mMatches, featureMatchingResult.mMatches);
}
@Override
diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/augment/Augmentable.kt b/app/src/main/java/org/autojs/autojs/runtime/api/augment/Augmentable.kt
index ea681315..0dd5041f 100644
--- a/app/src/main/java/org/autojs/autojs/runtime/api/augment/Augmentable.kt
+++ b/app/src/main/java/org/autojs/autojs/runtime/api/augment/Augmentable.kt
@@ -435,8 +435,13 @@ abstract class Augmentable(private val scriptRuntime: ScriptRuntime? = null) : F
val message = globalContext.getString(R.string.error_failed_to_invoke_method_with_description, methodDescription)
val niceMessage = when (val errMsg = e.message) {
null -> message
- else -> "$message. ${errMsg.replaceFirst(Regex("^(Wrapped )?\\w*(\\.\\w+)*(Exception|Error): "), "")}"
+ else -> {
+ val refined = errMsg.replaceFirst(Regex("^(Wrapped )?\\w*(\\.\\w+)*(Exception|Error): "), "")
+ val trailingDot = if (refined.endsWith(".")) "" else "."
+ "$message. $refined$trailingDot\n$e"
+ }
}
+ e.printStackTrace()
when (e) {
is WrappedIllegalArgumentException -> {
// @Hint by SuperMonster003 on Oct 31, 2024.
diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/augment/images/Images.kt b/app/src/main/java/org/autojs/autojs/runtime/api/augment/images/Images.kt
index ddf2bcf9..35713f4c 100644
--- a/app/src/main/java/org/autojs/autojs/runtime/api/augment/images/Images.kt
+++ b/app/src/main/java/org/autojs/autojs/runtime/api/augment/images/Images.kt
@@ -3,6 +3,7 @@ package org.autojs.autojs.runtime.api.augment.images
import android.annotation.SuppressLint
import android.graphics.Bitmap
import android.graphics.BitmapFactory
+import android.util.Log
import android.view.Gravity
import org.autojs.autojs.annotation.RhinoRuntimeFunctionInterface
import org.autojs.autojs.core.image.ColorDetector
@@ -43,6 +44,7 @@ import org.mozilla.javascript.BaseFunction
import org.mozilla.javascript.NativeArray
import org.mozilla.javascript.NativeObject
import org.mozilla.javascript.ScriptableObject
+import org.opencv.core.CvType
import org.opencv.features2d.DescriptorMatcher
import org.opencv.imgproc.Imgproc
import java.io.ByteArrayOutputStream
@@ -52,9 +54,12 @@ import kotlin.collections.component3
import kotlin.collections.contains
import kotlin.math.floor
import kotlin.math.ln
+import kotlin.math.max
+import kotlin.math.min
import kotlin.math.pow
import kotlin.math.round
import kotlin.math.roundToInt
+import kotlin.math.sqrt
import android.graphics.Rect as AndroidRect
import org.autojs.autojs.core.opencv.Mat as AutoJsMat
import org.autojs.autojs.runtime.api.Images as ApiImages
@@ -147,6 +152,8 @@ class Images(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime), AsEmitt
@Suppress("MayBeConstant")
companion object {
+ private val TAG = Images::class.java.simpleName
+
@JvmField
val DEFAULT_COLOR_THRESHOLD = 4
@@ -1014,19 +1021,28 @@ class Images(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime), AsEmitt
require(objectFeatures is ImageFeatures) {
"Argument objectFeatures ${objectFeatures.jsBrief()} for images.matchFeatures must be a ImageFeatures"
}
- val matcher = opt.inquire("matcher") {
- coerceIntNumber(DescriptorMatcher::class.java.getField(coerceString(it)).get(null))
- } ?: DescriptorMatcher.FLANNBASED
+ val isObjectOrbAlike = objectFeatures.javaObject.descriptors.type() == CvType.CV_8U
+ val matcherType = opt.inquire("matcher") {
+ DescriptorMatcher::class.java.getField(coerceString(it)).get(null) as? Int
+ } ?: when (isObjectOrbAlike) {
+ /* For ORB, BRISK, AKAZE, etc. */
+ true -> DescriptorMatcher.BRUTEFORCE_HAMMING
+ /* For SIFT, SURF, etc. */
+ else -> DescriptorMatcher.FLANNBASED
+ }
val drawMatches = opt.inquire("drawMatches") { scriptRuntime.files.nonNullPath(coerceString(it)) }
- val threshold = opt.inquire("threshold", ::coerceFloatNumber, 0.7f)
+ val threshold = opt.inquire("threshold", ::coerceFloatNumber, if (isObjectOrbAlike) 0.8f else 0.7f)
- val result = ImageFeatureMatching.featureMatching(sceneFeatures.javaObject, objectFeatures.javaObject, matcher, drawMatches, threshold) ?: return@ensureArgumentsLengthInRange null
-
- val javaMatchesImage = result.matches
- val points = result.points
+ val result = ImageFeatureMatching.featureMatching(
+ /* sceneDesc = */ sceneFeatures.javaObject,
+ /* objectDesc = */ objectFeatures.javaObject,
+ /* matcherType = */ matcherType,
+ /* debugMatchesImagePath = */ drawMatches,
+ /* threshold = */ threshold,
+ ) ?: return@ensureArgumentsLengthInRange null
if (!drawMatches.isJsNullish()) {
- val matchesImage = javaMatchesImage?.let { matToImage(scriptRuntime, arrayOf(it)) }
+ val matchesImage = result.matches?.let { matToImage(scriptRuntime, arrayOf(it)) }
if (matchesImage != null) {
save(scriptRuntime, arrayOf(matchesImage, drawMatches, "jpg", 100))
matchesImage.recycle()
@@ -1035,17 +1051,16 @@ class Images(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime), AsEmitt
val region = sceneFeatures.region
val scale = sceneFeatures.scale
- val size = points.size
val offsetX = region.x
val offsetY = region.y
- (0 until size).forEach { i ->
- val point = points[i]
- point.x = offsetX + point.x / scale
- point.y = offsetY + point.y / scale
- }
+ val quad = result.quad ?: return@ensureArgumentsLengthInRange null
+ require(quad.size == 4) { "Quad size of feature matching result must be 4 instead of ${quad.size}" }
- ObjectFrame(points[0], points[1], points[3], points[2])
+ val (tl, tr, br, bl) = quad.map { p ->
+ OpencvPoint(p.x / scale + offsetX, p.y / scale + offsetY)
+ }
+ ObjectFrame(tl, tr, bl, br)
}
@JvmStatic
@@ -1439,10 +1454,7 @@ class Images(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime), AsEmitt
// @Reference to module __images__.js from Auto.js Pro 9.3.11 by SuperMonster003 on Dec 19. 2023.
private fun fillDetectAndComputeFeaturesOptions(rows: Int, cols: Int, options: NativeObject): DetectAndComputeFeaturesOptions {
- val scale = options.inquire("scale") { coerceFloatNumber(it) } ?: when {
- rows * cols >= 1e6 -> 0.5f
- else -> 1.0f
- }
+ val scale = options.inquire("scale") { coerceFloatNumber(it) } ?: calcScale(rows, cols)
val cvtColor = when {
options.inquire("grayscale", ::coerceBoolean, false) -> Imgproc.COLOR_RGBA2GRAY
else -> -1
@@ -1450,7 +1462,17 @@ class Images(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime), AsEmitt
val method = getDetectFeatureMethod(options.inquire("method", ::coerceString, "SIFT"))
val region = buildRegionInternal(options.prop("region"), cols, rows)
- return DetectAndComputeFeaturesOptions(scale, cvtColor, method, region)
+ return DetectAndComputeFeaturesOptions(scale.coerceIn(0f, 1f), cvtColor, method, region)
+ }
+
+ private fun calcScale(rows: Int, cols: Int, targetArea: Int = 1_000_000, maxSide: Int = 1600): Float {
+ val total = rows * cols
+ if (total < targetArea) return 1f
+ val scaleByArea = sqrt(targetArea.toDouble() / total).toFloat()
+ val scaleBySide = maxSide.toFloat() / max(rows, cols)
+ return min(scaleByArea, scaleBySide).also {
+ Log.d(TAG, "Calculated scale: $it")
+ }
}
private fun extractMatPair(scriptRuntime: ScriptRuntime, argList: Array, funcName: String): Pair {
diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/augment/images/ObjectFrame.kt b/app/src/main/java/org/autojs/autojs/runtime/api/augment/images/ObjectFrame.kt
index b02af8f4..fbe62680 100644
--- a/app/src/main/java/org/autojs/autojs/runtime/api/augment/images/ObjectFrame.kt
+++ b/app/src/main/java/org/autojs/autojs/runtime/api/augment/images/ObjectFrame.kt
@@ -1,5 +1,6 @@
package org.autojs.autojs.runtime.api.augment.images
+import org.autojs.autojs.util.StringUtils
import org.opencv.core.Point
// @Reference to module __images__.js from Auto.js Pro 9.3.11 by SuperMonster003 on Dec 19. 2023.
@@ -20,4 +21,17 @@ class ObjectFrame(
@JvmField
val center = Point(centerX, centerY)
+
+ override fun toString(): String {
+ return "[${ObjectFrame::class.java.simpleName}] ${summary()}"
+ }
+
+ fun summary(): String = listOf(
+ "topLeft" to { topLeft },
+ "topRight" to { topRight },
+ "bottomLeft" to { bottomLeft },
+ "bottomRight" to { bottomRight },
+ "center" to { center },
+ ).let { StringUtils.toFormattedSummary(it) }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/org/autojs/autojs/util/ImageUtils.kt b/app/src/main/java/org/autojs/autojs/util/ImageUtils.kt
index de2e9cfe..0c805ee6 100644
--- a/app/src/main/java/org/autojs/autojs/util/ImageUtils.kt
+++ b/app/src/main/java/org/autojs/autojs/util/ImageUtils.kt
@@ -1,13 +1,17 @@
package org.autojs.autojs.util
import android.graphics.BitmapFactory
+import org.opencv.core.Core
+import org.opencv.core.CvType
+import org.opencv.core.Mat
+import org.opencv.imgproc.Imgproc
object ImageUtils {
@JvmStatic
fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
- // Raw height and width of image
+ // Raw height and width of image (zh-CN: 图像的原始高度和宽度)
val height = options.outHeight
val width = options.outWidth
@@ -19,6 +23,7 @@ object ImageUtils {
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
+ // zh-CN: 计算满足所需高度和宽度的2的幂次方的最大采样率.
while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
inSampleSize *= 2
}
@@ -26,4 +31,43 @@ object ImageUtils {
return inSampleSize
}
+ @JvmStatic
+ fun Mat.to8UC3(): Mat {
+ val src = this
+
+ // Convert bit depth to 8 bit (zh-CN: 位深转换为 8 位)
+ var tmp8 = Mat()
+ // e.g. CV_16U, CV_32F, etc (zh-CN: 例如 CV_16U, CV_32F 等)
+ val depth = src.depth()
+
+ if (depth != CvType.CV_8U) {
+ // Automatically calculate alpha/beta to map results to 0-255.
+ // zh-CN: 自动计算 alpha/beta, 使结果映射到 0-255.
+ val mm = Core.minMaxLoc(src)
+ val minV = mm.minVal
+ val maxV = mm.maxVal
+ // Avoid division by zero (zh-CN: 避免除以 0)
+ val alpha = if ((maxV - minV) < 1e-5) 1.0 else 255.0 / (maxV - minV)
+ val beta = -minV * alpha
+
+ // tmp8 is now 8-bit (zh-CN: 此时的 tmp8 为 8 位)
+ src.convertTo(tmp8, CvType.CV_8U, alpha, beta)
+ } else {
+ // Already 8-bit, continue using (zh-CN: 已经是 8 位, 继续使用)
+ tmp8 = src
+ }
+
+ // Convert channel count to 3 (zh-CN: 通道数转换为 3)
+ val ch = tmp8.channels()
+ var dst = Mat()
+
+ when (ch) {
+ 1 -> Imgproc.cvtColor(tmp8, dst, Imgproc.COLOR_GRAY2BGR)
+ 3 -> dst = tmp8.clone()
+ 4 -> Imgproc.cvtColor(tmp8, dst, Imgproc.COLOR_BGRA2BGR)
+ else -> throw IllegalArgumentException("Unsupported channel count: $ch")
+ }
+ return dst
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/org/autojs/autojs/util/StringUtils.kt b/app/src/main/java/org/autojs/autojs/util/StringUtils.kt
index ca719c39..f1a99d84 100644
--- a/app/src/main/java/org/autojs/autojs/util/StringUtils.kt
+++ b/app/src/main/java/org/autojs/autojs/util/StringUtils.kt
@@ -5,6 +5,8 @@ import android.text.TextUtils
import org.autojs.autojs.annotation.LocaleNonRelated
import org.autojs.autojs.app.GlobalAppContext
import org.autojs.autojs.core.pref.Language
+import org.autojs.autojs.extension.NumberExtensions.roundToString
+import org.opencv.core.Point
import java.util.Locale
import kotlin.math.min
import kotlin.math.pow
@@ -204,4 +206,28 @@ object StringUtils {
@Deprecated("Deprecated since v6.6.0", ReplaceWith("uppercaseFirstChar(s)"))
fun toUpperCaseFirst(s: String) = uppercaseFirstChar(s)
+ @JvmStatic
+ fun toFormattedSummary(dataList: List Any?>>): String {
+ val separatorLv0 = "\n"
+ val separatorLv1 = "$separatorLv0 "
+ val separatorLv2 = "$separatorLv1 "
+
+ return dataList.joinToString(prefix = "{$separatorLv1", separator = separatorLv1, postfix = "$separatorLv0}") { (name, action) ->
+ val value = when (val actionResult = action()) {
+ is CharSequence -> "\"$actionResult\""
+ is Iterable<*> -> actionResult.joinToString(prefix = "[$separatorLv2", separator = separatorLv2, postfix = "$separatorLv1]")
+ is Array<*> -> actionResult.joinToString(prefix = "[$separatorLv2", separator = separatorLv2, postfix = "$separatorLv1]")
+ is Point -> actionResult.toFormattedPointString(0)
+ else -> actionResult
+ }
+ "$name=$value"
+ }
+ }
+
+ @JvmStatic
+ @JvmOverloads
+ fun Point.toFormattedPointString(scale: Int = 0): String {
+ return "{${x.roundToString(scale)}, ${y.roundToString(scale)}}"
+ }
+
}
\ No newline at end of file
diff --git a/version.properties b/version.properties
index 217f7468..d7385618 100644
--- a/version.properties
+++ b/version.properties
@@ -1,7 +1,7 @@
-#Tue May 13 20:15:49 CST 2025
-BUILD_TIME=1747138549830
+#Wed May 14 23:13:52 CST 2025
+BUILD_TIME=1747235632440
COMPILE_SDK_VERSION=35
-JAVA_VERSION=23
+JAVA_VERSION=24
JAVA_VERSION_MIN_RADICAL=0
JAVA_VERSION_MIN_SUGGESTED=19
JAVA_VERSION_MIN_SUPPORTED=17
@@ -17,6 +17,6 @@ RAPID_OCR_OPENCV_MOBILE_LABEL_VERSION=13
RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3
TARGET_SDK_VERSION=35
TARGET_SDK_VERSION_INRT=29
-VERSION_BUILD=3217
+VERSION_BUILD=3220
VERSION_NAME=6.6.3 Alpha4
VSCODE_EXT_REQUIRED_VERSION=1.0.8