【概念释疑】CoreGraphics API 介绍

参考文章

设置

  • 设置勾画色CGContextSetRGBStrokeColor
void CGContextSetRGBStrokeColor (
   CGContextRef c,
   CGFloat red,
   CGFloat green,
   CGFloat blue,
   CGFloat alpha
);  
  • 设置填充色CGContextSetRGBFillColor
void CGContextSetRGBFillColor (
   CGContextRef c,
   CGFloat red,
   CGFloat green,
   CGFloat blue,
   CGFloat alpha
)  
  • 设置勾画宽度CGContextSetLineWidth
void CGContextSetLineWidth (
   CGContextRef c,
   CGFloat width
)  
  • 设置端点样式CGContextSetLineCap
void CGContextSetLineCap ( CGContextRef c, CGLineCap cap )  
  • 设置连接点样式CGContextSetLineJoin
void CGContextSetLineJoin ( CGContextRef c, CGLineJoin join )  
  • 设置虚线样式CGContextSetLineDash
void CGContextSetLineDash ( CGContextRef c, CGFloat phase, const CGFloat *lengths, size_t count );  

phase设置第一段实线开始的长度,lengths是一个浮点型数组,设置一个重复段中实线虚线的长度,count用以说明lengths的个数.

虚线样式1.png
虚线样式2.png

如上图虚线样式1,phase为0,说明它一个实线段的起点为0,lengths数组表明它的重复段是这样的:长10的实线,长20的虚线,长10的实线,长30的虚线,count为4.

上图虚线样式2与1唯一的不同的是,它的phase为5,它的第一个实线段从5开始.

  • 设置混合模式
void CGContextSetBlendMode ( CGContextRef c, CGBlendMode mode );  

混合模式是由CGBlendMode枚举来设置,详情见官方文档中的枚举说明.这里解释下几个参数:

R is the premultiplied result

S is the source color, and includes alpha

D is the destination color, and includes alpha

Ra, Sa, and Da are the alpha components of R, S, and D;

比如,kCGBlendModeDestinationOver,它的混合模式是这样来计算的: R = S*(1 - Da) + D.其中,前一种颜色是destination,后一种颜色是source.在官方Demo中,代码是这样的

    // Start with a background whose color we don't use in the demo
    CGContextSetGrayFillColor(context, 0.2, 1.0);
    CGContextFillRect(context, self.bounds);
    // We want to just lay down the background without any blending so we use the Copy mode rather than Normal
    CGContextSetBlendMode(context, kCGBlendModeCopy);
    // Draw a rect with the "background" color - this is the "Destination" for the blending formulas
    CGContextSetFillColorWithColor(context, self.destinationColor.CGColor);
    CGContextFillRect(context, CGRectMake(110.0, 20.0, 100.0, 100.0));
    // Set up our blend mode
    CGContextSetBlendMode(context, self.blendMode);
    // And draw a rect with the "foreground" color - this is the "Source" for the blending formulas
    CGContextSetFillColorWithColor(context, self.sourceColor.CGColor);
    CGContextFillRect(context, CGRectMake(60.0, 45.0, 200.0, 50.0)); 

代码中混合了两次,先是self.destinationColor.CGColor与一开始设置的gray,用kCGBlendModeCopy混合一次(这里,self.destinationColor.CGColor是source,gray是destination),然后,self.sourceColor.CGColor与self.destinationColor.CGColor,用self.blendMode混合一次.

创建Path

  • 创建Path起始点CGContextMoveToPoint
void CGContextMoveToPoint (
   CGContextRef c,
   CGFloat x,
   CGFloat y
)  
  • 添加线段CGContextAddLineToPoint
void CGContextAddLineToPoint (
   CGContextRef c,
   CGFloat x,
   CGFloat y
)  
  • 添加一系列线段CGContextAddLines
void CGContextAddLines (
   CGContextRef c,
   const CGPoint *points,
   size_t count
)  

相当于

CGContextMoveToPoint (c, points[0].x, points[0].y);  

for (k = 1; k < count; k++) {
    CGContextAddLineToPoint (c, points[k].x, points[k].y);
}  
示例1:勾画路径
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
CGContextSetLineWidth(context, 2.0);
CGContextMoveToPoint(context, 10.0, 30.0);
CGContextAddLineToPoint(context, 310.0, 30.0);
CGContextStrokePath(context);  
示例2:勾画路径
CGPoint addLines[] =
    {
        CGPointMake(10.0, 90.0),
        CGPointMake(70.0, 60.0),
        CGPointMake(130.0, 90.0),
        CGPointMake(190.0, 60.0),
        CGPointMake(250.0, 90.0),
        CGPointMake(310.0, 60.0),
    };

CGContextAddLines(context, addLines, sizeof(addLines)/sizeof(addLines[0]));
CGContextStrokePath(context);  
  • 添加矩形CGContextAddRect
