From b3d21e8db7818c1024a9a006c02793e89da01b01 Mon Sep 17 00:00:00 2001 From: TonyJiangWJ Date: Thu, 13 Jun 2024 23:56:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A9=E7=94=A8opencv=E5=A4=84=E7=90=86?= =?UTF-8?q?=E6=88=AA=E5=9B=BE=E5=9B=BE=E7=89=87=EF=BC=8C=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=88=AA=E5=9B=BE=E8=BF=94=E5=9B=9E=E9=80=9F=E5=BA=A630+ms=20?= =?UTF-8?q?=3D>=2010+ms?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../autojs/core/image/ImageWrapper.java | 49 +++++++++++++++- .../image/capture/GlobalScreenCapture.java | 56 ++++++++++++++++--- 2 files changed, 95 insertions(+), 10 deletions(-) diff --git a/autojs/src/main/java/com/stardust/autojs/core/image/ImageWrapper.java b/autojs/src/main/java/com/stardust/autojs/core/image/ImageWrapper.java index 42265d5d..d443dd60 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/image/ImageWrapper.java +++ b/autojs/src/main/java/com/stardust/autojs/core/image/ImageWrapper.java @@ -11,6 +11,8 @@ import com.stardust.autojs.core.opencv.OpenCVHelper; import com.stardust.pio.UncheckedIOException; import org.opencv.android.Utils; +import org.opencv.core.CvType; +import org.opencv.core.Rect; import org.opencv.imgcodecs.Imgcodecs; import java.io.FileNotFoundException; @@ -61,7 +63,11 @@ public class ImageWrapper { if (image == null) { return null; } - return new ImageWrapper(toBitmap(image)); + if (OpenCVHelper.isInitialized()) { + return ofImageByMat(image); + } else { + return new ImageWrapper(toBitmap(image)); + } } public static ImageWrapper ofMat(Mat mat) { @@ -100,6 +106,47 @@ public class ImageWrapper { return Bitmap.createBitmap(bitmap, 0, 0, image.getWidth(), image.getHeight()); } + public static ImageWrapper ofImageByMat(Image image) { + + long start = System.currentTimeMillis(); + + // 获取Image的平面 + Image.Plane[] planes = image.getPlanes(); + Log.d("ImageWrapper", "ofImageByMat: planes.length: " + planes.length); + // 获取Image的宽高 + int width = image.getWidth(); + int height = image.getHeight(); + + Log.d("ImageWrapper", "ofImageByMat: width:" + width + " height:" + height); + Image.Plane plane = planes[0]; + // 获取平面的数据缓冲区 + ByteBuffer buffer = planes[0].getBuffer(); + // 创建一个byte数组来存储缓冲区的数据 + byte[] bytes = new byte[buffer.remaining()]; + buffer.position(0); + int pixelStride = plane.getPixelStride(); + int rowPadding = plane.getRowStride() - pixelStride * image.getWidth(); + // 将数据从缓冲区拷贝到byte数组 + buffer.get(bytes); + + // 创建一个Mat对象 + Mat mat = new Mat(height, width + rowPadding / pixelStride, CvType.CV_8UC4); + // 将byte数组拷贝到Mat对象 + mat.put(0, 0, bytes); + if (width != mat.width()) { + Log.d("ImageWrapper", "ofImageByMat: mat width is not valid: " + mat.width() + " => " + width); + // 定义裁切区域 + Rect rect = new Rect(0, 0, width, height); + // 裁切图像 + Mat croppedImage = new Mat(mat, rect); + mat.release(); + mat = croppedImage; + } + Log.d("ImageWrapper", "ofImageByMat: create by mat cost: " + (System.currentTimeMillis() - start) + "ms"); + return new ImageWrapper(mat); + + } + public int getWidth() { ensureNotRecycled(); return mWidth; diff --git a/autojs/src/main/java/com/stardust/autojs/core/image/capture/GlobalScreenCapture.java b/autojs/src/main/java/com/stardust/autojs/core/image/capture/GlobalScreenCapture.java index 1ed2e8ff..5c8584d2 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/image/capture/GlobalScreenCapture.java +++ b/autojs/src/main/java/com/stardust/autojs/core/image/capture/GlobalScreenCapture.java @@ -29,6 +29,8 @@ import org.mozilla.javascript.ast.Loop; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; import androidx.annotation.Nullable; @@ -64,6 +66,10 @@ public class GlobalScreenCapture { private boolean hasPermission; private boolean noRegister; + private ReentrantLock captureLock = new ReentrantLock(); + + private Condition captureComplete = captureLock.newCondition(); + @SuppressLint("StaticFieldLeak") private static volatile GlobalScreenCapture INSTANCE; @@ -234,11 +240,17 @@ public class GlobalScreenCapture { if (noRegister) { return; } - Image oldCacheImage = mCachedImage.getAndSet(null); - if (oldCacheImage != null) { - oldCacheImage.close(); + captureLock.lock(); + try { + Image oldCacheImage = mCachedImage.getAndSet(null); + if (oldCacheImage != null) { + oldCacheImage.close(); + } + mCachedImage.set(reader.acquireLatestImage()); + captureComplete.signal(); + } finally { + captureLock.unlock(); } - mCachedImage.set(reader.acquireLatestImage()); } catch (Exception e) { mException = e; } @@ -257,13 +269,26 @@ public class GlobalScreenCapture { long startTime = System.currentTimeMillis(); int retryLimit = 5; while (!thread.isInterrupted()) { - Image cachedImage = mCachedImage.getAndSet(null); + Image cachedImage = getCachedImage(); if (cachedImage != null) { - if (mUnderUsingImage != null) { - mUnderUsingImage.close(); - } - mUnderUsingImage = cachedImage; return cachedImage; + } else { + Log.d(TAG, "capture: 加锁等待获取截图"); + long waitStart = System.currentTimeMillis(); + captureLock.lock(); + try { + captureComplete.await(); + Log.d(TAG, "capture: 获取到截图信号,等待耗时:" + (System.currentTimeMillis() - waitStart) + "ms"); + cachedImage = getCachedImage(); + if (cachedImage != null) { + return cachedImage; + } + Log.d(TAG, "capture: 获取到截图信号,但是图片已经被其他脚本获取 重新获取"); + } catch (InterruptedException ex) { + throw new ScriptInterruptedException(); + } finally { + captureLock.unlock(); + } } if (System.currentTimeMillis() - startTime > 1000) { startTime = System.currentTimeMillis(); @@ -275,10 +300,23 @@ public class GlobalScreenCapture { break; } } + } throw new ScriptInterruptedException(); } + private Image getCachedImage() { + Image cachedImage = mCachedImage.getAndSet(null); + if (cachedImage != null) { + if (mUnderUsingImage != null) { + mUnderUsingImage.close(); + } + mUnderUsingImage = cachedImage; + return cachedImage; + } + return null; + } + public synchronized void unregister(ScriptRuntime runtime) { Log.d(TAG, "unregister: " + runtime); registeredRuntimes.remove(runtime);