• 一文弄懂CGAffineTransform和CTM


    一文弄懂CGAffineTransform和CTM
    • 一些概念

    坐标空间(系):视图(View)坐标空间与绘制(draw)坐标空间
    CTM:全称current transformation matrix,看名称 “当前变换矩阵” 也就是矩阵。
    CGAffineTransform:是一个具体的矩阵数据值。CGAffineTransform是CTM的具体值。
    • 关于矩阵变换

    相同CGAffineTransform作用于不同的坐标空间,其结果不一样。

    移动:

    视图空间 中心为原点,向右为x递增,向下y递增,CGAffineTransformMakeTranslation(-75, 25);  左移75,下移25
    绘制空间 左下点为原点,向右为x递增,向上y递增,CGAffineTransformMakeTranslation(-75, 25);  左移75,上移25

    视图空间示例:_demoView.transform = CGAffineTransformMakeTranslation(-75, 25);

    绘制空间示例:
    CGContextConcatCTM(ctx, CGAffineTransformMakeTranslation(-75, 25));
    CGContextDrawImage(ctx, CGRectMake(0, 0, imageWidth, imageHeight), self.image.CGImage);


    旋转:
    视图空间 中心为原点,向右为x递增,向下y递增, transform = CGAffineTransformRotate(transform, -M_PI_2); 围绕中心点,逆时针旋转90度
    绘制空间 左下点为原点,向右为x递增,向上y递增 transform = CGAffineTransformRotate(transform, -M_PI_2); 围绕左下角点,顺时针旋转90度

    视图空间示例:_demoView.transform = CGAffineTransformRotate(transform, -M_PI_2);

    绘制空间示例:
    CGContextConcatCTM(ctx, CGAffineTransformRotate(transform, -M_PI_2););
    CGContextDrawImage(ctx, CGRectMake(0, 0, imageWidth, imageHeight), self.image.CGImage);

    缩放:
    视图空间 默认以中心点为原点 transform = CGAffineTransformMakeScale(1, -1); 沿着中心X轴线竖直翻转
    绘制空间 默认以左下角为原点 transform = CGAffineTransformMakeScale(1, -1); 沿着X轴横线竖直翻转

    视图空间示例:_demoView.transform = CGAffineTransformMakeScale(1, -1);

    绘制空间示例:
    CGContextConcatCTM(ctx, CGAffineTransformMakeScale(1, -1));
    CGContextDrawImage(ctx, CGRectMake(0, 0, imageWidth, imageHeight), self.image.CGImage);

    如果我们现在想要把上面的图片逆时针旋转90度,可以使用CGAffineTransform组合的方式。这里提供两种转换方式,都是采用的平移、旋转,执行顺序不同导致,给的参数也不同。后续说明原因。
    方法一:

    //移动到屏幕右边
    transform = CGAffineTransformTranslate(transform, imageHeight,0);
    //逆时针旋转90度
    transform = CGAffineTransformRotate(transform, M_PI_2);
    //将transform作用于context
    CGContextConcatCTM(ctx, transform);
    CGContextDrawImage(ctx, CGRectMake(0, 0, imageWidth, imageHeight), self.image.CGImage);


    方法二:
    //逆时针旋转90度
    transform = CGAffineTransformRotate(transform, M_PI_2);
    //右移图片高度的距离
    transform = CGAffineTransformTranslate(transform, 0,-imageHeight);
    CGContextConcatCTM(ctx, transform);
    CGContextDrawImage(ctx, CGRectMake(0, 0, imageWidth, imageHeight), self.image.CGImage);

    执行结果如下
    CGAffineTransformRotate组合坐标系问题
    注意:所有的变换都会影响到坐标系产生新的坐标系。比如:旋转转换,坐标系也跟着旋转,按照X轴翻转缩放,坐标系也会翻转。
    解释一下逆时针旋转图片 方法一 和 方法二
    方法一比较容易理解,
    1.右移图片宽度
    2.左下角 为圆心,逆时针旋转90度。


    重点说明下方法二
    1.逆时针旋转90度
    2.右移图片高度。

    逆时针旋转大家容易理解,但是,右移图片高度为什么是transform = CGAffineTransformTranslate(transform, 0,-imageHeight);???
    谜底是,逆时针旋转的时候,坐标系也跟着逆时针旋转90度,变成了右下角为原点,y左边递增,右边递减。x上方向递增,下方向递减。所以此时想要把图片向右边移动-imageHeight的距离,按照新的坐标系,就是往y轴的递减方向走。也就有了transform = CGAffineTransformTranslate(transform, 0,-imageHeight);

    • 关于CGContextDrawImage

    DrawImage绘制什么情况是颠倒的,什么情况不是颠倒的,坐标系又是什么样的?

    使用UIGraphicsGetCurrentContext()获取的上下文,CGContextDrawImage是颠倒的。想要正向的图片需要做CTM变换。
    -(void)drawImage{
        
        CGFloat imageWidth = CGImageGetWidth(self.image.CGImage);
        CGFloat imageHeight = CGImageGetHeight(self.image.CGImage);
        
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(imageWidth, imageHeight), 0, [UIScreen mainScreen].scale);
        CGContextRef context = UIGraphicsGetCurrentContext();

    //,为了保证正向显示图片,需要先上移图片高度,再沿X轴翻转。
    //    CGContextTranslateCTM(context, 0, imageHeight);
    //    CGContextScaleCTM(context, 1, -1);
    // 使用转换之后的坐标系绘制图片
        CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), self.image.CGImage);
        
        UIImage *newImg = UIGraphicsGetImageFromCurrentImageContext();
        
        UIGraphicsEndImageContext();
        
        self.imageView.image = newImg;
    }

     自己创建位图,再调用CGContextDrawImage,并不会出现上下颠倒的问题。
    -(void)drawImage{
        
        CGFloat imageWidth = CGImageGetWidth(self.image.CGImage);
        CGFloat imageHeight = CGImageGetHeight(self.image.CGImage);
        
        //创建位图上下文
        CGContextRef ctx = CGBitmapContextCreate(NULL, imageHeight,imageWidth,
                                                 CGImageGetBitsPerComponent(self.image.CGImage), 0,
                                                 CGImageGetColorSpace(self.image.CGImage),
                                                 CGImageGetBitmapInfo(self.image.CGImage));
        //这里drawImage是正的。
        CGContextDrawImage(ctx, CGRectMake(0, 0, imageWidth, imageHeight), self.image.CGImage);
        CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
        UIImage *img = [UIImage imageWithCGImage:cgimg];
        CGContextRelease(ctx);
        CGImageRelease(cgimg);
        self.imageView.image = img;
        return ;
    }

    CGContextDrawImage的坐标系
    默认是以左下角为坐标原点开始绘制,但是,But,通过一系列的CTM转换之后,最终的绘制坐标CGRectMake(0, 0, imageWidth, imageHeight)是根据新的坐标系计算的。比如逆时针旋转90度的例子,新坐标系 原点为右下角,y左边递增,右边递减。x上方向递增

    CGContextDrawImage(ctx, CGRectMake(100, 100, imageWidth, imageHeight), self.image.CGImage);

    关于CGRectApplyAffineTransform转换CGRect

    • 关于CGRectApplyAffineTransform转换CGRect

    CGrectApplyAffineTrans对于平移、缩放、旋转的表现情况


    CGRectApplyAffineTransform(CGRect rect, CGAffineTransform t) 在当前坐标系,对rect做仿射变换之后,得到的新rect。
    //平移
        CGRect rc = CGRectMake(100, 0, 100, 200);
        //打印结果为(200, 100, 100, 200);
        CGRect newRC = CGRectApplyAffineTransform(rc, CGAffineTransformMakeTranslation(100, 100));

    //缩放
        CGRect rc = CGRectMake(100, 0, 100, 200);
        //打印结果为 (200, 0, 200, 400); 所有值都乘了2
        CGRect newRC = CGRectApplyAffineTransform(rc, CGAffineTransformMakeScale(2, 2));


    //旋转
        CGAffineTransform transform = CGAffineTransformMakeTranslation(0, 0);
        transform = CGAffineTransformRotate(transform, -M_PI_2);
        
        CGRect rc = CGRectMake(100, 0, 100, 200);
        //打印结果为(0, -200, 200, 100); 得到的是相对于(0,0)旋转90度的值
        CGRect newRC = CGRectApplyAffineTransform(rc, transform);
       

    //平移 + 缩放
        CGAffineTransform transform = CGAffineTransformMakeTranslation(100, 100);
        transform = CGAffineTransformScale(transform, 2, 2);
        CGRect rc = CGRectMake(100, 0, 100, 200);
        //打印为(300, 100, 200, 400); 先计算了缩放,再计算平移得到此值
        CGRect newRC = CGRectApplyAffineTransform(rc, transform);

    //平移+旋转
        CGAffineTransform transform = CGAffineTransformMakeTranslation(100, 100);
        transform = CGAffineTransformRotate(transform, -M_PI_2);
        
        CGRect rc = CGRectMake(100, 0, 100, 200);
        //打印为(100, -100, 200, 100);  先计算了旋转,再计算平移得到此值
        CGRect newRC = CGRectApplyAffineTransform(rc, transform);

    //平移 + 旋转 + 缩放

        CGAffineTransform transform = CGAffineTransformMakeTranslation(100, 100);
        transform = CGAffineTransformRotate(transform, -M_PI_2);
        transform = CGAffineTransformScale(transform, 2, 2);
        CGRect rc = CGRectMake(100, 0, 100, 200);
        //打印为 (100, -300, 400, 200); 先计算了旋转/缩放,再计算平移得到此值
        CGRect newRC = CGRectApplyAffineTransform(rc, transform);

    总结:
        平移:CGRectApplyAffineTransform对于平移转换是与实际变换结果一致的。
        旋转:以当前坐标系原点(0,0)进行旋转计算后的值。
        缩放:得到的结果为,rect中的各个值乘以缩放比例。
        组合变换:先计算旋转和缩放,最后计算平移

  • 相关阅读:
    QT槽和信号机制介绍
    J2EE面试题之二
    .net 面试题系列文章一(附答案)
    SUN JAVA面试笔试题
    用友在清华的笔试题
    Java面试笔试题整理
    雅虎公司C#笔试题(之一)
    Oracle面试题及答案整理
    一些著名的大公司JAVA面试题目
    百度技术研发笔试题目1
  • 原文地址:https://www.cnblogs.com/xiongwj0910/p/15421646.html
Copyright © 2020-2023  润新知