void CGContextAddRect ( CGContextRef c, CGRect rect );  

相当于

CGContextMoveToPoint (c, CGRectGetMinX(rect), CGRectGetMinY(rect));
 
CGContextAddLineToPoint (c, CGRectGetMaxX(rect), CGRectGetMinY(rect));
 
CGContextAddLineToPoint (c, CGRectGetMaxX(rect), CGRectGetMaxY(rect);
 
CGContextAddLineToPoint (c, CGRectGetMinX(rect), CGRectGetMaxY(rect));

CGContextClosePath (c);  
  • 添加一系列矩形CGContextAddRects
void CGContextAddRects ( CGContextRef c, const CGRect *rects, size_t count );  

相当于

for (k = 0; k < count; k++) {
    CGContextAddRect (c, rects[k]);
}  
  • 添加椭圆CGContextAddEllipseInRect
void CGContextAddEllipseInRect ( CGContextRef c, CGRect rect );  
  • 添加弧线
void CGContextAddArc ( CGContextRef c, CGFloat x, CGFloat y, CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise );  
  • 添加弧线2
void CGContextAddArcToPoint ( CGContextRef c, CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2, CGFloat radius );  
示例3:添加弧线2
CGPoint p[3] =
    {
        CGPointMake(210.0, 30.0),
        CGPointMake(210.0, 60.0),
        CGPointMake(240.0, 60.0),
    };
    CGContextMoveToPoint(context, p[0].x, p[0].y);
    CGContextAddArcToPoint(context, p[1].x, p[1].y, p[2].x, p[2].y, 30.0);
    CGContextStrokePath(context);  
  • 二阶贝塞尔曲线
void CGContextAddQuadCurveToPoint ( CGContextRef c, CGFloat cpx, CGFloat cpy, CGFloat x, CGFloat y );  
示例4:二阶贝塞尔曲线
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
    s = CGPointMake(30.0, 300.0);
    e = CGPointMake(270.0, 300.0);
    cp1 = CGPointMake(150.0, 180.0);
    CGContextMoveToPoint(context, s.x, s.y);
    CGContextAddQuadCurveToPoint(context, cp1.x, cp1.y, e.x, e.y);
    CGContextStrokePath(context);  
  • 三阶贝塞尔曲线
void CGContextAddCurveToPoint ( CGContextRef c, CGFloat cp1x, CGFloat cp1y, CGFloat cp2x, CGFloat cp2y, CGFloat x, CGFloat y );  
示例5:三阶贝塞尔曲线
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
CGContextSetLineWidth(context, 2.0);
CGPoint s = CGPointMake(30.0, 120.0);
CGPoint e = CGPointMake(300.0, 120.0);
CGPoint cp1 = CGPointMake(120.0, 30.0);
CGPoint cp2 = CGPointMake(210.0, 210.0);
CGContextMoveToPoint(context, s.x, s.y);
CGContextAddCurveToPoint(context, cp1.x, cp1.y, cp2.x, cp2.y, e.x, e.y);
CGContextStrokePath(context);  
贝塞尔曲线[知识补充]
二阶贝塞尔曲线.png
三阶贝塞尔曲线.png

绘制Path

  • 勾画当前路径CGContextStrokePath,调用此函数后,会清除当前路径
void CGContextStrokePath (
   CGContextRef c
)  
  • 填充路径CGContextFillPath,非零绕组规则,调用此函数后,会清除当前路径
void CGContextFillPath ( CGContextRef c )  
  • 勾画间隔的线段CGContextStrokeLineSegments,调用此函数后,会清除当前路径
void CGContextStrokeLineSegments (
   CGContextRef c,
   const CGPoint *points,
   size_t count
)  

相当于

CGContextBeginPath (context);
for (k = 0; k < count; k += 2) {
    CGContextMoveToPoint(context, s[k].x, s[k].y);
    CGContextAddLineToPoint(context, s[k+1].x, s[k+1].y);
}
CGContextStrokePath(context);  
示例1:勾画间隔线段
CGPoint strokeSegments[] =
    {
        CGPointMake(10.0, 150.0),
        CGPointMake(70.0, 120.0),
        CGPointMake(130.0, 150.0),
        CGPointMake(190.0, 120.0),
        CGPointMake(250.0, 150.0),
        CGPointMake(310.0, 120.0),
    };

CGContextStrokeLineSegments(context, strokeSegments, sizeof(strokeSegments)/sizeof(strokeSegments[0]));  
  • 勾画矩形CGContextStrokeRect
void CGContextStrokeRect ( CGContextRef c, CGRect rect );  

相当于

CGContextAddRect ( CGContextRef c, CGRect rect );
CGContextStrokePath(context);    

  • 按指定宽度勾画矩形CGContextStrokeRectWithWidth
void CGContextStrokeRectWithWidth ( CGContextRef c, CGRect rect, CGFloat width );  

相当于

CGContextSetLineWidth ( CGContextRef c, CGFloat width );
    
CGContextAddRect ( CGContextRef c, CGRect rect );
CGContextStrokePath(context);  
  • 填充矩形CGContextFillRect
void CGContextFillRect ( CGContextRef c, CGRect rect );  

相当于

CGContextAddRect ( CGContextRef c, CGRect rect );
CGContextFillPath(context);  
  • 填充一系列矩形CGContextFillRects
void CGContextFillRects ( CGContextRef c, const CGRect *rects, size_t count );  

相当于

for (k = 0; k < count; k++) {
    CGContextFillRect (c, rects[k]);
}  
  • 以指定的模式绘制路径CGContextDrawPath
void CGContextDrawPath ( CGContextRef c, CGPathDrawingMode mode );  

介绍下这几个枚举值:

kCGPathFill,           // 非零绕组填充
kCGPathEOFill,         // 奇偶填充
kCGPathStroke,         // 勾边
kCGPathFillStroke,     // 非零绕组填充并勾边
kCGPathEOFillStroke    // 奇偶填充并勾边
  • 勾画椭圆CGContextStrokeEllipseInRect
void CGContextStrokeEllipseInRect ( CGContextRef c, CGRect rect );  

相当于

CGContextAddEllipseInRect ( CGContextRef c, CGRect rect );  
CGContextStrokePath(context);   
  • 填充椭圆CGContextFillEllipseInRect
void CGContextFillEllipseInRect ( CGContextRef c, CGRect rect );  
非零绕组规则和奇偶规则[补充知识]

图形学的两个规则

非零绕组和奇偶.png

如上图,左下角是按照奇偶规则填充,右下角按非零绕组规则填充。
(据我理解,这条曲线应该是一条闭合曲线)
奇偶规则:从一点射出任意一条指向无穷远的射线,曲线和射线的交点个数为奇数,则该点在曲线内,否则,在曲线外。

非零绕组规则:给这条曲线标注方向,从一点射出任意一条指向无穷远的射线,定义一个方向的交点(比如从左到右,或从上到下)为1,它的反向的交点(从右到左,或从下到上)为-1,如果绕组总数为0,表示该点在曲线外,非零,则在曲线内。

上图中,判定区里的点,按照奇偶规则,它与曲线有偶数个交点,所以在曲线外,按照非零绕组规则,如图中所标方向,并将箭头作为射线方向,它有两个同向(都是从上往下)的交点,故而非零,判定该点在曲线内。

存取上下文环境

  • 存储当前绘制状态的备份到绘制状态栈中CGContextSaveGState
void CGContextSaveGState ( CGContextRef c )  
  • 将当前绘制状态切换到绘制栈顶的状态,栈顶的状态被删除CGContextRestoreGState
void CGContextRestoreGState ( CGContextRef c )  

这两个方法的配对使用,主要用于:该绘制动作只想作用于该阶段,不想对继后的上下文产生影响.这时,先存储绘制状态,然后实行绘制操作A,然后再取出存储的状态.这样A操作不会对绘制状态产生影响.

可存储的图像状态[知识补充]

Each graphics context maintains a stack of graphics states. Note that not all aspects of the current drawing environment are elements of the graphics state. For example, the current path is not considered part of the graphics state and is therefore not saved when you call the CGContextSaveGState function. The graphics state parameters that are saved are:

CTM (current transformation matrix)

clip region

image interpolation quality

line width

line join

miter limit

line cap

line dash

flatness

should anti-alias

rendering intent

fill color space

stroke color space

fill color

stroke color

alpha value

font

font size

character spacing

text drawing mode

shadow parameters

the pattern phase

the font smoothing parameter

blend mode

示例1:切图操作
CGFloat height = self.bounds.size.height;
CGContextTranslateCTM(context, 0.0, height);
CGContextScaleCTM(context, 1.0, -1.0);

CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0);

