1. 使用Quartz的API 创建Context,并且绘图
完全使用Quartz创建Context, 并且使用Quartz绘图的API, 此时, context坐标系是left-low原点, 并且Quartz绘制方法认为图片的坐标系也是LLO.
/**
CoreGraphic API -> LLO坐标系
CGBitmapContextCreate -> 创建 bitmap Context
CGBitmapContextCreateImage -> 获取 bitmap Image CGImageRef -> 转化成UIImage
用 CoreGraphic CGContextDrawImage 绘图
*/
-(void)draw1{
// 1. 创建颜色空间
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
float width = self.bounds.size.width;
float height = self.bounds.size.height;
int bitsPerComponent = 8;
//RGBA(的bytes) * bitsPerComponent *width
int bytesPerRow = 4 * 8 * bitsPerComponent * width;
// 2. 创建bitmapContext, 使用Quartz创建的Context,因此坐标系是LLO, 坐标原点是左下角
CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrderDefault);
// 3. 绘制背景
CGContextFillRect(context, self.bounds);
CGContextSetRGBFillColor(context, 1, 0, 0, 1);
CGContextSetRGBStrokeColor(context, 0, 1, 0, 1);
CGContextFillRect(context, CGRectMake(0, 0, 400, 400));
CGContextStrokeRect(context, CGRectMake(0, 0, width, height));
// 4. 获取UIKit图片, 并且使用Quartz绘制图片. 注意使用Quartz相关的api绘制图片时,只能传入UIImage的底层CGImage.
// 并且此时由于绘制图片API `CGContextDrawImage` 是Quartz API, 因此绘制方法表示图片的坐标原点也是LLO
UIImage *img=[UIImage imageNamed:@"1.jpg"];
CGContextDrawImage(context, CGRectMake(0, 0, 100, 100), img.CGImage);
// 5. 从context中获取CGImage, 并创建UIImage
CGImageRef cgimg = CGBitmapContextCreateImage(context);
UIImage *resultImg = [UIImage imageWithCGImage:cgimg];
// 6. 清理CoreGraphic 资源
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
CGImageRelease(cgimg);
self.imageView.image = resultImg;
}
绘制结果
2. 使用UIKit的API 创建Context, 并且绘图
UIKit绘图时, 通过UIKit的API创建Context, 此时Context的坐标原点是ULO, UIKit绘图的API认为图像的原点也是ULO. 值得注意的是,与Quartz API不同的是UIKit会维护一个Context Stack, 用于记录当前UIKit中各种状态(填充颜色 setFill, 绘线颜色setStroke等等)是作用于哪个Context的.
/**
UIKit API -> ULO 原点
UIGraphicsBeginImageContextWithOptions -> 创建bitmap Context, 并push 入 stack
UIGraphicsGetImageFromCurrentImageContext -> 获取 bitmap
*/
-(void)draw2 {
// 1. 创建 context, 并且将context push到UIKit维护的Context Stack
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);
// 2. 创建Path, 绘制背景和填充颜色
UIBezierPath *rect = [UIBezierPath bezierPathWithRect:self.bounds];
[[UIColor redColor] setFill]; // 作用于Context Stack 栈顶stack
[rect fill];
[[UIColor greenColor] setStroke]; // 作用于Context Stack 栈顶
[rect stroke];
// 3. 使用 UIKit 的drawing method,因此需要坐标系是 ULO, 这里使用 UIKit方法创建, 因此绘制的文字都是正的. 坐标原点是ULO
NSString *text= @"文字";
UIFont *font=[UIFont systemFontOfSize:14];
[text drawAtPoint:CGPointMake(100, 100) withAttributes:font.fontDescriptor.fontAttributes];
// 4. 用UIKit 绘制图像, context是UIKit创建的, 因此是ULO坐标系, (0,0,100,100)在左上角,并且图片是正向
UIImage *img=[UIImage imageNamed:@"1.jpg"];
[img drawInRect:CGRectMake(0, 0, 100, 100)];
// 5. 从当前context获取UIImage
UIImage *resultImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.imageView.image = resultImg;
}
3-1 使用Quartz创建Context, 使用UIKit绘图
使用Quartz创建的Context坐标系是LLO, 而使用UIKit的绘图API(绘制文字, 绘制图像) 依赖的坐标系是ULO. 隐藏图像和文字会倒置.
/**
CoreGraphic 创建 Context -> LLO
绘图使用 UIKit 绘制
*/
-(void)draw3_1 {
// 1. 创建CGContext使用的颜色空间
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
float width = self.bounds.size.width;
float height = self.bounds.size.height;
int bitsPerComponent = 8;
//RGBA*8*width
int bytesPerRow = 4 * 8 * bitsPerComponent * width;
// 2. 使用Quartz API创建CGContext, 坐标系是 LLO
CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrderDefault);
// 3. UIKit API的绘图方法. 需要首先将当前context push到 当前UIKit维护的context stack中!!!!!(此时绘图方法才知道绘制到哪个context)
UIGraphicsPushContext(context);
// 4. 填充颜色, 配置部分状态
CGContextFillRect(context, self.bounds);
CGContextSetRGBFillColor(context, 1, 0, 0, 1);
CGContextSetRGBStrokeColor(context, 0, 1, 0, 1);
CGContextFillRect(context, CGRectMake(0, 0, 400, 400));
CGContextStrokeRect(context, CGRectMake(0, 0, width, height));
[[UIColor redColor] setFill];
// 5. 使用UIKit方法绘制文字.
NSString *text= @"文字";
UIFont *font=[UIFont systemFontOfSize:14];
[text drawAtPoint:CGPointMake(100, 100) withAttributes:font.fontDescriptor.fontAttributes];
// 6. 使用UIKit方法绘制图像
UIImage *img=[UIImage imageNamed:@"1.jpg"];
[img drawInRect:CGRectMake(0, 0, 100, 100)];
// 7. 从当前context中获取UIImage
CGImageRef cgimg = CGBitmapContextCreateImage(context);
UIImage *resultImg = [UIImage imageWithCGImage:cgimg];
UIGraphicsPopContext();
// 8. 清理资源
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
CGImageRelease(cgimg);
self.imageView.image = resultImg;
}
3-2 使用Quartz创建Context, 使用UIKit绘图(修正坐标系)
3-1中由于Quartz创建的Context坐标系是LLO, 而UIKit绘制坐标系要求ULO, 只要两者不匹配. 因此在绘制之前要保证. 当前Context的坐标系与绘制方法依赖的坐标系相同. 因此在调用UIKit时,需要将Context坐标系转换成ULO, 绘制完成以后. 最好将该坐标系恢复原始坐标系.
-(void)draw3_2 {
// 1. 创建CGContext使用的颜色空间
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
float width = self.bounds.size.width;
float height = self.bounds.size.height;
int bitsPerComponent = 8;
//RGBA*8*width
int bytesPerRow = 4 * 8 * bitsPerComponent * width;
// 2. 使用Quartz API创建CGContext, 坐标系是 LLO
CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrderDefault);
// 3. UIKit API的绘图方法. 需要首先将当前context push到 当前UIKit维护的context stack中!!!!!(此时绘图方法才知道绘制到哪个context)
UIGraphicsPushContext(context);
// 4. 填充颜色, 配置部分状态
CGContextFillRect(context, self.bounds);
CGContextSetRGBFillColor(context, 1, 0, 0, 1);
CGContextSetRGBStrokeColor(context, 0, 1, 0, 1);
CGContextFillRect(context, CGRectMake(0, 0, 400, 400));
CGContextStrokeRect(context, CGRectMake(0, 0, width, height));
[[UIColor redColor] setFill];
// 5. 在使用UIKit绘制方法进行绘制之前,先将当前context的坐标系调整成ULO, 具体步骤如下: 翻转画布 -> 先将画布向上移动 height, 然后将沿着y翻转坐标轴
CGContextTranslateCTM(context, 0, height);
CGContextScaleCTM(context, 1.0, -1.0);
// 6.1 使用UIKit方法绘制文字, 由于context的坐标系统已经是ULO.此时绘制的内容是通过ULO为坐标系的
NSString *text= @"文字1";
UIFont *font=[UIFont systemFontOfSize:14];
[text drawAtPoint:CGPointMake(100, 100) withAttributes:font.fontDescriptor.fontAttributes];
// 6.2. 使用UIKit方法绘制图像 ,同上
UIImage *img=[UIImage imageNamed:@"1.jpg"];
[img drawInRect:CGRectMake(0, 0, 100, 100)];
// 6.3 还原之前的context的坐标系, 转化成 LLO
CGContextTranslateCTM(context, 0, height);
CGContextScaleCTM(context, 1.0, -1.0);
// 6.4 用UIKit 绘制文字和图片. 此时看的出来 坐标系是LLO, 图片和蚊子位置正确. 具体表现同 3-1
text= @"文字2";
[text drawAtPoint:CGPointMake(100, 100) withAttributes:font.fontDescriptor.fontAttributes];
[img drawInRect:CGRectMake(0, 0, 100, 100)];
// 7. 从当前context中获取UIImage
CGImageRef cgimg = CGBitmapContextCreateImage(context);
UIImage *resultImg = [UIImage imageWithCGImage:cgimg];
UIGraphicsPopContext();
// 8. 清理资源
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
CGImageRelease(cgimg);
self.imageView.image = resultImg;
}
4-1 使用UIKit创建Context, 使用Quartz 绘制API
-(void)draw4_1{
// 1. UIKit方法会创建Context, 并且将该Context push到UIKit维护的Context stack, 并且UIKit会自动将Context的坐标调整为ULO,
/*
UIGraphicsBeginImageContextWithOptions() 相当于 Quartz 以下API:
1. CGBitmapContextCreate -> 创建 CGContext
2. UIGraphicsPushContext(context) -> 将context push进入UIKit的 context stack
3. CGContextTranslateCTM(context, 0, height); CGContextScaleCTM(context, 1.0, -1.0); -> 调整context的坐标系为ULO
*/
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);
// 2. UIKit 的相关方法填充背景. 绘制边界
UIBezierPath *rect = [UIBezierPath bezierPathWithRect:self.bounds];
[[UIColor redColor] setFill];
[rect fill];
[[UIColor greenColor] setStroke];
[rect stroke];
// 3. UIKit通过`UIGraphicsGetCurrentContext`获取 CGContext 对象(Quartz API使用)
CGContextRef context = UIGraphicsGetCurrentContext();
// 4. Quartz 的绘制方法, 要求Context坐标系是LLO, 此时和当前context不匹配. 绘制是倒置的图像
UIImage *img = [UIImage imageNamed:@"1.jpg"];
CGContextDrawImage(context, CGRectMake(0, 0, 100, 100), img.CGImage);
UIImage *resultImg = UIGraphicsGetImageFromCurrentImageContext();
// 5 清理CGContext
UIGraphicsEndImageContext();
self.imageView.image = resultImg;
}
4-2 使用UIKit创建Context, 使用Quartz 绘制API(坐标系校正)
-(void)draw4_2{
// 1. UIKit方法会创建Context, 并且将该Context push到UIKit维护的Context stack, 并且UIKit会自动将Context的坐标调整为ULO,
/*
UIGraphicsBeginImageContextWithOptions() 相当于 Quartz 以下API:
1. CGBitmapContextCreate -> 创建 CGContext
2. UIGraphicsPushContext(context) -> 将context push进入UIKit的 context stack
3. CGContextTranslateCTM(context, 0, height); CGContextScaleCTM(context, 1.0, -1.0); -> 调整context的坐标系为ULO
*/
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);
// 2. UIKit 的相关方法填充背景. 绘制边界
UIBezierPath *rect = [UIBezierPath bezierPathWithRect:self.bounds];
[[UIColor redColor] setFill];
[rect fill];
[[UIColor greenColor] setStroke];
[rect stroke];
// 3. UIKit通过`UIGraphicsGetCurrentContext`获取 CGContext 对象(Quartz API使用)
CGContextRef context = UIGraphicsGetCurrentContext();
// 4. 在调用Quartz方法绘制之前,需要将 坐标系满足 LLO
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// 5.1 具体的Quartz 绘制方法, 此时绘制的图像位于左下角
// CGContextDrawXXXX 方法 -> 会以 Context 的LLO为坐标系
UIImage *img=[UIImage imageNamed:@"1.jpg"];
CGContextDrawImage(context, CGRectMake(0, 0, 100, 100), img.CGImage);
// 5.2 将context坐标重新转化成ULO, 然后继续绘制. 绘制的图像位于左上角
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
[img drawInRect:CGRectMake(0, 0, 100, 100)];
UIImage *resultImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.imageView.image = resultImg;
}
5. drawRect: 或者 drawLayer:inContext中绘图
这里使用自定义view的drawRect举例,
/**
drawRect 会帮我们做如下事情(类似于UIGraphicsBeginImageContextWithOptions,但产生的context并非 bitmapContext):
1. 创建一个context(注意这个context 并非 BitmapContext, 也就是说绘制的目的不是得到绘制图片, 而是直接渲染在view底层的layer 的content上)
2. 会隐士调用UIGraphicsPushContext(context), 将这个创建的 context push 进入UIKit context stack
3. 自动帮我们调整context坐标系是ULO
*/
- (void)drawRect:(CGRect)rect {
// 1. 直接通过UIKit API 获取当前的绘制所在的context
CGContextRef context = UIGraphicsGetCurrentContext();
// 2. 设置背景等等
CGContextSetRGBFillColor(context, 1, 0, 0, 1);
CGContextFillRect(context, rect);
// 3. 使用UIKit API 进行绘制
UIImage *img = [UIImage imageNamed:@"1.jpg"];
[img drawInRect:CGRectMake(0, 0, 100, 100)];
NSString *text = @"文字";
UIFont *font = [UIFont systemFontOfSize:14];
[text drawAtPoint:CGPointMake(100, 100) withAttributes:font.fontDescriptor.fontAttributes];
// ps: 无需清理
}