1
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array/>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array/>
|
||||
<key>NSPrivacyTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyTrackingDomains</key>
|
||||
<array/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,212 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>files</key>
|
||||
<dict>
|
||||
<key>Headers/unimoduleXMlkitScannigU-Swift.h</key>
|
||||
<data>
|
||||
rrVnYx9RgfpqdbbblROMvU/4Unk=
|
||||
</data>
|
||||
<key>Headers/unimoduleXMlkitScannigU.h</key>
|
||||
<data>
|
||||
FH2SRnQAHIO8AbDZPa5/Bz/CcdA=
|
||||
</data>
|
||||
<key>Info.plist</key>
|
||||
<data>
|
||||
oggkQO8+5ChK5UkvRJRHpMoSBFI=
|
||||
</data>
|
||||
<key>Modules/module.modulemap</key>
|
||||
<data>
|
||||
wKuVddM+8I2G/8RcOmW6s0wX31M=
|
||||
</data>
|
||||
<key>Modules/unimoduleXMlkitScannigU.swiftmodule/Project/x86_64-apple-ios-simulator.swiftsourceinfo</key>
|
||||
<data>
|
||||
jtqbS2Trq3D8rtb92vby7+bs5o4=
|
||||
</data>
|
||||
<key>Modules/unimoduleXMlkitScannigU.swiftmodule/x86_64-apple-ios-simulator.abi.json</key>
|
||||
<data>
|
||||
aL7ncI5o4KD4BuCR7PRaOaVMe74=
|
||||
</data>
|
||||
<key>Modules/unimoduleXMlkitScannigU.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface</key>
|
||||
<data>
|
||||
3tANMXD9JTgGV6RX7TRRcoYtUFA=
|
||||
</data>
|
||||
<key>Modules/unimoduleXMlkitScannigU.swiftmodule/x86_64-apple-ios-simulator.swiftdoc</key>
|
||||
<data>
|
||||
V0pmF7rnAzF9xczsp8QRRW6oedw=
|
||||
</data>
|
||||
<key>Modules/unimoduleXMlkitScannigU.swiftmodule/x86_64-apple-ios-simulator.swiftinterface</key>
|
||||
<data>
|
||||
3tANMXD9JTgGV6RX7TRRcoYtUFA=
|
||||
</data>
|
||||
<key>Modules/unimoduleXMlkitScannigU.swiftmodule/x86_64-apple-ios-simulator.swiftmodule</key>
|
||||
<data>
|
||||
HD1yZ99Moza0WmezwtWRpMx5Pnw=
|
||||
</data>
|
||||
<key>config.json</key>
|
||||
<data>
|
||||
YDYijrWiF8Rxzv8lihbkxXuOk7s=
|
||||
</data>
|
||||
</dict>
|
||||
<key>files2</key>
|
||||
<dict>
|
||||
<key>Headers/unimoduleXMlkitScannigU-Swift.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
5EiTAikkGs6OTkMJQl4yJG93zGnE9T3Xrx0k9gPJx9k=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Headers/unimoduleXMlkitScannigU.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
XAsze1m7Z4PhyI2gj+RafZVS4xyqWjgvqraTawdVPMY=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Modules/module.modulemap</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
+9eWeZGAx3sZSdYnYgkJicwkn6jTvvAsdR2TYB6RkJ8=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Modules/unimoduleXMlkitScannigU.swiftmodule/Project/x86_64-apple-ios-simulator.swiftsourceinfo</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
wP6hT6p1lWe3E3s+SUNjJQZ9E5PH6uILYCL8RbV/eT4=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Modules/unimoduleXMlkitScannigU.swiftmodule/x86_64-apple-ios-simulator.abi.json</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
qs8Ju2FnIp4JwyRbr3eZBTHjXnx1Zf+UZdZuRy+9kBs=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Modules/unimoduleXMlkitScannigU.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
HauXAU+7IlSB5MF/vIm1VHVr3OWKqepeLQgIUrC6wQ4=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Modules/unimoduleXMlkitScannigU.swiftmodule/x86_64-apple-ios-simulator.swiftdoc</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
MzXLl4ShiG1kAyuafALgeqtRIcp7ZWXecbXf1V6UnSM=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Modules/unimoduleXMlkitScannigU.swiftmodule/x86_64-apple-ios-simulator.swiftinterface</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
HauXAU+7IlSB5MF/vIm1VHVr3OWKqepeLQgIUrC6wQ4=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Modules/unimoduleXMlkitScannigU.swiftmodule/x86_64-apple-ios-simulator.swiftmodule</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
4noLKKIKPbb2FMi/RHd6gtbVz/if6JDHpgyY8vWcJEw=
|
||||
</data>
|
||||
</dict>
|
||||
<key>config.json</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
NoNtCzvQ0iOAfyiuCHYdlYR2DRmlZB9Toiooz3wQ0lQ=
|
||||
</data>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>rules</key>
|
||||
<dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^version.plist$</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>rules2</key>
|
||||
<dict>
|
||||
<key>.*\.dSYM($|/)</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>11</real>
|
||||
</dict>
|
||||
<key>^(.*/)?\.DS_Store$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>2000</real>
|
||||
</dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^Info\.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^PkgInfo$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^embedded\.provisionprofile$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^version\.plist$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,297 @@
|
||||
import { AVCaptureSession, AVCaptureDevice, AVMediaType, AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureDeviceInput, AVCaptureInput, AVCaptureVideoDataOutput, AVCaptureConnection, AVCaptureVideoPreviewLayer, AVLayerVideoGravity, AVCaptureVideoOrientation } from 'AVFoundation';
|
||||
import { PHPhotoLibrary, PHAuthorizationStatus } from 'Photos';
|
||||
import { CMSampleBuffer, CMSampleBufferGetFormatDescription, CMFormatDescription, CMVideoFormatDescriptionGetDimensions } from 'CoreMedia';
|
||||
import { DispatchQueue } from "Dispatch"
|
||||
import { UIView, UIImage , UIApplication } from 'UIKit';
|
||||
import { CGRect, CGFloat } from 'CoreFoundation';
|
||||
import { Barcode, BarcodeScannerOptions, BarcodeFormat, BarcodeScanner, CMVideoDimensions } from "MLKitBarcodeScanning"
|
||||
import { VisionImage } from "MLKit"
|
||||
import { SCANNING_PHOTO_RESULT, CALL_BACK_TYPE_PRIVATE, SCANNING_PHOTO_RESULT_BOUND, SCANNING_PHOTO_RESULT_POS ,AUTH_CALL_BACK_TYPE} from '../interface.uts';
|
||||
import { NotificationCenter , NSNotification , Notification } from 'Foundation';
|
||||
import { Selector } from 'ObjectiveC';
|
||||
// 识别结果
|
||||
let scanResult = [] as Swift.Array<Barcode>
|
||||
// 视频宽
|
||||
let videoWidth = 0
|
||||
// 视频高
|
||||
let videoHeight = 0
|
||||
// let camera:xCamera|null = null;
|
||||
let barcodeOptions = BarcodeScannerOptions(formats = BarcodeFormat.all)
|
||||
class CaptureOutSessionBuffer implements AVCaptureVideoDataOutputSampleBufferDelegate {
|
||||
onwerCamera:xCamera
|
||||
constructor(dv:xCamera) {
|
||||
this.onwerCamera = dv;
|
||||
super()
|
||||
}
|
||||
captureOutput(output : AVCaptureOutput, @argumentLabel("didOutput") sampleBuffer : CMSampleBuffer, @argumentLabel("from") connection : AVCaptureConnection) {
|
||||
let t = this;
|
||||
DispatchQueue.main.async(execute=():void => {
|
||||
|
||||
// console.log('图片捕捉正确')
|
||||
let image = new VisionImage(buffer = sampleBuffer)
|
||||
// 这里会直接影响到测量的起始点。下面是镜像,测量是图片左上为起始,但设备是竖向,因此计算时的x,y坐标其实是相对屏幕向右转下,x,y是反向的,因为是镜像
|
||||
// 所以计算位置时,x,y是反的,宽和高也是相反的。
|
||||
image.orientation = UIImage.Orientation.upMirrored
|
||||
|
||||
// options=barcodeOptions
|
||||
let barcodeScanner = new BarcodeScanner.barcodeScanner(options = barcodeOptions)
|
||||
console.log(image)
|
||||
barcodeScanner.process(image, completion = (features : Swift.Array<Barcode> | null, error) => {
|
||||
if (features == null || error != null) {
|
||||
return;
|
||||
}
|
||||
if (features!.length == 0 || scanResult.length>0) {
|
||||
return;
|
||||
}
|
||||
scanResult = features!
|
||||
let descVideo : CMFormatDescription | null = CMSampleBufferGetFormatDescription(sampleBuffer);
|
||||
if (descVideo != null) {
|
||||
let dimesionsize : CMVideoDimensions = CMVideoFormatDescriptionGetDimensions(descVideo!)
|
||||
t.onwerCamera.videoBox_width= Number(dimesionsize.width)
|
||||
t.onwerCamera.videoBox_height = Number(dimesionsize.height)
|
||||
}
|
||||
this.onwerCamera.pause()
|
||||
|
||||
let listpos = [] as number[][][]
|
||||
let bounedlist = [] as SCANNING_PHOTO_RESULT_BOUND[]
|
||||
let rulstText = [] as string[]
|
||||
|
||||
for (let i = 0; i < features!.length; i++) {
|
||||
let barcode = features![i]
|
||||
// 位置信息
|
||||
let bounds = barcode.frame
|
||||
// 坐标
|
||||
let corners = barcode.cornerPoints
|
||||
// 解析的内容
|
||||
let rawValue = barcode.rawValue
|
||||
let iminfo = {
|
||||
width: Number(bounds.width),
|
||||
height: Number(bounds.height),
|
||||
centerX: Number(bounds.midX),
|
||||
centerY: Number(bounds.midY)
|
||||
} as SCANNING_PHOTO_RESULT_BOUND
|
||||
let posinfo = [] as number[][]
|
||||
for (pos in corners!) {
|
||||
// pos是pointValue类型并返回CGPoint,其中有x,y
|
||||
let ponit = pos.cgPointValue
|
||||
posinfo.push([Number(ponit.x), Number(ponit.y)] as number[])
|
||||
}
|
||||
listpos.push(posinfo)
|
||||
bounedlist.push(iminfo)
|
||||
rulstText.push(rawValue == null ? '' : (rawValue!))
|
||||
|
||||
}
|
||||
let res = {
|
||||
url: '',
|
||||
position: listpos,
|
||||
bounds: bounedlist,
|
||||
text: rulstText
|
||||
} as SCANNING_PHOTO_RESULT
|
||||
|
||||
t.onwerCamera.createPointView(res)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 检查权限
|
||||
export function isCheckPermissions(callFun : null | AUTH_CALL_BACK_TYPE){
|
||||
let realcall : AUTH_CALL_BACK_TYPE = (_ : boolean) => { };
|
||||
if (callFun != null) {
|
||||
let cllfunFun = callFun!;
|
||||
realcall = cllfunFun
|
||||
}
|
||||
// 相册选取权限
|
||||
PHPhotoLibrary.requestAuthorization((isok : PHAuthorizationStatus) => {
|
||||
// 拒绝了相册选取权限。
|
||||
if (isok != PHAuthorizationStatus.authorized) {
|
||||
realcall(false)
|
||||
return;
|
||||
}
|
||||
// 录像权限
|
||||
AVCaptureDevice.requestAccess(for = AVMediaType.video, completionHandler = (isok2 : boolean) => {
|
||||
if (!isok2) {
|
||||
realcall(false)
|
||||
return;
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
export class xCamera {
|
||||
// 父容器
|
||||
parentView : UIView|null = null;
|
||||
// 相机区域视图
|
||||
cameraView : AVCaptureVideoPreviewLayer|null = null;
|
||||
// 视频宽
|
||||
videoBox_width = 0
|
||||
// 视频高。
|
||||
videoBox_height = 0
|
||||
pointView = [] as UIView[]
|
||||
// 默认的相机
|
||||
captureDevice:AVCaptureDevice|null = null
|
||||
// 捕获会话
|
||||
captureSession:AVCaptureSession|null = null
|
||||
// 输入相机设备
|
||||
inputDevice : AVCaptureDeviceInput | null = null;
|
||||
// 输出会话
|
||||
captureOut = new AVCaptureVideoDataOutput();
|
||||
// 图像捕捉事件代理。
|
||||
captureOutDelegate : CaptureOutSessionBuffer|null = null ;
|
||||
constructor(){
|
||||
super();
|
||||
}
|
||||
|
||||
newCamraViewLayer(){
|
||||
// 设置预览层绑定
|
||||
// 下面的缩放会居中显示,因此左顶点可能在左上方
|
||||
this.cameraView!.videoGravity = AVLayerVideoGravity.resizeAspectFill
|
||||
this.cameraView!.connection!.videoOrientation = AVCaptureVideoOrientation.portrait
|
||||
this.cameraView!.frame = this.parentView!.layer.bounds;
|
||||
// 绑定渲染层。
|
||||
this.parentView!.layer.addSublayer(this.cameraView!)
|
||||
}
|
||||
clearPointView(){
|
||||
for (let i = 0; i < this.pointView.length; i++) {
|
||||
let item = this.pointView[i];
|
||||
item.removeFromSuperview();
|
||||
}
|
||||
this.pointView = [] as UIView[]
|
||||
}
|
||||
// 创建点。
|
||||
createPointView (result : SCANNING_PHOTO_RESULT){
|
||||
if (this.cameraView == null || this.parentView == null) return;
|
||||
let realCgSize = this.cameraView!.frame as CGRect;
|
||||
let realWidth = Number(realCgSize.width);
|
||||
let realHeight = Number(realCgSize.height);
|
||||
this.clearPointView();
|
||||
// 计算视频和容器的宽高比
|
||||
let videoAspectRatio = this.videoBox_width / this.videoBox_height
|
||||
let containerAspectRatio = realWidth / realHeight
|
||||
let ratio_real_width = 0
|
||||
let ratio_real_height = 0
|
||||
if (videoAspectRatio > containerAspectRatio) {
|
||||
ratio_real_width = realWidth
|
||||
// 视频的宽高比大于预览层的宽高比,宽为预览宽,但高会大于预览高从而被裁剪。
|
||||
ratio_real_height = realHeight * videoAspectRatio
|
||||
} else {
|
||||
ratio_real_width = realWidth / videoAspectRatio
|
||||
ratio_real_height = realHeight
|
||||
}
|
||||
|
||||
let pointSize = 30
|
||||
for (let i = 0; i < result.bounds.length; i++) {
|
||||
let text = result.text[i]
|
||||
let bounds = result.bounds[i]
|
||||
let pos = result.position[i];
|
||||
let centerx = (pos[2][0] - pos[0][0]) / 2 + pos[0][0]
|
||||
let centery = (pos[2][1] - pos[0][1]) / 2 + pos[0][1]
|
||||
let diffTop = (realHeight - ratio_real_height) / 2
|
||||
let diffLeft = (realWidth - ratio_real_width) / 2
|
||||
let x = (ratio_real_width - ratio_real_width * (bounds.centerY / this.videoBox_height) - diffLeft - 15).toInt()
|
||||
|
||||
let y = (ratio_real_height * (bounds.centerX / this.videoBox_width) + diffTop - 15).toInt()
|
||||
// console.log(bounds.centerX,bounds.centerY,x,y,videoWidth,videoHeight,realWidth,realHeight,ratio_real_width,ratio_real_height)
|
||||
|
||||
let rect = new CGRect(x = x, y = y, width = pointSize.toInt(), height = pointSize.toInt())
|
||||
let pointViewSelf = new UIView();
|
||||
pointViewSelf.layer.frame = rect;
|
||||
pointViewSelf.backgroundColor = UTSiOS.colorWithString('#21d429');
|
||||
pointViewSelf.layer.borderWidth = 2
|
||||
pointViewSelf.layer.borderColor = UTSiOS.colorWithString('#FFFFFF').cgColor;
|
||||
pointViewSelf.layer.cornerRadius = new CGFloat(pointSize / 2);
|
||||
pointViewSelf.isUserInteractionEnabled = true;
|
||||
// let onlick = new PointClickEvent((atext:string) => {
|
||||
// console.log("ok....",result.text)
|
||||
// component!.__$$emit('clickQr',new Map<string,string>([["result",atext]]))
|
||||
// },text);
|
||||
// pointClickEvent.push(onlick)
|
||||
// let tapGestureCancel = new UITapGestureRecognizer()
|
||||
// tapGestureCancel.addTarget(pointClickEvent[pointClickEvent.length-1], action = new Selector("onclick"))
|
||||
// pointViewSelf.addGestureRecognizer(tapGestureCancel)
|
||||
|
||||
this.parentView!.addSubview(pointViewSelf)
|
||||
|
||||
this.pointView.push(pointViewSelf)
|
||||
|
||||
}
|
||||
}
|
||||
setView(view:UIView){
|
||||
this.parentView = view;
|
||||
}
|
||||
oninit(){
|
||||
this.captureDevice = AVCaptureDevice.default(for = AVMediaType.video)
|
||||
this.captureSession = new AVCaptureSession();
|
||||
this.captureSession!.sessionPreset = AVCaptureSession.Preset.low
|
||||
|
||||
this.captureOutDelegate = new CaptureOutSessionBuffer(this)
|
||||
|
||||
// 注册相机启动时的通知事件。
|
||||
NotificationCenter.default.addObserver(this,selector = Selector("camreaOpeingStatus"),name=NSNotification.Name("didStartRunningNotification"),object = null)
|
||||
// 新建设备的输入设备
|
||||
this.inputDevice = UTSiOS.try(new AVCaptureDeviceInput(device = this.captureDevice!), "?")
|
||||
if(this.inputDevice!=null){
|
||||
// 添加输入到会话中。
|
||||
this.captureSession!.addInput(this.inputDevice!)
|
||||
}
|
||||
|
||||
this.captureOut.alwaysDiscardsLateVideoFrames = true;
|
||||
// 设置输出会话
|
||||
this.captureOut.setSampleBufferDelegate(this.captureOutDelegate!, queue = DispatchQueue.global())
|
||||
if (this.captureSession!.canAddOutput(this.captureOut)) {
|
||||
this.captureSession!.addOutput(this.captureOut)
|
||||
console.log("绑定输出层正确!")
|
||||
}
|
||||
let t = this;
|
||||
DispatchQueue.main.async(execute=():void => {
|
||||
t.cameraView = new AVCaptureVideoPreviewLayer(session = t.captureSession!)
|
||||
// 设置预览层绑定
|
||||
// 下面的缩放会居中显示,因此左顶点可能在左上方
|
||||
t.cameraView!.videoGravity = AVLayerVideoGravity.resizeAspectFill
|
||||
t.cameraView!.connection!.videoOrientation = AVCaptureVideoOrientation.portrait
|
||||
t.cameraView!.frame = t.parentView!.layer.bounds;
|
||||
// 绑定渲染层。
|
||||
t.parentView!.layer.addSublayer(t.cameraView!)
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
@objc static camreaOpeingStatus(notification:Notification){
|
||||
|
||||
console.log(notification,'启动了。')
|
||||
}
|
||||
open(){
|
||||
this.clearPointView()
|
||||
this.pause();
|
||||
this.captureSession!.startRunning()
|
||||
this.oninit();
|
||||
}
|
||||
stop(){
|
||||
// 暂停预览。
|
||||
this.pause()
|
||||
// 移除通知。
|
||||
NotificationCenter.default.removeObserver(this, name=NSNotification.Name("didStartRunningNotification"),object = null)
|
||||
// 移除视频预览层。
|
||||
this.cameraView!.removeFromSuperlayer()
|
||||
// 移除点。
|
||||
this.clearPointView()
|
||||
if(this.inputDevice!=null){
|
||||
this.captureSession!.removeInput(this.inputDevice!)
|
||||
}
|
||||
this.captureSession!.removeOutput(this.captureOut)
|
||||
|
||||
}
|
||||
pause(){
|
||||
if(this.captureSession!.isRunning){
|
||||
this.captureSession!.stopRunning()
|
||||
|
||||
}
|
||||
this.cameraView!.removeFromSuperlayer()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"deploymentTarget": "12",
|
||||
"dependencies-pods": [
|
||||
{
|
||||
"name": "GoogleMLKit/BarcodeScanning",
|
||||
"version": "5.0.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,537 @@
|
||||
<template>
|
||||
<view>
|
||||
</view>
|
||||
</template>
|
||||
<script lang="uts">
|
||||
// import { UIView } from 'UIKit';
|
||||
import { AVCaptureSession, AVCaptureDevice, AVMediaType, AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureDeviceInput, AVCaptureInput, AVCaptureVideoDataOutput, AVCaptureConnection, AVCaptureVideoPreviewLayer, AVLayerVideoGravity, AVCaptureVideoOrientation } from 'AVFoundation';
|
||||
import { PHPhotoLibrary, PHAuthorizationStatus } from 'Photos';
|
||||
import { CMSampleBuffer, CMSampleBufferGetFormatDescription, CMFormatDescription, CMVideoFormatDescriptionGetDimensions } from 'CoreMedia';
|
||||
import { DispatchQueue } from "Dispatch"
|
||||
import { UIView, UIImage, UIApplication } from 'UIKit';
|
||||
import { CGRect, CGFloat, CGPoint, CGAffineTransform } from 'CoreFoundation';
|
||||
import { Barcode, BarcodeScannerOptions, BarcodeFormat, BarcodeScanner, CMVideoDimensions } from "MLKitBarcodeScanning"
|
||||
import { VisionImage } from "MLKit"
|
||||
import { SCANNING_PHOTO_RESULT, CALL_BACK_TYPE_PRIVATE, SCANNING_PHOTO_RESULT_BOUND, SCANNING_PHOTO_RESULT_POS, AUTH_CALL_BACK_TYPE } from '../interface.uts';
|
||||
import { NotificationCenter, NSNotification, Notification } from 'Foundation';
|
||||
import { Selector } from 'ObjectiveC';
|
||||
|
||||
type ConverXy = {
|
||||
x : number,
|
||||
y : number,
|
||||
minx : number,
|
||||
maxx : number,
|
||||
miny : number,
|
||||
maxy : number,
|
||||
}
|
||||
type contaiaSize = {
|
||||
width : number,
|
||||
height : number
|
||||
}
|
||||
type contaiaCalseSize = {
|
||||
scale : number,
|
||||
offsetX : number,
|
||||
offsetY : number
|
||||
}
|
||||
type contaiPointXy = {
|
||||
x : number,
|
||||
y : number
|
||||
}
|
||||
// let inputDevice : AVCaptureDeviceInput | null = null;
|
||||
// 识别结果
|
||||
let scanResult = [] as Swift.Array<Barcode>
|
||||
let captureSession : null | AVCaptureSession = null;
|
||||
// 视频宽
|
||||
let videoWidth = 0
|
||||
// 视频高
|
||||
let videoHeight = 0
|
||||
// 图像捕捉事件代理。
|
||||
let captureOutDelegate : CaptureOutSessionBuffer | null = null;
|
||||
let pointsEvents = [] as PointClickEvent[]
|
||||
let isCamreaing = false;
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
previewLayer: null as null | UIView,
|
||||
pointView: [] as UIView[],
|
||||
avcapDev:null as null|AVCaptureDevice
|
||||
}
|
||||
},
|
||||
name: "x-mlkit-scannig-u",
|
||||
emits: ["scan", "clickQr"],
|
||||
props:{
|
||||
/**
|
||||
* 是否开启闪光灯
|
||||
*/
|
||||
flash:{
|
||||
type:Boolean,
|
||||
default:false
|
||||
}
|
||||
},
|
||||
// 创建组件对应的原生 View,返回值类型需要修改为实际 View 类型
|
||||
NVLoad() : UIView {
|
||||
let view = new UIView();
|
||||
this.previewLayer = view
|
||||
|
||||
return view
|
||||
},
|
||||
NVLayouted() {
|
||||
|
||||
|
||||
},
|
||||
NVBeforeUnload() {
|
||||
// 注销前关闭摄像头。
|
||||
this.closeCamera();
|
||||
},
|
||||
methods: {
|
||||
|
||||
isCheckPermissions(callFun : null | AUTH_CALL_BACK_TYPE) {
|
||||
let realcall : AUTH_CALL_BACK_TYPE = (_ : boolean) => { };
|
||||
if (callFun != null) {
|
||||
let cllfunFun = callFun!;
|
||||
realcall = cllfunFun
|
||||
}
|
||||
|
||||
AVCaptureDevice.requestAccess(for = AVMediaType.video, completionHandler = (isok2 : boolean) => {
|
||||
if (!isok2) {
|
||||
realcall(false)
|
||||
return;
|
||||
}
|
||||
realcall(true)
|
||||
})
|
||||
},
|
||||
/** 手动开启和关闭闪光灯 */
|
||||
setFlashModel(flash:boolean){
|
||||
if(this.avcapDev!=null){
|
||||
let islock = UTSiOS.try(this.avcapDev!.lockForConfiguration(),"?")
|
||||
if(islock!=null){
|
||||
this.avcapDev!.torchMode = flash? AVCaptureDevice.TorchMode.on : AVCaptureDevice.TorchMode.off
|
||||
this.avcapDev!.unlockForConfiguration()
|
||||
}
|
||||
}
|
||||
},
|
||||
openCamera() {
|
||||
let t = this;
|
||||
this.isCheckPermissions((auth : boolean) => {
|
||||
if (auth) {
|
||||
t.createVideo()
|
||||
}
|
||||
})
|
||||
|
||||
},
|
||||
chooseImage() {
|
||||
let t = this;
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sourceType:['album'],
|
||||
success(evt : ChooseImageSuccess) {
|
||||
|
||||
if (evt.tempFilePaths.length > 0) {
|
||||
let imgs = evt.tempFilePaths[0] as string;
|
||||
let realImgPath = imgs as string
|
||||
if(imgs.indexOf('file://')>-1){
|
||||
realImgPath = imgs.substring(7) as string;
|
||||
}
|
||||
let image = new UIImage(contentsOfFile = realImgPath);
|
||||
|
||||
if (image != null) {
|
||||
let realimageds = image! as UIImage;
|
||||
|
||||
// 图片识别码.
|
||||
let visionImage = new VisionImage(image = realimageds)
|
||||
visionImage.orientation = realimageds.imageOrientation
|
||||
|
||||
let barcodeOptions = BarcodeScannerOptions(formats = BarcodeFormat.all)
|
||||
let barcodeScanner = new BarcodeScanner.barcodeScanner(options = barcodeOptions)
|
||||
barcodeScanner.process(visionImage, completion = (features : Swift.Array<Barcode> | null, error) => {
|
||||
|
||||
if (features == null || error != null) {
|
||||
uni.setStorageSync("xMlKITScaning", JSON.stringify([] as string[]))
|
||||
t.$emit("scan")
|
||||
return;
|
||||
}
|
||||
if (features!.length == 0) {
|
||||
uni.setStorageSync("xMlKITScaning", JSON.stringify([] as string[]))
|
||||
t.$emit("scan")
|
||||
return;
|
||||
}
|
||||
|
||||
let listpos = [] as number[][][]
|
||||
let bounedlist = [] as SCANNING_PHOTO_RESULT_BOUND[]
|
||||
let rulstText = [] as string[]
|
||||
|
||||
for (let i = 0; i < features!.length; i++) {
|
||||
let barcode = features![i]
|
||||
// 位置信息
|
||||
let bounds = barcode.frame
|
||||
// 坐标
|
||||
let corners = barcode.cornerPoints
|
||||
// 解析的内容
|
||||
let rawValue = barcode.rawValue
|
||||
let iminfo = {
|
||||
width: Number(bounds.width),
|
||||
height: Number(bounds.height),
|
||||
centerX: Number(bounds.midX),
|
||||
centerY: Number(bounds.midY)
|
||||
} as SCANNING_PHOTO_RESULT_BOUND
|
||||
let posinfo = [] as number[][]
|
||||
for (pos in corners!) {
|
||||
// pos是pointValue类型并返回CGPoint,其中有x,y
|
||||
let ponit = pos.cgPointValue
|
||||
posinfo.push([Number(ponit.x), Number(ponit.y)] as number[])
|
||||
}
|
||||
listpos.push(posinfo)
|
||||
bounedlist.push(iminfo)
|
||||
rulstText.push(rawValue == null ? '' : (rawValue!))
|
||||
|
||||
}
|
||||
let result = {
|
||||
url: '',
|
||||
position: listpos,
|
||||
bounds: bounedlist,
|
||||
text: rulstText
|
||||
} as SCANNING_PHOTO_RESULT
|
||||
|
||||
|
||||
uni.setStorageSync("xMlKITScaning", JSON.stringify(rulstText))
|
||||
t.$emit("scan")
|
||||
})
|
||||
} else {
|
||||
|
||||
uni.setStorageSync("xMlKITScaning", JSON.stringify([] as string[]))
|
||||
t.$emit("scan")
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
uni.setStorageSync("xMlKITScaning", JSON.stringify([] as string[]))
|
||||
t.$emit("scan")
|
||||
}
|
||||
},
|
||||
fail() {
|
||||
uni.setStorageSync("xMlKITScaning", JSON.stringify([] as string[]))
|
||||
t.$emit("scan")
|
||||
}
|
||||
})
|
||||
},
|
||||
closeCamera() {
|
||||
// 移除点。
|
||||
this.clearPointView()
|
||||
this.setFlashModel(false)
|
||||
if (captureSession != null) {
|
||||
captureSession!.stopRunning()
|
||||
|
||||
for (input in captureSession!.inputs) {
|
||||
captureSession!.removeInput(input)
|
||||
}
|
||||
|
||||
for (output in captureSession!.outputs) {
|
||||
captureSession!.removeOutput(output)
|
||||
}
|
||||
captureSession = null;
|
||||
}
|
||||
scanResult = [] as Swift.Array<Barcode>
|
||||
pointsEvents = [] as PointClickEvent[]
|
||||
this.avcapDev = null;
|
||||
|
||||
},
|
||||
createPointView(result : SCANNING_PHOTO_RESULT) {
|
||||
|
||||
let realCgSize = this.previewLayer!.frame as CGRect;
|
||||
let realWidth = Number(realCgSize.width);
|
||||
let realHeight = Number(realCgSize.height);
|
||||
this.clearPointView();
|
||||
// 计算视频和容器的宽高比
|
||||
let videoAspectRatio = videoWidth / videoHeight
|
||||
let containerAspectRatio = realWidth / realHeight
|
||||
let ratio_real_width = 0
|
||||
let ratio_real_height = 0
|
||||
if (videoAspectRatio > containerAspectRatio) {
|
||||
ratio_real_width = realWidth
|
||||
// 视频的宽高比大于预览层的宽高比,宽为预览宽,但高会大于预览高从而被裁剪。
|
||||
ratio_real_height = realHeight * videoAspectRatio
|
||||
} else {
|
||||
ratio_real_width = realWidth / videoAspectRatio
|
||||
ratio_real_height = realHeight
|
||||
}
|
||||
let t = this;
|
||||
let pointSize = 30
|
||||
DispatchQueue.main.async(execute = () : void => {
|
||||
|
||||
for (let i = 0; i < result.bounds.length; i++) {
|
||||
let text = result.text[i]
|
||||
let bounds = result.bounds[i]
|
||||
let pos = result.position[i];
|
||||
let centerx = (pos[2][0] - pos[0][0]) / 2 + pos[0][0]
|
||||
let centery = (pos[2][1] - pos[0][1]) / 2 + pos[0][1]
|
||||
let diffTop = (realHeight - ratio_real_height) / 2
|
||||
let diffLeft = (realWidth - ratio_real_width) / 2
|
||||
let x = (ratio_real_width - ratio_real_width * (bounds.centerY / videoHeight) - diffLeft - 15).toInt()
|
||||
|
||||
let y = (ratio_real_height * (bounds.centerX / videoWidth) + diffTop - 15).toInt()
|
||||
// console.log(bounds.centerX,bounds.centerY,x,y,videoWidth,videoHeight,realWidth,realHeight,ratio_real_width,ratio_real_height)
|
||||
let result = calcPoint(realWidth, realHeight, videoWidth, videoHeight, bounds.centerX, bounds.centerY)
|
||||
let rect = new CGRect(x = (result.x - 15).toInt(), y = (result.y - 15).toInt(), width = pointSize.toInt(), height = pointSize.toInt())
|
||||
let pointViewSelf = new UIView();
|
||||
pointViewSelf.layer.frame = rect;
|
||||
pointViewSelf.backgroundColor = UTSiOS.colorWithString('#21d429');
|
||||
pointViewSelf.layer.borderWidth = 2
|
||||
pointViewSelf.layer.borderColor = UTSiOS.colorWithString('#FFFFFF').cgColor;
|
||||
pointViewSelf.layer.cornerRadius = new CGFloat(pointSize / 2);
|
||||
|
||||
let onlick = new PointClickEvent((atext : string) => {
|
||||
console.log("ok....", atext)
|
||||
uni.setStorageSync("xMlKITScaningClick", atext)
|
||||
t.$emit('clickQr')
|
||||
}, text);
|
||||
pointsEvents.push(onlick)
|
||||
let tapGestureCancel = new UITapGestureRecognizer()
|
||||
tapGestureCancel.addTarget(onlick, action = new Selector("onclick"))
|
||||
pointViewSelf.addGestureRecognizer(tapGestureCancel)
|
||||
|
||||
|
||||
this.previewLayer!.addSubview(pointViewSelf)
|
||||
this.pointView.push(pointViewSelf)
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
clearPointView() {
|
||||
DispatchQueue.main.async(execute = () : void => {
|
||||
for (let i = 0; i < this.pointView.length; i++) {
|
||||
let item = this.pointView[i];
|
||||
item.removeFromSuperview();
|
||||
}
|
||||
this.pointView = [] as UIView[]
|
||||
})
|
||||
},
|
||||
|
||||
createVideo() {
|
||||
let t = this;
|
||||
isCamreaing = false;
|
||||
this.closeCamera();
|
||||
captureSession = new AVCaptureSession();
|
||||
let captureDevice = AVCaptureDevice.default(for = AVMediaType.video)
|
||||
let captureOut = new AVCaptureVideoDataOutput();
|
||||
|
||||
captureSession!.sessionPreset = AVCaptureSession.Preset.photo
|
||||
let parentView = this.previewLayer!;
|
||||
|
||||
captureOutDelegate = new CaptureOutSessionBuffer((result : SCANNING_PHOTO_RESULT) => {
|
||||
t.createPointView(result)
|
||||
uni.setStorageSync("xMlKITScaning", JSON.stringify(result.text))
|
||||
t.$emit("scan")
|
||||
})
|
||||
|
||||
// 新建设备的输入设备
|
||||
let inputDevice = UTSiOS.try(new AVCaptureDeviceInput(device = captureDevice!), "?")
|
||||
if (inputDevice != null) {
|
||||
// 添加输入到会话中。
|
||||
captureSession!.addInput(inputDevice!)
|
||||
}
|
||||
|
||||
|
||||
captureOut.alwaysDiscardsLateVideoFrames = true;
|
||||
// 设置输出会话
|
||||
captureOut.setSampleBufferDelegate(captureOutDelegate!, queue = DispatchQueue.global())
|
||||
if (captureSession!.canAddOutput(captureOut)) {
|
||||
captureSession!.addOutput(captureOut)
|
||||
console.log("绑定输出层正确!")
|
||||
}
|
||||
|
||||
|
||||
DispatchQueue.main.async(execute = () : void => {
|
||||
let cameraView = new AVCaptureVideoPreviewLayer(session = captureSession!)
|
||||
// 设置预览层绑定
|
||||
// 下面的缩放会居中显示,因此左顶点可能在左上方
|
||||
cameraView.videoGravity = AVLayerVideoGravity.resizeAspectFill
|
||||
//后摄像头,设备是横向向的,显示到竖向,是顺时旋转90度.
|
||||
cameraView.connection!.videoOrientation = AVCaptureVideoOrientation.portrait
|
||||
cameraView.frame = parentView.layer.bounds;
|
||||
// 绑定渲染层。
|
||||
parentView.layer.addSublayer(cameraView)
|
||||
captureSession!.startRunning()
|
||||
t.avcapDev = captureDevice!
|
||||
t.setFlashModel(t.flash)
|
||||
// ios识别太快了,图像还没定焦就识别出来了,稍微延迟下,不要太快了。与安卓对齐 下。
|
||||
setTimeout(function () {
|
||||
isCamreaing = true;
|
||||
}, 300);
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PointClickEvent {
|
||||
private callback : (txt : string) => void
|
||||
private text : string;
|
||||
constructor(callback : (txt : string) => void, atext : string) {
|
||||
this.callback = callback;
|
||||
this.text = atext;
|
||||
super.init()
|
||||
}
|
||||
|
||||
@objc onclick() {
|
||||
|
||||
this.callback(this.text)
|
||||
}
|
||||
}
|
||||
|
||||
class CaptureOutSessionBuffer implements AVCaptureVideoDataOutputSampleBufferDelegate {
|
||||
callback : (str : SCANNING_PHOTO_RESULT) => void = (str : SCANNING_PHOTO_RESULT) => { }
|
||||
constructor(dv : (str : SCANNING_PHOTO_RESULT) => void) {
|
||||
this.callback = dv;
|
||||
super()
|
||||
}
|
||||
captureOutput(output : AVCaptureOutput, @argumentLabel("didOutput") sampleBuffer : CMSampleBuffer, @argumentLabel("from") connection : AVCaptureConnection) {
|
||||
if (!isCamreaing) return;
|
||||
// console.log('图片捕捉正确')
|
||||
let image = new VisionImage(buffer = sampleBuffer)
|
||||
// 这里会直接影响到测量的起始点。下面是镜像,测量是图片左上为起始,但设备是竖向,因此计算时的x,y坐标其实是相对屏幕向右转下,x,y是反向的,因为是镜像
|
||||
// 所以计算位置时,x,y是反的,宽和高也是相反的。
|
||||
image.orientation = UIImage.Orientation.upMirrored
|
||||
let barcodeOptions = BarcodeScannerOptions(formats = BarcodeFormat.all)
|
||||
// options=barcodeOptions
|
||||
let barcodeScanner = new BarcodeScanner.barcodeScanner(options = barcodeOptions)
|
||||
|
||||
barcodeScanner.process(image, completion = (features : Swift.Array<Barcode> | null, error) => {
|
||||
|
||||
if (features == null || error != null) {
|
||||
return;
|
||||
}
|
||||
if (features!.length == 0 || scanResult.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 暂停识别。
|
||||
captureSession!.stopRunning()
|
||||
|
||||
scanResult = features!
|
||||
let descVideo : CMFormatDescription | null = CMSampleBufferGetFormatDescription(sampleBuffer);
|
||||
if (descVideo != null) {
|
||||
let dimesionsize : CMVideoDimensions = CMVideoFormatDescriptionGetDimensions(descVideo!)
|
||||
videoWidth = Number(dimesionsize.width)
|
||||
videoHeight = Number(dimesionsize.height)
|
||||
}
|
||||
|
||||
|
||||
let listpos = [] as number[][][]
|
||||
let bounedlist = [] as SCANNING_PHOTO_RESULT_BOUND[]
|
||||
let rulstText = [] as string[]
|
||||
|
||||
for (let i = 0; i < features!.length; i++) {
|
||||
let barcode = features![i]
|
||||
// 位置信息
|
||||
let bounds = barcode.frame
|
||||
// 坐标
|
||||
let corners = barcode.cornerPoints
|
||||
// 解析的内容
|
||||
let rawValue = barcode.rawValue
|
||||
let iminfo = {
|
||||
width: Number(bounds.width),
|
||||
height: Number(bounds.height),
|
||||
centerX: Number(bounds.midX),
|
||||
centerY: Number(bounds.midY)
|
||||
} as SCANNING_PHOTO_RESULT_BOUND
|
||||
let posinfo = [] as number[][]
|
||||
for (pos in corners!) {
|
||||
// pos是pointValue类型并返回CGPoint,其中有x,y
|
||||
let ponit = pos.cgPointValue
|
||||
posinfo.push([Number(ponit.x), Number(ponit.y)] as number[])
|
||||
}
|
||||
listpos.push(posinfo)
|
||||
bounedlist.push(iminfo)
|
||||
rulstText.push(rawValue == null ? '' : (rawValue!))
|
||||
|
||||
}
|
||||
let res = {
|
||||
url: '',
|
||||
position: listpos,
|
||||
bounds: bounedlist,
|
||||
text: rulstText
|
||||
} as SCANNING_PHOTO_RESULT
|
||||
|
||||
this.callback(res)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//映射坐标.
|
||||
function getCorrectionMatrix(c_w : number, c_h : number, image_w : number, image_h : number, image_point_x : number, image_point_y : number) : CGPoint {
|
||||
let sourcePoints = [
|
||||
0,
|
||||
0,
|
||||
image_w.toInt(),
|
||||
0,
|
||||
image_w.toInt(),
|
||||
image_h.toInt(),
|
||||
0,
|
||||
image_h.toInt()
|
||||
]
|
||||
let destinationPoints = [
|
||||
0,
|
||||
0,
|
||||
c_w.toInt(),
|
||||
0,
|
||||
c_w.toInt(),
|
||||
c_h.toInt(),
|
||||
0,
|
||||
c_h.toInt()
|
||||
]
|
||||
|
||||
let vertexSize = 2
|
||||
let rotationDegrees = 90 // 默认后置摄像头就是90度的
|
||||
let shiftOffset = rotationDegrees / 90 * vertexSize;
|
||||
let tempArray = destinationPoints.slice(0)
|
||||
for (let i = 0; i < sourcePoints.length; i++) {
|
||||
let fromIndex = (i + shiftOffset) % sourcePoints.length
|
||||
destinationPoints[i] = tempArray[fromIndex.toInt()]
|
||||
}
|
||||
|
||||
function scaleAndCenterContent(
|
||||
container : contaiaSize,
|
||||
content : contaiaSize
|
||||
) : contaiaCalseSize {
|
||||
// 计算缩放比例
|
||||
const scaleWidth = container.width / content.width;
|
||||
const scaleHeight = container.height / content.height;
|
||||
const scale = Math.min(scaleWidth, scaleHeight);
|
||||
|
||||
// 计算居中偏移量
|
||||
const offsetX = (container.width - content.width * scale) / 2;
|
||||
const offsetY = (container.height - content.height * scale) / 2;
|
||||
|
||||
return { scale, offsetX, offsetY } as contaiaCalseSize;
|
||||
}
|
||||
function mapContentCoordinatesToContainer(
|
||||
contentX : number,
|
||||
contentY : number,
|
||||
calcsize : contaiaCalseSize
|
||||
) : contaiPointXy {
|
||||
// 映射内容层坐标到容器层坐标
|
||||
const containerX = contentX * calcsize.scale + calcsize.offsetX;
|
||||
const containerY = contentY * calcsize.scale + calcsize.offsetY;
|
||||
|
||||
return { x: containerX, y: containerY } as contaiPointXy;
|
||||
}
|
||||
|
||||
let rc = scaleAndCenterContent(
|
||||
{ width: c_w, height: c_h } as contaiaSize,
|
||||
{ width: image_h, height: image_w } as contaiaSize
|
||||
)
|
||||
let result = mapContentCoordinatesToContainer(image_point_y, image_point_x, rc)
|
||||
|
||||
let cp = new CGPoint(x = (c_w - result.x).toInt(), y = result.y.toInt());
|
||||
return cp;
|
||||
}
|
||||
function calcPoint(c_w : number, c_h : number, image_w : number, image_h : number, image_point_x : number, image_point_y : number) {
|
||||
// 图片被顺时旋转了90度
|
||||
let newconverxy = getCorrectionMatrix(c_w, c_h, image_w, image_h, image_point_x, image_point_y)
|
||||
return {
|
||||
x: newconverxy.x, y: newconverxy.y, minx: 0, maxx: 0, miny: 0, maxy: 0
|
||||
} as ConverXy
|
||||
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>我们需要您的相机权限来拍照或录像。</string>
|
||||
</dict>
|
||||
</plist>
|
||||
Reference in New Issue
Block a user