CGContextDrawImage(context, CGRectMake(10.0, height - 100.0, 90.0, 90.0), self.image);

CGContextSaveGState(context);

CGRect clips[] =
    {
        CGRectMake(110.0, height - 100.0, 35.0, 90.0),
        CGRectMake(165.0, height - 100.0, 35.0, 90.0),
    };

CGContextClipToRects(context, clips, sizeof(clips) / sizeof(clips[0]));
CGContextDrawImage(context, CGRectMake(110.0, height - 100.0, 90.0, 90.0), self.image);
CGContextRestoreGState(context);  

修改剪切Path

在剪切路径中,需要理解交汇区(intersection),如CGContextClip函数中,是 the current path 和 the current clipping path 交汇的地方可以操作,在CGContextClipToRects函数中,是the clipping path 与 the current clipping path 交汇的地方可以操作.我们经常会需要利用CGContextSaveGState来存储当前的图像状态(会存储clip region) ,利用CGContextRestoreGState来取出存储的状态.

  • 剪切当前Path CGContextClip,非零绕组规则
void CGContextClip ( CGContextRef c );  
  • 剪切当前Path CGContextClip,奇偶规则
void CGContextEOClip ( CGContextRef c );  
  • 矩形框剪切CGContextClipToRect
void CGContextClipToRect ( CGContextRef c, CGRect rect );  
  • 一系列矩形框剪切CGContextClipToRects
