• iOS自定义拍照框拍照&裁剪(二)


    计算裁剪区域

    现在我们已经得到了一个正向的图片, 如果我们的相机框并非在中心区域,如图,我们如何得到想要的结果? 我的解决思路是,把拍照框裁剪问题 拆分成三个问题

    1.相片旋转问题,这个在“iOS自定义拍照框拍照&裁剪(一)”已经处理

    2.裁剪区域问题。

    3.预览显示问题。

    • 裁剪区域问题

    1.计算出拍照背景框相对于屏幕的区域。假设为(12,193,200,100)

    2.计算相对于图片的区域。如图(12,120,200,100)

    3.根据屏幕旋转方向计算相对于 修正方向后的图片的裁剪区域,如果是水平拍摄的图片,此裁剪区域需要计算转换。(445,12,100,200)

    4.根据屏幕内图片的裁剪区域,计算相对于原始图片大小范围内的裁剪区域。

    5.根据裁剪区域使用CGImageRef imageRef = CGImageCreateWithImageInRect(image.CGImage, rect);获取裁剪后的图片数据。

    图片名称
    • 预览显示问题

    1.使用上述计算中得到的 屏幕内图片的裁剪区域clipRC,计算预览图片是的相机框显示区域。如果本身是竖直显示的图片,则 clipRC 即是 预览显示时相机框的预期。如果是横着显示的照片,则需要对clipRC进行等比压缩计算。如下图

    2.根据屏幕旋转方向获取预览显示拍照框图片,比如横着显示的图片,拍照框也需要旋转90度。如下图

    图片名称
    • 关键代码实现

    计算背景框相对于当前设备大小的CGRect

    //根据拍照时手机的方向计算相对于设备大小范围内的图片裁剪区域
    -(CGRect)calculateClipRectWithImage:(UIImage *)finalImage{
        //设置成图片大小比例
        CGFloat imageWidth = CGImageGetWidth(finalImage.CGImage);
        CGFloat imageHeight = CGImageGetHeight(finalImage.CGImage);
        
        CGFloat maxH = imageWidth < imageHeight ? imageHeight : imageWidth;
        CGFloat minW = imageWidth < imageHeight ? imageWidth : imageHeight;
        //图片竖直显示时候的高度
        CGFloat imagePortraitHeight = [UIScreen mainScreen].bounds.size.width * maxH/minW;
        //图片竖直显示时,相对于父类的坐标区域
        CGRect portraitRect = CGRectMake(0, ([UIScreen mainScreen].bounds.size.height - imagePortraitHeight)/2, [UIScreen mainScreen].bounds.size.width, imagePortraitHeight);
        
        //裁剪区域 -- 相对于父视图的坐标区域
        CGRect diyFrameRC = [CTPDIYFrameCameraVideoViewModel transRCFromDIYFrame:self.diyFrame];
        //转换成相对于图片imgRC为坐标参考的坐标区域
        CGRect clipRC = [self convertRC:diyFrameRC toCoordinateSpace:portraitRect];
        
        CGRect imageRC = CGRectMake(0, 0, portraitRect.size.width, portraitRect.size.height);
        //根据屏幕方向计算,图片显示在当前屏幕范围内的裁剪区域
        diyFrameRC = [self calculateRect:clipRC contentRC:imageRC];
    
        return diyFrameRC;
    }
    
    /**
    坐标转换,将相机框的坐标转换成相对于图片的Rect
    相同坐标空间下的两个RC,将sourceRC转换成相对于destRC的裁剪区域
    */
    -(CGRect)convertRC:(CGRect)sourceRC toCoordinateSpace:(CGRect)destRC{
        CGRect rc = CGRectMake(0, 0, sourceRC.size.width, sourceRC.size.height);
        CGFloat x = sourceRC.origin.x - destRC.origin.x;
        CGFloat y = sourceRC.origin.y - destRC.origin.y;
        //相对于父视图的交集区域RC
    //    rc = CGRectIntersection(destRC,sourceRC);
    //    rc.origin.x = x > 0 ? x : 0;
    //    rc.origin.y = y > 0 ? y : 0;
        rc.origin.x = x;
        rc.origin.y = y;
        return rc;
    }
    
    /**
     *根据屏幕旋转方向计算裁剪区域,oldRC必须是基于contentRC坐标系的值
     *oldRC:为转换前的裁剪区域
     *contentRC:为竖直方向的内容显示区域
     *返回转换后的区域。
     */
    -(CGRect)calculateRect:(CGRect)oldRc contentRC:(CGRect)contentRC{
        CGSize newSize = CGSizeMake(oldRc.size.height, oldRc.size.width);
        CGSize newContentSize = CGSizeMake(contentRC.size.height, contentRC.size.width);
        NSInteger newX = 0;
        NSInteger newY = 0;
        //裁剪图片采用的设备坐标系,左上角为原点。注意变换的移动和旋转方向
        if (self.shootingOrientation == UIDeviceOrientationPortraitUpsideDown) {
            //当前视图的右下角区域
            newSize = oldRc.size;
            newContentSize = contentRC.size;
            newX = newContentSize.width - newSize.width - oldRc.origin.x;
            newY = newContentSize.height - newSize.height - oldRc.origin.y;
        }else if (self.shootingOrientation == UIDeviceOrientationLandscapeLeft){
            //当前视图的左下角
            newX = oldRc.origin.y;
            newY = newContentSize.height - newSize.height - oldRc.origin.x;
        }else if(self.shootingOrientation == UIDeviceOrientationLandscapeRight){
            //当前视图的右上角
            newX = newContentSize.width - newSize.width - oldRc.origin.y;
            newY = oldRc.origin.x;
        }else if(self.shootingOrientation == UIDeviceOrientationPortrait){
            //竖直拍照,返回
            newSize = oldRc.size;
            newContentSize = contentRC.size;
            newX = oldRc.origin.x;
            newY = oldRc.origin.y;
        }
        //前置摄像头左右转换。
        if (self.devicePosition == AVCaptureDevicePositionFront) {
            //前置摄像头在上述计算后的基础之上沿Y轴翻转
            newX = newContentSize.width - newSize.width - newX;
        }
        //新的坐标点
        CGRect newRC = CGRectMake(newX, newY, newSize.width, newSize.height);
        return newRC;
    }
    

    计算显示区域

    /**
     * 计算拍照完成之后,显示在屏幕的 裁剪区域
     * clipRC:相对于竖直屏幕根据手机方向旋转之后的 裁剪区域。
     */
    -(CGRect)calculateNewDIYImageFrameWithImage:(UIImage *)finalImage
                                                   ClipRC:(CGRect)diyFrameRC{
        
        CGFloat imageWidth = self.photoPreviewImageView.bounds.size.width;
        CGFloat imageHeight = self.photoPreviewImageView.bounds.size.height;
        
        //计算摄像头竖直拍照时,相对于原图大小的裁剪区域
        CGFloat scale = 1.0;
        if (imageWidth > imageHeight) {
            //横向显示的照片
            scale = imageHeight/[UIScreen mainScreen].bounds.size.width;
        }
        
        CGRect clipRC = CGRectMake((NSInteger)(diyFrameRC.origin.x * scale), (NSInteger)(diyFrameRC.origin.y * scale), (NSInteger)(diyFrameRC.size.width * scale), (NSInteger)(diyFrameRC.size.height * scale));
        
        //设置成图片大小比例
        //裁剪区域 -- 相对于父视图的坐标区域
        CGFloat newX = self.photoPreviewImageView.frame.origin.x + clipRC.origin.x;
        CGFloat newY = self.photoPreviewImageView.frame.origin.y + clipRC.origin.y;
        //显示区域
        CGRect newRC = CGRectMake(newX, newY, clipRC.size.width, clipRC.size.height);
        
        return newRC;
    }
    /**
    根据屏幕方向,设置图片的orientation,获取方向正确的拍照框图片。
    */
    -(UIImage *)changeImageOrigin{
    	//拍照框
        UIImage *image = self.frameImage;
        if (self.shootingOrientation == UIDeviceOrientationLandscapeLeft) {
            image = [UIImage imageWithCGImage:self.frameImage.CGImage scale:[UIScreen mainScreen].scale orientation:UIImageOrientationLeft];
        }else if (self.shootingOrientation == UIDeviceOrientationLandscapeRight){
            image = [UIImage imageWithCGImage:self.frameImage.CGImage scale:[UIScreen mainScreen].scale orientation:UIImageOrientationRight];
            
        }else if (self.shootingOrientation == UIDeviceOrientationPortraitUpsideDown){
            image = [UIImage imageWithCGImage:self.frameImage.CGImage scale:[UIScreen mainScreen].scale orientation:UIImageOrientationDown];
        }
        
        if (self.devicePosition == AVCaptureDevicePositionFront) {
            //前置摄像头Y轴方向翻转,
            if (self.shootingOrientation == UIDeviceOrientationLandscapeRight) {
                image = [UIImage imageWithCGImage:self.frameImage.CGImage scale:[UIScreen mainScreen].scale orientation:UIImageOrientationLeft];
            }
            if (self.shootingOrientation == UIDeviceOrientationLandscapeLeft) {
                image = [UIImage imageWithCGImage:self.frameImage.CGImage scale:[UIScreen mainScreen].scale orientation:UIImageOrientationRight];
            }
        }
        return image;
    }
    
  • 相关阅读:
    C#动态调用WCF接口(2)
    WCF 动态调用(1)
    WCF信道工厂Channel Factory
    使用 ObjectDataSource 缓存数据
    DatabaseFactory.CreateDatabase 方法操作数据库
    大学毕业4年-回想和总结(5)-投资理財方法论
    用递归法将一个整数n转换成字符串。
    朴素贝叶斯
    Android热补丁技术—dexposed原理简析(阿里Hao)
    SQLAlchemy使用笔记--SQLAlchemy ORM(三)
  • 原文地址:https://www.cnblogs.com/xiongwj0910/p/15409959.html
Copyright © 2020-2023  润新知