文章目錄
  1. 1. 场景
  2. 2. 问题
  3. 3. 解决方案
    1. 3.1. 1.初始化
    2. 3.2. 2.平移
    3. 3.3. 3.缩放
    4. 3.4. 4.旋转

先前封装了一个C++的跨平台画刷库,底层绘制部分用OpenGL实现。现在想将该库作为内核开发iOS上的应用,却在坐标系转换上出现了点麻烦。这里将问题和解决方式做下记录。

场景

获取iOS设备上相册里的图片,在平移/缩放/旋转等编辑操作后,调用内核绘制图片,保证视觉上其显示效果(图片位置和图片内容)不改变:

  • 图片编辑时,UIView接受手势,用UIKit实时展现图片的变换状态;
  • 编辑完成时,将图片和变换矩阵传入内核,利用OpenGL绘制最终状态。

问题

UIKitOpenGL的坐标系不相同:

  • UIKit的坐标系:以左上角为原点,X轴向右,Y轴向下(右图);
  • OpenGL的坐标系:以左下角为原点,X轴向右,Y轴向上(左图);
    图片来源于网络

解决方案

图片编辑过程中,响应手势并通过图片的centertransform动态更新显示;同时将所有图片的变换累积到一个变换矩阵imageTransform中;在编辑完成时内核利用imageTransform将图片正确绘制。需要注意的是:

centertransformUIKit坐标系,imageTransformOpenGL坐标系。

1.初始化

在进入图片编辑模块时,初始化imageTransform

1
self.imageTransform = CGAffineTransformIdentity;

2.平移

响应pan手势。由于两个坐标系的Y轴方向相反,所以imageTransform累积平移变换时,需要将Y轴方向变化量取反

1
-(void)panImage:(UIPanGestureRecognizer*)pan {
	if (pan.state == UIGestureRecognizerStateChanged) {
  		/// 改变图片显示状态
  		CGPoint loc = [pan translationInView:self.view];
  		[pan setTranslation:CGPointZero inView:self.view];
  		self.imageView.center = CGPointMake(self.imageView.center.x+loc.x, self.imageView.center.y+loc.y);

  		/// 累积平移矩阵
  		CGAffineTransform tX = CGAffineTransformIdentity;
  		tX = CGAffineTransformTranslate(tX, loc.x, -loc.y);
  		self.imageTransform = CGAffineTransformConcat(self.imageTransform, tX);
  	}
}

3.缩放

UIView的缩放默认是以其中心点为固定点,所以imageTransform累积缩放变换时,需要先将图片移动到原点位置,进行缩放后再恢复

1
-(void)pinchImage:(UIPinchGestureRecognizer*)pinch{    
    if (pinch.state == UIGestureRecognizerStateChanged) {
		/// 改变图片显示状态
        self.imageView.transform = CGAffineTransformScale(self.imageView.transform, pinch.scale, pinch.scale);
        	
    	/// 累积平移矩阵
    	CGPoint pivot = self.imageView.center;
        pivot.y = self.view.bounds.size.height - pivot.y;
        CGAffineTransform tX = CGAffineTransformIdentity; 
        tX = CGAffineTransformIdentity;
        tX = CGAffineTransformTranslate(tX, pivot.x, pivot.y);
        tX = CGAffineTransformScale(tX, pinch.scale, pinch.scale);
        tX = CGAffineTransformTranslate(tX, -pivot.x, -pivot.y)
        self.imageTransform = CGAffineTransformConcat(self.imageTransform, tX);
 
 		/// 充值缩放因子
        pinch.scale = 1;
    }
}

4.旋转

在UIKit的坐标系中,由X轴正方向朝Y轴正方向旋转n弧度(视觉上是逆时针),相当于在OpenGL坐标系中由X轴正方向朝Y轴正方向旋转PI-n弧度(视觉上是顺时针)。此时得到的图片只是旋转角度正确了,最后还需要对图片进行相对于图片中心的翻转才能得到一样的图片内容。

1
-(void)rotateImage:(UIRotationGestureRecognizer*)rotate{    
    if (rotate == UIGestureRecognizerStateChanged) {
        /// 改变图片显示状态
        CGFloat angle = rotate.rotation;
        self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, angle);
        
        /// 累积平移矩阵
        CGPoint pivot = self.imageView.center;
        pivot.y = self.view.bounds.size.height - pivot.y;
        CGAffineTransform tX = CGAffineTransformIdentity;
        tX = CGAffineTransformTranslate(tX, pivot.x, pivot.y);
        tX = CGAffineTransformRotate(tX, M_PI-angle);
        tX = CGAffineTransformScale(tX, -1, -1);	//中心翻转
        tX = CGAffineTransformTranslate(tX, -pivot.x, -pivot.y);
        self.imageTransform = CGAffineTransformConcat(self.imageTransform, tX);
        
        // 重置旋转角度
        rotate.rotation = 0;  
    }
}
文章目錄
  1. 1. 场景
  2. 2. 问题
  3. 3. 解决方案
    1. 3.1. 1.初始化
    2. 3.2. 2.平移
    3. 3.3. 3.缩放
    4. 3.4. 4.旋转