标签:
Quartz 2D 绘制模型定义了两种独立的坐标空间:用户空间(用于表现文档页)和设备空间(用于表现设备的本地分辨率)。
当我们需要一个点或者显示文档时, Quartz 会将用户空间坐标系统映射到设备空间坐标系统。当前映射关系的变换矩阵称为 CTM(current transformation matrix)。
我们可能使用 Quartz 内置的变换函数来操作 CTM 来方便的实现平移、旋转和缩放我们的绘图。
我们可以不需要了解变换矩阵的数学含义而使用这些函数。
调用 CGContextDrawImage (context, CGRectMake(0, 0, 50, 50), [UIImage imageNamed:@"image"].CGImage) 来绘制图片。
调用 CGContextTranslateCTM(context, 100, 200) 来平移绘制图片。
调用 CGContextRotateCTM(context, M_PI_4) 来旋转绘制图片。
调用 CGContextScaleCTM(context, 0.5, 0.8) 来缩放绘制图片。
联合变换将两个矩阵相乘来联接现价变换操作。调用 CGContextConcatCTM(context, affineTransform) 来实现联合变换或直接执行两个或多个变换操作而不恢复图形状态。
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 100, 200);//#1
CGContextRotateCTM(context, M_PI_4);
CGAffineTransform affineTransform = CGAffineTransformMakeScale(0.5, 0.8);//#2
CGContextConcatCTM(context, affineTransform);
CGContextDrawImage (context, CGRectMake(0, 0, 100, 100), [UIImage imageNamed:@"image"].CGImage);
CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(100, 200);
CGAffineTransform rotateTransform = CGAffineTransformMakeRotation(M_PI_4);
CGAffineTransform scaleTransform = CGAffineTransformMakeScale(0.5, 0.8);
CGContextConcatCTM(context, translateTransform);
CGContextConcatCTM(context, rotateTransform);
CGContextConcatCTM(context, scaleTransform);
CGAffineTransform translateTransform = CGAffineTransformTranslate(CGContextGetCTM(context), 100, 200);
CGAffineTransform rotateTransform = CGAffineTransformRotate(translateTransform, M_PI_4);
CGAffineTransform scaleTransform = CGAffineTransformScale(rotateTransform, 0.5, 0.8);
CGContextConcatCTM(context, scaleTransform);
CGAffineTransform invertTransform = CGAffineTransformInvert(scaleTransform);
CGContextConcatCTM(context, invertTransform);
CGPoint applyPoint = CGPointApplyAffineTransform(CGPointMake(50, 50), translateTransform);
CGSize applySize = CGSizeApplyAffineTransform(CGSizeMake(100, 100), translateTransform);
CGRect applyRect = CGRectApplyAffineTransform(CGRectMake(0, 0, 100, 100), translateTransform);
调用 CGAffineTransformEqualToTransform(invertTransform, scaleTransform) 判断仿射变换是否相等。
调用 CGAffineTransformIsIdentity(invertTransform) 判断仿射变换是否为单位变换。单位变换没有平移、缩放和旋转操作。
Quartz 常量 CGAffineTransformIdentity 表示一个单位变换。
CGAffineTransformIsIdentity(invertTransform);
CGAffineTransformEqualToTransform(invertTransform, CGAffineTransformIdentity);
CGAffineTransform userToDeviceTransform = CGContextGetUserSpaceToDeviceSpaceTransform(context);
CGPoint deviceSpacePoint = CGContextConvertPointToDeviceSpace(context, CGPointMake(100, 200));
CGPoint userSpacePoint = CGContextConvertPointToUserSpace(context, deviceSpacePoint);
CGSize deviceSpaceSize = CGContextConvertSizeToDeviceSpace(context, CGSizeMake(100, 100));
CGSize userSpaceSize = CGContextConvertSizeToUserSpace(context, deviceSpaceSize);
CGRect deviceSpaceRect = CGContextConvertRectToDeviceSpace(context, CGRectMake(100, 200, 100, 100));
CGRect userSpaceRect = CGContextConvertRectToUserSpace(context, deviceSpaceRect);
图案(Pattern)是反复绘制的单元,这些绘制单元重复地绘制到一个图形上下文中。我们可以像使用颜色一样使用这些图案。
当我们使用图案来绘制时,Quartz 将页面分割成图案单元的集合,其中每个单元的大小为图案的大小,然后使用我们提供的回调函数来绘制这些单元格。
我们可以指定水平和竖直方向上两个图案单元之间的间距。我们也可以指定间距为负数,这样图案单元便会重叠。
当我们绘制一个图案单元时,Quartz 使用图案空间的坐标系统。图案空间是一个抽象空间,它会使用我们创建图案时指定的变换矩阵来映射到默认用户空间。我们可以修改变换矩阵来实现有趣的效果。
当我们在图案空间上应用变换时,Quartz 只将变换应用于图案空间。
图案空间坐标系与 UIKit 创建的图形上下文坐标系不同。当图案被应用到 UIKit 创建的图形上下文时,图案空间会自己进行适配。
在 Quartz 2D 中,我们可以创建着色图案和模板图案两种图案。
着色图案有与其相关的固有颜色。着色图案中的颜色是图案单元创建流程的一部分,而不是绘制流程的一部分。
其它图案只限定了形状,颜色值是在绘制过程中指定的,而不是创建过程。因此可以被认为是模板图案(或者是非着色图案、甚至图像蒙板)。
平铺是将模式单元格绘制到页面的某个部分的过程。
用户空间定义的图案单元在渲染到设备时可能无法精确匹配,这是由用户空间单元和设备像素之间的差异导致的。所以 Quartz 提供了三个平铺选项,以在必要时调整。
无失真平铺: 以细微调整图案单元之间的间距为代价,但通常不超过一个设备像素。
最小失真恒定间距平铺:设定图案单元之间的间距,以细微调整单元大小为代价,但通常不超过一个设备像素。
恒定间距平铺:设定图案单元之间的间距,以调整图案单元大小为代价,以求尽快的平铺。
我们可以像使用 CGContextSetFillColor(context, colors) 一样来使用 CGContextSetFillPattern(context, pattern, colors) 给上下文的路径进行填充或描边。
当我们使用图案填充或描边时,Quartz 会按照以下指令来绘制每个图案单元。
void MyDrawColoredPattern (void *info, CGContextRef myContext) {
CGFloat subunit = 5;
CGSize size = {subunit, subunit};
CGPoint point1 = {0,0}, point2 = {subunit, subunit}, point3 = {0,subunit}, point4 = {subunit,0};
CGRect myRect1 = {point1, size}, myRect2 = {point2, size}, myRect3 = {point3, size}, myRect4 = {point4, size};
CGContextSetRGBFillColor(myContext, 0, 0, 1, 0.5);
CGContextFillRect(myContext, myRect1);
CGContextSetRGBFillColor(myContext, 1, 0, 0, 0.5);
CGContextFillRect(myContext, myRect2);
CGContextSetRGBFillColor(myContext, 0, 1, 0, 0.5);
CGContextFillRect(myContext, myRect3);
CGContextSetRGBFillColor(myContext, .5, 0, .5, 0.5);
CGContextFillRect(myContext, myRect4);
}
CGColorSpaceRef patternSpace;
patternSpace = CGColorSpaceCreatePattern(NULL); // 创建模式颜色空间,并传递NULL作为参数
CGContextSetFillColorSpace(myContext, patternSpace); // 在模式颜色空间中设置填充颜色
CGColorSpaceRelease(patternSpace);
CGPatternRef CGPatternCreate(void *info,
CGRect bounds,
CGAffineTransform matrix,
CGFloat xStep,
CGFloat yStep,
CGPatternTiling tiling,
bool isColored,
const CGPatternCallbacks *callbacks);
struct CGPatternCallbacks {
unsigned int version;
CGPatternDrawPatternCallback __nullable drawPattern;
CGPatternReleaseInfoCallback __nullable releaseInfo;
};
typedef struct CGPatternCallbacks CGPatternCallbacks;
7.1 version 为结构体的版本号,可以设置为 0。
7.2 drawPattern 指向绘制回调的指针。
7.3 releaseInfo 指向一个回调函数,该回调在释放 CGPattern 对象时被调用,以释放存储在我们传递给绘制回调的 info 参数中的数据。如果在这个参数中没有传递任何数据,则设置该域为 NULL。
调用 CGContextSetFillPattern(context, pattern, &alpha) 或 CGContextSetStrokePattern(context, pattern, &alpha) 来使用图案进行填充或描边。
第三个参数为颜色数组。虽然着色图案提供了自己的颜色,我们仍然需要传递一个单一的 alpha 值来告诉 Quartz 在绘制时着色图案的透明度。
#define H_PATTERN_SIZE 10
#define V_PATTERN_SIZE 10
#define H_PSIZE H_PATTERN_SIZE + 5
#define V_PSIZE H_PATTERN_SIZE + 10
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
MyColoredPatternPainting(context, self.bounds);
}
void MyDrawColoredPattern(void *info, CGContextRef myContext) {
CGFloat subunit = 5;
CGSize size = {subunit, subunit};
CGPoint point1 = {0,0}, point2 = {subunit, subunit}, point3 = {0,subunit}, point4 = {subunit,0};
CGRect myRect1 = {point1, size}, myRect2 = {point2, size}, myRect3 = {point3, size}, myRect4 = {point4, size};
CGContextSetRGBFillColor (myContext, 0, 0, 1, 0.5);
CGContextFillRect (myContext, myRect1);
CGContextSetRGBFillColor (myContext, 1, 0, 0, 0.5);
CGContextFillRect (myContext, myRect2);
CGContextSetRGBFillColor (myContext, 0, 1, 0, 0.5);
CGContextFillRect (myContext, myRect3);
CGContextSetRGBFillColor (myContext, .5, 0, .5, 0.5);
CGContextFillRect (myContext, myRect4);
}
void MyColoredPatternPainting(CGContextRef myContext, CGRect rect) {
CGPatternRef pattern;
CGColorSpaceRef patternSpace;
CGFloat alpha = 1;
static const CGPatternCallbacks callbacks = {0, &MyDrawColoredPattern, NULL};
CGContextSaveGState(myContext);
patternSpace = CGColorSpaceCreatePattern(NULL);
CGContextSetFillColorSpace(myContext, patternSpace);
CGColorSpaceRelease(patternSpace);
pattern = CGPatternCreate(