void CGContextClipToRects ( CGContextRef c, const CGRect *rects, size_t count );  
  • 获取剪切路径frame
CGRect CGContextGetClipBoundingBox ( CGContextRef c );  

注:bounding box是包括了剪切路径中所有点的矩形.

  • 创建Mask CGContextClipToMask
void CGContextClipToMask ( CGContextRef c, CGRect rect, CGImageRef mask );  

遮罩分为两种:image mask 和 mask image.写到这儿,本来我想展开解释,但是苹果在mask方面的函数命名,和API解释,感觉都不统一,把人绕得云山雾罩.所以,我们还是抛开苹果来解释吧.

此函数就是给指定矩形与当前剪切区的重叠部分增加一个遮罩.上面提到的两种遮罩,就是指重叠区怎么展示,是重叠区展示,还是非重叠区展示(通过alpha通道来区分).

我们常用的就是给一个图片,图片中alpha为1的重叠区是全透过,alpha为0则不透过.

绘图

  • 绘制图片
void CGContextDrawImage ( CGContextRef c, CGRect rect, CGImageRef image );  

其中,rect指定图片尺寸和起始点.

  • 平铺
void CGContextDrawTiledImage ( CGContextRef c, CGRect rect, CGImageRef image );  

其中,rect指定平铺图片的尺寸和平铺起始点.

转换用户空间

  • 合并转换CGContextConcatCTM
void CGContextConcatCTM ( CGContextRef c, CGAffineTransform transform );  
  • 获取当前的转换仿射矩阵CGContextGetCTM
CGAffineTransform CGContextGetCTM ( CGContextRef c );  
  • 旋转CGContextRotateCTM
void CGContextRotateCTM ( CGContextRef c, CGFloat angle );  
  • 缩放CGContextScaleCTM
void CGContextScaleCTM ( CGContextRef c, CGFloat sx, CGFloat sy );  
  • 平移CGContextTranslateCTM
void CGContextTranslateCTM ( CGContextRef c, CGFloat tx, CGFloat ty );  
示例1:绘制PDF
    // 我们期望PDF的绘制是以左下角作为坐标原点的,所以先将当前坐标矩阵下移,然后翻转
    CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    
    // Grab the first PDF page
    CGPDFPageRef page = CGPDFDocumentGetPage(self.pdfDocument, 1);
    // We're about to modify the context CTM to draw the PDF page where we want it, so save the graphics state in case we want to do more drawing
    CGContextSaveGState(context);
    // CGPDFPageGetDrawingTransform provides an easy way to get the transform for a PDF page. It will scale down to fit, including any
    // base rotations necessary to display the PDF page correctly. 
    CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(page, kCGPDFCropBox, self.bounds, 0, true);
    // 合并仿射矩阵
    CGContextConcatCTM(context, pdfTransform);
    // Finally, we draw the page and restore the graphics state for further manipulations!
    CGContextDrawPDFPage(context, page);
    CGContextRestoreGState(context);  

绘制文本

示例1:绘制文本
#define kTextString "Hello From Quartz"
#define kTextStringLength strlen(kTextString)  
===========================================================================================
    CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
    CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);


    CGContextSelectFont(context, "Helvetica", 36.0, kCGEncodingMacRoman);

    CGContextSetTextMatrix(context, CGAffineTransformMakeScale(1.0, -1.0));

    CGContextSetTextDrawingMode(context, kCGTextFill);
    CGContextShowTextAtPoint(context, 10.0, 30.0, kTextString, kTextStringLength);
    CGContextSetTextDrawingMode(context, kCGTextStroke);
    CGContextShowTextAtPoint(context, 10.0, 70.0, kTextString, kTextStringLength);
    CGContextSetTextDrawingMode(context, kCGTextFillStroke);
    CGContextShowTextAtPoint(context, 10.0, 110.0, kTextString, kTextStringLength);
    

    CGFontRef helvetica = CGFontCreateWithFontName((CFStringRef)@"Helvetica");
    CGContextSetFont(context, helvetica);
    CGContextSetFontSize(context, 12.0);
    CGContextSetTextDrawingMode(context, kCGTextFill);  

下载源码

苹果官方Sample

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容