6.7.0 - Alpha - 修复 images 模块相关方法出现异常时 oneShot 标记功能失效的问题 (issue #372)
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
{
|
||||
"$data": {
|
||||
"v6.7.0": {
|
||||
"released_date": "2025/06/08",
|
||||
"released_date": "2025/06/10",
|
||||
"feature": [
|
||||
"zip 模块, 用于文件压缩与解压缩操作 (Ref to [Auto.js Pro](https://g.pro.autojs.org/)) (参阅 项目文档 > [Zip](https://docs.autojs6.com/#/zip))"
|
||||
],
|
||||
"fix": [
|
||||
"使用 XML 语法将 JavaScript 表达式作为属性值时, this 对象可能出现指向错误的问题",
|
||||
"调用 images.requestScreenCapture 时用户取消授权可能导致应用崩溃的问题",
|
||||
"images 部分相关方法出现异常时 oneShot 标记功能失效的问题 _[`issue #372`](http://issues.autojs6.com/372)_",
|
||||
"版本历史页面部分系统因字体差别导致统计数据显示不完整的问题"
|
||||
],
|
||||
"improvement": [],
|
||||
|
||||
@@ -32,7 +32,7 @@ import java.util.concurrent.atomic.AtomicLong
|
||||
* Transformed by SuperMonster003 on May 16, 2023.
|
||||
*/
|
||||
// @Reference to Auto.js Pro 9.3.11 by SuperMonster003 on Dec 20, 2023.
|
||||
open class ImageWrapper : Recyclable, MonitorResource {
|
||||
open class ImageWrapper : Shootable<ImageWrapper>, MonitorResource {
|
||||
|
||||
private var mScriptRuntime: ScriptRuntime
|
||||
|
||||
@@ -62,36 +62,44 @@ open class ImageWrapper : Recyclable, MonitorResource {
|
||||
val size
|
||||
get() = Size(mWidth.toDouble(), mHeight.toDouble()).also { ensureNotRecycled() }
|
||||
|
||||
val bitmap by lazy {
|
||||
ensureNotRecycled()
|
||||
if (mBitmap == null) {
|
||||
if (mMat != null) {
|
||||
mBitmap = createBitmap(mMat!!.width(), mMat!!.height())
|
||||
Utils.matToBitmap(mMat, mBitmap)
|
||||
} else {
|
||||
mBitmap = mediaImage?.let { toBitmap(it) }
|
||||
val bitmap: Bitmap
|
||||
get() {
|
||||
ensureNotRecycled()
|
||||
if (mBitmap == null) {
|
||||
val mat = mMat
|
||||
if (mat != null) {
|
||||
val bitmap = createBitmap(mat.width(), mat.height()).also { mBitmap = it }
|
||||
Utils.matToBitmap(mat, bitmap)
|
||||
} else {
|
||||
val mediaImage = mediaImage
|
||||
if (mediaImage != null) {
|
||||
mBitmap = toBitmap(mediaImage)
|
||||
}
|
||||
}
|
||||
}
|
||||
return mBitmap ?: throw Exception("Bitmap of ImageWrapper should never be null")
|
||||
}
|
||||
return@lazy mBitmap ?: throw Exception("Bitmap of ImageWrapper should never be null")
|
||||
}
|
||||
|
||||
val mat by lazy {
|
||||
ensureNotRecycled()
|
||||
if (mMat != null) {
|
||||
return@lazy mMat!!
|
||||
val mat: Mat
|
||||
get() {
|
||||
ensureNotRecycled()
|
||||
val mat = mMat
|
||||
if (mat != null) {
|
||||
return mat
|
||||
}
|
||||
val bitmap = mBitmap
|
||||
if (bitmap != null) {
|
||||
val newMat = Mat().also { mMat = it }
|
||||
Utils.bitmapToMat(bitmap, newMat)
|
||||
return newMat
|
||||
}
|
||||
if (mediaImage != null) {
|
||||
val plane = plane ?: throw AssertionError("Image plain is null")
|
||||
plane.buffer.position(0)
|
||||
return Mat(mHeight, mWidth, CvType.CV_8UC4, plane.buffer, plane.rowStride.toLong()).also { mMat = it }
|
||||
}
|
||||
throw AssertionError("Both bitmap and image are null")
|
||||
}
|
||||
if (mBitmap != null) {
|
||||
mMat = Mat()
|
||||
Utils.bitmapToMat(mBitmap, mMat)
|
||||
return@lazy mMat!!
|
||||
}
|
||||
if (mediaImage != null) {
|
||||
val plane = plane ?: throw AssertionError("Image plain is null")
|
||||
plane.buffer.position(0)
|
||||
return@lazy Mat(mHeight, mWidth, CvType.CV_8UC4, plane.buffer, plane.rowStride.toLong()).also { mMat = it }
|
||||
}
|
||||
throw AssertionError("Both bitmap and image are null")
|
||||
}
|
||||
|
||||
val bgrMat
|
||||
get() = Mat().also {
|
||||
@@ -308,7 +316,7 @@ open class ImageWrapper : Recyclable, MonitorResource {
|
||||
fun recycleAll() {
|
||||
imageList.forEach {
|
||||
when (val o = it.get()) {
|
||||
is Recyclable -> o.recycle()
|
||||
is Shootable<*> -> o.recycle()
|
||||
is Bitmap -> o.recycle()
|
||||
is org.opencv.core.Mat -> OpenCVHelper.release(o)
|
||||
is Image -> o.close()
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package org.autojs.autojs.core.image;
|
||||
|
||||
public interface Recyclable {
|
||||
public interface Shootable<T> {
|
||||
|
||||
void recycle();
|
||||
|
||||
boolean isRecycled();
|
||||
|
||||
ImageWrapper setOneShot(boolean b);
|
||||
T setOneShot(boolean b);
|
||||
|
||||
default ImageWrapper oneShot() {
|
||||
default T oneShot() {
|
||||
return setOneShot(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.autojs.autojs.annotation.ScriptVariable;
|
||||
import org.autojs.autojs.concurrent.VolatileDispose;
|
||||
import org.autojs.autojs.core.image.CapturedImage;
|
||||
import org.autojs.autojs.core.image.ImageWrapper;
|
||||
import org.autojs.autojs.core.image.Shootable;
|
||||
import org.autojs.autojs.core.image.RhinoColorFinder;
|
||||
import org.autojs.autojs.core.image.TemplateMatching;
|
||||
import org.autojs.autojs.core.image.capture.ScreenCaptureRequester;
|
||||
@@ -56,6 +57,7 @@ import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
import static org.autojs.autojs.util.RhinoUtils.isMainThread;
|
||||
@@ -153,12 +155,22 @@ public class Images {
|
||||
if (image == null) {
|
||||
throw new NullPointerException(str(R.string.error_method_called_with_null_argument, "Images.pixel", "image"));
|
||||
}
|
||||
int pixel = image.pixel(x, y);
|
||||
image.shoot();
|
||||
return pixel;
|
||||
try {
|
||||
return image.pixel(x, y);
|
||||
} finally {
|
||||
shoot(image);
|
||||
}
|
||||
}
|
||||
|
||||
public static ImageWrapper concat(ScriptRuntime scriptRuntime, ImageWrapper imgA, ImageWrapper imgB, int direction) {
|
||||
try {
|
||||
return concatInternal(scriptRuntime, imgA, imgB, direction);
|
||||
} finally {
|
||||
shoot(imgA, imgB);
|
||||
}
|
||||
}
|
||||
|
||||
private static ImageWrapper concatInternal(ScriptRuntime scriptRuntime, ImageWrapper imgA, ImageWrapper imgB, int direction) {
|
||||
if (!Arrays.asList(Gravity.START, Gravity.END, Gravity.TOP, Gravity.BOTTOM).contains(direction)) {
|
||||
throw new IllegalArgumentException(str(R.string.error_illegal_argument, "direction", direction));
|
||||
}
|
||||
@@ -186,8 +198,6 @@ public class Images {
|
||||
canvas.drawBitmap(imgA.getBitmap(), (float) (width - imgA.getWidth()) / 2, 0, paint);
|
||||
canvas.drawBitmap(imgB.getBitmap(), (float) (width - imgB.getWidth()) / 2, imgA.getHeight(), paint);
|
||||
}
|
||||
imgA.shoot();
|
||||
imgB.shoot();
|
||||
return ImageWrapper.ofBitmap(scriptRuntime, bitmap);
|
||||
}
|
||||
|
||||
@@ -279,12 +289,22 @@ public class Images {
|
||||
}
|
||||
|
||||
public ImageWrapper copy(@NonNull ImageWrapper image) {
|
||||
ImageWrapper imageWrapper = image.clone();
|
||||
image.shoot();
|
||||
return imageWrapper;
|
||||
try {
|
||||
return image.clone();
|
||||
} finally {
|
||||
shoot(image);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean save(@NonNull ImageWrapper image, @NonNull String path, @NonNull String format, int quality) throws IOException {
|
||||
try {
|
||||
return saveInternal(image, path, format, quality);
|
||||
} finally {
|
||||
shoot(image);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean saveInternal(@NonNull ImageWrapper image, @NonNull String path, @NonNull String format, int quality) throws IOException {
|
||||
Bitmap bitmap = image.getBitmap();
|
||||
Bitmap.CompressFormat compressFormat = parseImageFormat(format);
|
||||
|
||||
@@ -306,19 +326,24 @@ public class Images {
|
||||
}
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(path)) {
|
||||
boolean b = bitmap.compress(compressFormat, quality, fos);
|
||||
image.shoot();
|
||||
return b;
|
||||
return bitmap.compress(compressFormat, quality, fos);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] compressToBytes(@NotNull ImageWrapper image, @NotNull String format, int quality) {
|
||||
try {
|
||||
return compressToBytesInternal(image, format, quality);
|
||||
} finally {
|
||||
shoot(image);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] compressToBytesInternal(@NotNull ImageWrapper image, @NotNull String format, int quality) {
|
||||
Bitmap bitmap = image.getBitmap();
|
||||
Bitmap.CompressFormat compressFormat = parseImageFormat(format);
|
||||
|
||||
if (compressFormat == Bitmap.CompressFormat.PNG && quality != 100) {
|
||||
byte[] compressed = PngQuantBridge.quantize(bitmap, quality);
|
||||
image.shoot();
|
||||
if (compressed != null) {
|
||||
return compressed;
|
||||
}
|
||||
@@ -327,14 +352,12 @@ public class Images {
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
if (compressFormat == Bitmap.CompressFormat.WEBP_LOSSLESS && quality != 100) {
|
||||
image.shoot();
|
||||
throw new IllegalArgumentException(mContext.getString(R.string.error_webp_lossless_quality_not_supported));
|
||||
}
|
||||
}
|
||||
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
bitmap.compress(compressFormat, quality, outputStream);
|
||||
image.shoot();
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
@@ -352,17 +375,31 @@ public class Images {
|
||||
releaseScreenCapturer();
|
||||
}
|
||||
|
||||
public ImageWrapper rotate(@NonNull ImageWrapper img, float x, float y, float degree) {
|
||||
public ImageWrapper rotate(@NonNull ImageWrapper image, float x, float y, float degree) {
|
||||
try {
|
||||
return rotateInternal(image, x, y, degree);
|
||||
} finally {
|
||||
shoot(image);
|
||||
}
|
||||
}
|
||||
|
||||
private ImageWrapper rotateInternal(@NonNull ImageWrapper image, float x, float y, float degree) {
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.postRotate(degree, x, y);
|
||||
ImageWrapper imageWrapper = ImageWrapper.ofBitmap(mScriptRuntime, Bitmap.createBitmap(img.getBitmap(), 0, 0, img.getWidth(), img.getHeight(), matrix, true));
|
||||
img.shoot();
|
||||
return imageWrapper;
|
||||
return ImageWrapper.ofBitmap(mScriptRuntime, Bitmap.createBitmap(image.getBitmap(), 0, 0, image.getWidth(), image.getHeight(), matrix, true));
|
||||
}
|
||||
|
||||
@ScriptInterface
|
||||
public ImageWrapper flip(@NonNull ImageWrapper img, boolean horizontal, boolean vertical) {
|
||||
Bitmap original = img.getBitmap();
|
||||
public ImageWrapper flip(@NonNull ImageWrapper image, boolean horizontal, boolean vertical) {
|
||||
try {
|
||||
return flipInternal(image, horizontal, vertical);
|
||||
} finally {
|
||||
shoot(image);
|
||||
}
|
||||
}
|
||||
|
||||
private ImageWrapper flipInternal(@NonNull ImageWrapper image, boolean horizontal, boolean vertical) {
|
||||
Bitmap original = image.getBitmap();
|
||||
Matrix matrix = new Matrix();
|
||||
|
||||
// Set scaling ratio according to input parameters.
|
||||
@@ -380,14 +417,19 @@ public class Images {
|
||||
if (vertical) matrix.postTranslate(0, original.getHeight());
|
||||
|
||||
Bitmap flipped = Bitmap.createBitmap(original, 0, 0, original.getWidth(), original.getHeight(), matrix, true);
|
||||
img.shoot();
|
||||
return ImageWrapper.ofBitmap(mScriptRuntime, flipped);
|
||||
}
|
||||
|
||||
public ImageWrapper clip(@NonNull ImageWrapper img, int x, int y, int w, int h) {
|
||||
ImageWrapper imageWrapper = ImageWrapper.ofBitmap(mScriptRuntime, Bitmap.createBitmap(img.getBitmap(), x, y, w, h));
|
||||
img.shoot();
|
||||
return imageWrapper;
|
||||
public ImageWrapper clip(@NonNull ImageWrapper image, int x, int y, int w, int h) {
|
||||
try {
|
||||
return clipInternal(image, x, y, w, h);
|
||||
} finally {
|
||||
shoot(image);
|
||||
}
|
||||
}
|
||||
|
||||
private ImageWrapper clipInternal(@NonNull ImageWrapper image, int x, int y, int w, int h) {
|
||||
return ImageWrapper.ofBitmap(mScriptRuntime, Bitmap.createBitmap(image.getBitmap(), x, y, w, h));
|
||||
}
|
||||
|
||||
public ImageWrapper read(String path) {
|
||||
@@ -416,25 +458,32 @@ public class Images {
|
||||
return ImageWrapper.ofBitmap(mScriptRuntime, Drawables.loadBase64Data(data));
|
||||
}
|
||||
|
||||
public String toBase64(ImageWrapper img, String format, int quality) {
|
||||
byte[] input = toBytes(img, format, quality);
|
||||
img.shoot();
|
||||
return Base64.encodeToString(input, Base64.NO_WRAP);
|
||||
public String toBase64(ImageWrapper image, String format, int quality) {
|
||||
try {
|
||||
byte[] input = toBytes(image, format, quality);
|
||||
return Base64.encodeToString(input, Base64.NO_WRAP);
|
||||
} finally {
|
||||
shoot(image);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] toBytes(@NonNull ImageWrapper img, String format, int quality) {
|
||||
Bitmap.CompressFormat compressFormat = parseImageFormat(format);
|
||||
Bitmap bitmap = img.getBitmap();
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
bitmap.compress(compressFormat, quality, outputStream);
|
||||
img.shoot();
|
||||
return outputStream.toByteArray();
|
||||
public byte[] toBytes(@NonNull ImageWrapper image, String format, int quality) {
|
||||
try {
|
||||
Bitmap.CompressFormat compressFormat = parseImageFormat(format);
|
||||
Bitmap bitmap = image.getBitmap();
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
bitmap.compress(compressFormat, quality, outputStream);
|
||||
return outputStream.toByteArray();
|
||||
} finally {
|
||||
shoot(image);
|
||||
}
|
||||
}
|
||||
|
||||
public ImageWrapper fromBytes(byte[] bytes) throws BitmapUtils.DecodeException {
|
||||
return ImageWrapper.ofBitmap(mScriptRuntime, BitmapUtils.bitmapFromByteArrayOrThrow(bytes));
|
||||
}
|
||||
|
||||
/** @noinspection deprecation */
|
||||
private Bitmap.CompressFormat parseImageFormat(String format) {
|
||||
return switch (format.toLowerCase(Language.getPrefLanguage().getLocale())) {
|
||||
case "png" -> Bitmap.CompressFormat.PNG;
|
||||
@@ -468,6 +517,14 @@ public class Images {
|
||||
}
|
||||
|
||||
public ImageWrapper invert(@NonNull ImageWrapper image) {
|
||||
try {
|
||||
return invertInternal(image);
|
||||
} finally {
|
||||
shoot(image);
|
||||
}
|
||||
}
|
||||
|
||||
private ImageWrapper invertInternal(@NonNull ImageWrapper image) {
|
||||
initOpenCvIfNeeded();
|
||||
|
||||
Bitmap originalBitmap = image.getBitmap();
|
||||
@@ -503,8 +560,6 @@ public class Images {
|
||||
Bitmap invertedBitmap = Bitmap.createBitmap(originalWidth, originalHeight, Bitmap.Config.ARGB_8888);
|
||||
Utils.matToBitmap(destMat, invertedBitmap);
|
||||
|
||||
image.shoot();
|
||||
|
||||
return ImageWrapper.ofBitmap(mScriptRuntime, invertedBitmap);
|
||||
}
|
||||
|
||||
@@ -557,6 +612,14 @@ public class Images {
|
||||
@Nullable
|
||||
@ScriptInterface
|
||||
public Point findImage(ImageWrapper image, ImageWrapper template, float weakThreshold, float strictThreshold, Rect rect, int maxLevel) throws Exception {
|
||||
try {
|
||||
return findImageInternal(image, template, weakThreshold, strictThreshold, rect, maxLevel);
|
||||
} finally {
|
||||
shoot(image, template);
|
||||
}
|
||||
}
|
||||
|
||||
private Point findImageInternal(ImageWrapper image, ImageWrapper template, float weakThreshold, float strictThreshold, Rect rect, int maxLevel) throws Exception {
|
||||
initOpenCvIfNeeded();
|
||||
if (image == null) {
|
||||
throw new NullPointerException(mContext.getString(R.string.error_method_called_with_null_argument, "Images.findImage", "image"));
|
||||
@@ -588,8 +651,6 @@ public class Images {
|
||||
if (shouldReleaseMat) {
|
||||
OpenCVHelper.release(src);
|
||||
}
|
||||
image.shoot();
|
||||
template.shoot();
|
||||
}
|
||||
if (point != null) {
|
||||
if (rect != null) {
|
||||
@@ -603,6 +664,14 @@ public class Images {
|
||||
}
|
||||
|
||||
public List<TemplateMatching.Match> matchTemplate(ImageWrapper image, ImageWrapper template, float weakThreshold, float strictThreshold, Rect rect, int maxLevel, int limit, boolean useTransparentMask) {
|
||||
try {
|
||||
return matchTemplateInternal(image, template, weakThreshold, strictThreshold, rect, maxLevel, limit, useTransparentMask);
|
||||
} finally {
|
||||
shoot(image, template);
|
||||
}
|
||||
}
|
||||
|
||||
private List<TemplateMatching.Match> matchTemplateInternal(ImageWrapper image, ImageWrapper template, float weakThreshold, float strictThreshold, Rect rect, int maxLevel, int limit, boolean useTransparentMask) {
|
||||
initOpenCvIfNeeded();
|
||||
if (image == null) {
|
||||
throw new NullPointerException(mContext.getString(R.string.error_method_called_with_null_argument, "Images.matchTemplate", "image"));
|
||||
@@ -623,8 +692,6 @@ public class Images {
|
||||
if (src != image.getMat()) {
|
||||
OpenCVHelper.release(src);
|
||||
}
|
||||
image.shoot();
|
||||
template.shoot();
|
||||
|
||||
for (TemplateMatching.Match match : result) {
|
||||
Point point = match.point;
|
||||
@@ -669,4 +736,8 @@ public class Images {
|
||||
}
|
||||
}
|
||||
|
||||
public static void shoot(Shootable<?>... shootableArgs) {
|
||||
Arrays.stream(shootableArgs).filter(Objects::nonNull).forEach(Shootable::shoot);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.autojs.autojs.runtime.api.augment.images
|
||||
|
||||
import org.autojs.autojs.core.image.Shootable
|
||||
import org.autojs.autojs.runtime.api.ImageFeatureMatching
|
||||
import org.autojs.autojs.util.RhinoUtils
|
||||
import org.mozilla.javascript.Undefined
|
||||
@@ -9,15 +10,28 @@ class ImageFeatures(
|
||||
@JvmField var javaObject: ImageFeatureMatching.FeatureMatchingDescriptor,
|
||||
@JvmField var scale: Float,
|
||||
@JvmField var region: Rect,
|
||||
) {
|
||||
) : Shootable<ImageFeatures> {
|
||||
|
||||
private var mIsOneShot = false
|
||||
|
||||
@JvmField
|
||||
var recycled = false
|
||||
|
||||
override fun isRecycled() = recycled
|
||||
|
||||
override fun setOneShot(b: Boolean): ImageFeatures {
|
||||
mIsOneShot = b
|
||||
return this
|
||||
}
|
||||
|
||||
override fun shoot() {
|
||||
if (mIsOneShot) recycle()
|
||||
}
|
||||
|
||||
@JvmField
|
||||
var onRecycled: ((ImageFeatures) -> Undefined) = { _: ImageFeatures -> RhinoUtils.UNDEFINED }
|
||||
|
||||
fun recycle() {
|
||||
override fun recycle() {
|
||||
if (!recycled) {
|
||||
javaObject.release()
|
||||
onRecycled.invoke(this)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
#Mon Jun 09 19:43:01 CST 2025
|
||||
BUILD_TIME=1749469381134
|
||||
#Tue Jun 10 18:30:41 CST 2025
|
||||
BUILD_TIME=1749551441206
|
||||
COMPILE_SDK_VERSION=35
|
||||
IMAGE_QUANT_CMAKE_VERSION=3.22.1
|
||||
IMAGE_QUANT_NDK_VERSION=26.1.10909125
|
||||
@@ -19,6 +19,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=3286
|
||||
VERSION_BUILD=3289
|
||||
VERSION_NAME=6.7.0 Alpha
|
||||
VSCODE_EXT_REQUIRED_VERSION=1.0.8
|
||||
|
||||
Reference in New Issue
Block a user