1. 使用Quartz的API 創(chuàng)建Context,并且繪圖
完全使用Quartz創(chuàng)建Context, 并且使用Quartz繪圖的API, 此時(shí), context坐標(biāo)系是left-low原點(diǎn), 并且Quartz繪制方法認(rèn)為圖片的坐標(biāo)系也是LLO.
/**
CoreGraphic API -> LLO坐標(biāo)系
CGBitmapContextCreate -> 創(chuàng)建 bitmap Context
CGBitmapContextCreateImage -> 獲取 bitmap Image CGImageRef -> 轉(zhuǎn)化成UIImage
用 CoreGraphic CGContextDrawImage 繪圖
*/
-(void)draw1{
// 1. 創(chuàng)建顏色空間
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. 創(chuàng)建bitmapContext, 使用Quartz創(chuàng)建的Context,因此坐標(biāo)系是LLO, 坐標(biāo)原點(diǎn)是左下角
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相關(guān)的api繪制圖片時(shí),只能傳入U(xiǎn)IImage的底層CGImage.
// 并且此時(shí)由于繪制圖片API `CGContextDrawImage` 是Quartz API, 因此繪制方法表示圖片的坐標(biāo)原點(diǎn)也是LLO
UIImage *img=[UIImage imageNamed:@"1.jpg"];
CGContextDrawImage(context, CGRectMake(0, 0, 100, 100), img.CGImage);
// 5. 從context中獲取CGImage, 并創(chuàng)建UIImage
CGImageRef cgimg = CGBitmapContextCreateImage(context);
UIImage *resultImg = [UIImage imageWithCGImage:cgimg];
// 6. 清理CoreGraphic 資源
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
CGImageRelease(cgimg);
self.imageView.image = resultImg;
}
繪制結(jié)果

2. 使用UIKit的API 創(chuàng)建Context, 并且繪圖
UIKit繪圖時(shí), 通過UIKit的API創(chuàng)建Context, 此時(shí)Context的坐標(biāo)原點(diǎn)是ULO, UIKit繪圖的API認(rèn)為圖像的原點(diǎn)也是ULO. 值得注意的是,與Quartz API不同的是UIKit會(huì)維護(hù)一個(gè)Context Stack, 用于記錄當(dāng)前UIKit中各種狀態(tài)(填充顏色 setFill, 繪線顏色setStroke等等)是作用于哪個(gè)Context的.
/**
UIKit API -> ULO 原點(diǎn)
UIGraphicsBeginImageContextWithOptions -> 創(chuàng)建bitmap Context, 并push 入 stack
UIGraphicsGetImageFromCurrentImageContext -> 獲取 bitmap
*/
-(void)draw2 {
// 1. 創(chuàng)建 context, 并且將context push到UIKit維護(hù)的Context Stack
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);
// 2. 創(chuàng)建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,因此需要坐標(biāo)系是 ULO, 這里使用 UIKit方法創(chuàng)建, 因此繪制的文字都是正的. 坐標(biāo)原點(diǎn)是ULO
NSString *text= @"文字";
UIFont *font=[UIFont systemFontOfSize:14];
[text drawAtPoint:CGPointMake(100, 100) withAttributes:font.fontDescriptor.fontAttributes];
// 4. 用UIKit 繪制圖像, context是UIKit創(chuàng)建的, 因此是ULO坐標(biāo)系, (0,0,100,100)在左上角,并且圖片是正向
UIImage *img=[UIImage imageNamed:@"1.jpg"];
[img drawInRect:CGRectMake(0, 0, 100, 100)];
// 5. 從當(dāng)前context獲取UIImage
UIImage *resultImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.imageView.image = resultImg;
}

