先前封装了一个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;  
    }
}

  • 突然兴致大发地想自己整个博客,用来纪录工作上的成长和收获。于是,我就花了些时间学着用HexoGitHub Pages搭起了这个。
  • 很早就有想法写博客,也一直因为各种原因没做好。这次不想那么多了,能写多少算多少,能写成怎样算怎样。不付诸行动的话,再美好的憧憬都是扯淡。
  • 目前的博客就是采用最简单的模版,所有的博文也暂时不进行分类。不过内容主要都是工作上的,大概分为:
    • 开发技术
    • 项目管理
    • 工作感悟
    • 其他随笔
  • 这次兴致大发的起因是想将调试了几天代码取得的一点收获纪录下来的,结果现在还没整理好。我先随便写这么点,试试Hexo,也熟悉一下Markdown