3-1 使用Quartz創(chuàng)建Context, 使用UIKit繪圖
使用Quartz創(chuàng)建的Context坐標(biāo)系是LLO, 而使用UIKit的繪圖API(繪制文字, 繪制圖像) 依賴的坐標(biāo)系是ULO. 隱藏圖像和文字會(huì)倒置.
/**
CoreGraphic 創(chuàng)建 Context -> LLO
繪圖使用 UIKit 繪制
*/
-(void)draw3_1 {
// 1. 創(chuàng)建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創(chuàng)建CGContext, 坐標(biāo)系是 LLO
CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrderDefault);
// 3. UIKit API的繪圖方法. 需要首先將當(dāng)前context push到 當(dāng)前UIKit維護(hù)的context stack中!!!!!(此時(shí)繪圖方法才知道繪制到哪個(gè)context)
UIGraphicsPushContext(context);
// 4. 填充顏色, 配置部分狀態(tài)
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. 從當(dāng)前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創(chuàng)建Context, 使用UIKit繪圖(修正坐標(biāo)系)
3-1中由于Quartz創(chuàng)建的Context坐標(biāo)系是LLO, 而UIKit繪制坐標(biāo)系要求ULO, 只要兩者不匹配. 因此在繪制之前要保證. 當(dāng)前Context的坐標(biāo)系與繪制方法依賴的坐標(biāo)系相同. 因此在調(diào)用UIKit時(shí),需要將Context坐標(biāo)系轉(zhuǎn)換成ULO, 繪制完成以后. 最好將該坐標(biāo)系恢復(fù)原始坐標(biāo)系.
-(void)draw3_2 {
// 1. 創(chuàng)建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創(chuàng)建CGContext, 坐標(biāo)系是 LLO
CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrderDefault);
// 3. UIKit API的繪圖方法. 需要首先將當(dāng)前context push到 當(dāng)前UIKit維護(hù)的context stack中!!!!!(此時(shí)繪圖方法才知道繪制到哪個(gè)context)
UIGraphicsPushContext(context);
// 4. 填充顏色, 配置部分狀態(tài)
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繪制方法進(jìn)行繪制之前,先將當(dāng)前context的坐標(biāo)系調(diào)整成ULO, 具體步驟如下: 翻轉(zhuǎn)畫布 -> 先將畫布向上移動(dòng) height, 然后將沿著y翻轉(zhuǎn)坐標(biāo)軸
CGContextTranslateCTM(context, 0, height);
CGContextScaleCTM(context, 1.0, -1.0);
// 6.1 使用UIKit方法繪制文字, 由于context的坐標(biāo)系統(tǒng)已經(jīng)是ULO.此時(shí)繪制的內(nèi)容是通過ULO為坐標(biāo)系的
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的坐標(biāo)系, 轉(zhuǎn)化成 LLO
CGContextTranslateCTM(context, 0, height);
CGContextScaleCTM(context, 1.0, -1.0);
// 6.4 用UIKit 繪制文字和圖片. 此時(shí)看的出來 坐標(biāo)系是LLO, 圖片和蚊子位置正確. 具體表現(xiàn)同 3-1
text= @"文字2";
[text drawAtPoint:CGPointMake(100, 100) withAttributes:font.fontDescriptor.fontAttributes];
[img drawInRect:CGRectMake(0, 0, 100, 100)];
// 7. 從當(dāng)前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創(chuàng)建Context, 使用Quartz 繪制API
-(void)draw4_1{
// 1. UIKit方法會(huì)創(chuàng)建Context, 并且將該Context push到UIKit維護(hù)的Context stack, 并且UIKit會(huì)自動(dòng)將Context的坐標(biāo)調(diào)整為ULO,
/*
UIGraphicsBeginImageContextWithOptions() 相當(dāng)于 Quartz 以下API:
1. CGBitmapContextCreate -> 創(chuàng)建 CGContext
2. UIGraphicsPushContext(context) -> 將context push進(jìn)入U(xiǎn)IKit的 context stack
3. CGContextTranslateCTM(context, 0, height); CGContextScaleCTM(context, 1.0, -1.0); -> 調(diào)整context的坐標(biāo)系為ULO
*/
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);
// 2. UIKit 的相關(guān)方法填充背景. 繪制邊界
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坐標(biāo)系是LLO, 此時(shí)和當(dāng)前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創(chuàng)建Context, 使用Quartz 繪制API(坐標(biāo)系校正)
-(void)draw4_2{
// 1. UIKit方法會(huì)創(chuàng)建Context, 并且將該Context push到UIKit維護(hù)的Context stack, 并且UIKit會(huì)自動(dòng)將Context的坐標(biāo)調(diào)整為ULO,
/*
UIGraphicsBeginImageContextWithOptions() 相當(dāng)于 Quartz 以下API:
1. CGBitmapContextCreate -> 創(chuàng)建 CGContext
2. UIGraphicsPushContext(context) -> 將context push進(jìn)入U(xiǎn)IKit的 context stack
3. CGContextTranslateCTM(context, 0, height); CGContextScaleCTM(context, 1.0, -1.0); -> 調(diào)整context的坐標(biāo)系為ULO
*/
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);
// 2. UIKit 的相關(guān)方法填充背景. 繪制邊界
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. 在調(diào)用Quartz方法繪制之前,需要將 坐標(biāo)系滿足 LLO
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// 5.1 具體的Quartz 繪制方法, 此時(shí)繪制的圖像位于左下角
// CGContextDrawXXXX 方法 -> 會(huì)以 Context 的LLO為坐標(biāo)系
UIImage *img=[UIImage imageNamed:@"1.jpg"];
CGContextDrawImage(context, CGRectMake(0, 0, 100, 100), img.CGImage);
// 5.2 將context坐標(biāo)重新轉(zhuǎn)化成ULO, 然后繼續(xù)繪制. 繪制的圖像位于左上角
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 會(huì)幫我們做如下事情(類似于UIGraphicsBeginImageContextWithOptions,但產(chǎn)生的context并非 bitmapContext):
1. 創(chuàng)建一個(gè)context(注意這個(gè)context 并非 BitmapContext, 也就是說繪制的目的不是得到繪制圖片, 而是直接渲染在view底層的layer 的content上)
2. 會(huì)隱士調(diào)用UIGraphicsPushContext(context), 將這個(gè)創(chuàng)建的 context push 進(jìn)入U(xiǎn)IKit context stack
3. 自動(dòng)幫我們調(diào)整context坐標(biāo)系是ULO
*/
- (void)drawRect:(CGRect)rect {
// 1. 直接通過UIKit API 獲取當(dāng)前的繪制所在的context
CGContextRef context = UIGraphicsGetCurrentContext();
// 2. 設(shè)置背景等等
CGContextSetRGBFillColor(context, 1, 0, 0, 1);
CGContextFillRect(context, rect);
// 3. 使用UIKit API 進(jìn)行繪制
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: 無需清理
}
