CGContext图形上下文详解

核心绘图状态管理

CGContextSaveGState函数的作用是将当前图形状态推入堆栈。之后,您对图形状态所做的修改会影响随后的描画操作,但不影响存储在堆栈中的拷贝。在修改完成后,您可以通过CGContextRestoreGState函数把堆栈顶部的状态弹出,返回到之前的图形状态。这种推入和弹出的方式是回到之前图形状态的快速方法,避免逐个撤消所有的状态修改;这也是将某些状态(比如裁剪路径)恢复到原有设置的唯一方式。

CGContextSaveGState:压栈操作,保存一份当前图形上下文
CGContextRestoreGState:出栈操作,恢复一份当前图形上下文

图形上下文的坐标空间变换

CTM(current transformation matrix当前转换矩阵)
CGContextScaleCTM:坐标系X,Y缩放
CGContextTranslateCTM:坐标系平移
CGContextRotateCTM:坐标系旋转
CGContextConcatCTM
CGContextGetCTM:获得一份CTM

设置Line属性及连接样式

CGContextSetLineWidth
CGContextSetLineCap
CGContextSetLineJoin
CGContextSetMiterLimit
CGContextSetLineDash
CGContextSetFlatness
CGContextSetAlpha://设置透明度

CGContextSetAlpha(context, 0.2) 

CGContextSetBlendMode

设置Path绘制

CGContextBeginPath
CGContextMoveToPoint:画笔移动到该点开始画线
CGContextAddLineToPoint:画直线到该点
CGContextAddCurveToPoint:画三次曲线函数

CGContextMoveToPoint(context, 200, 300);//设置Path的起点 
CGContextAddCurveToPoint(context,250, 280, 250, 400, 280, 300);//设置贝塞尔曲线的控制点坐标和控制点坐标终点坐标
CGContextStrokePath(context);

CGContextAddQuadCurveToPoint:画二次曲线

CGContextMoveToPoint(context, 120, 300);//设置Path的起点
CGContextAddQuadCurveToPoint(context,190, 310, 120, 390);//设置贝塞尔曲线的控制点坐标和终点坐标
CGContextStrokePath(context);

CGContextClosePath:闭合曲线
CGContextAddRect:画矩形

CGContextAddRect(context,CGRectMake(140, 120, 60, 30));//画方框

CGContextAddRects
CGContextAddLines

/*画三角形*/
    //只要三个点就行跟画一条线方式一样,把三点连接起来
    CGPoint sPoints[3];//坐标点
    sPoints[0] =CGPointMake(100, 220);//坐标1
    sPoints[1] =CGPointMake(130, 220);//坐标2
    sPoints[2] =CGPointMake(130, 160);//坐标3
    CGContextAddLines(context, sPoints, 3);//添加线
    CGContextClosePath(context);
    CGContextDrawPath(context, kCGPathFillStroke); //根据坐标绘制路径

CGContextAddEllipseInRect

//画椭圆
    CGContextAddEllipseInRect(context, CGRectMake(160, 180, 20, 8)); //椭圆
    CGContextDrawPath(context, kCGPathFillStroke);

CGContextAddArc
CGContextAddArcToPoint

/*画圆角矩形*/
    float fw = 180;
    float fh = 280;
    
    CGContextMoveToPoint(context, fw, fh-20);  // 开始坐标右边开始
    CGContextAddArcToPoint(context, fw, fh, fw-20, fh, 10);  // 右下角角度
    CGContextAddArcToPoint(context, 120, fh, 120, fh-20, 10); // 左下角角度
    CGContextAddArcToPoint(context, 120, 250, fw-20, 250, 10); // 左上角
    CGContextAddArcToPoint(context, fw, 250, fw, fh-20, 10); // 右上角
    CGContextClosePath(context);
    CGContextDrawPath(context, kCGPathFillStroke); //根据坐标绘制路径

CGContextAddPath
CGContextCopyPath

获取路径信息

CGContextReplacePathWithStrokedPath
CGContextIsPathEmpty:表示目前的图形上下文是否包含任何的子路径。

bool isPathEmpty =  CGContextIsPathEmpty(ctf);

CGContextGetPathCurrentPoint:返回一个非空的路径中的当前点。

CGPoint currentPoint = CGContextGetPathCurrentPoint(ctf);// 获取当前画笔处于最后的那一个点

CGContextGetPathBoundingBox:返回包含当前路径的最小矩形。

 CGRect boxRect = CGContextGetPathBoundingBox(ctf);// 包含路径的最小矩形

CGContextPathContainsPoint:检查当前路径中是否包含指定的点。

路径绘制

CGContextStrokePath
CGContextDrawPath

两者区别:
 /*CGPathDrawingMode是填充方式,枚举类型
    kCGPathFill:只有填充(非零缠绕数填充),不绘制边框
    kCGPathEOFill:奇偶规则填充(多条路径交叉时,奇数交叉填充,偶交叉不填充)
    kCGPathStroke:只有边框
    kCGPathFillStroke:既有边框又有填充
    kCGPathEOFillStroke:奇偶填充并绘制边框
    */
CGContextStrokePath(context); 直接在图形上下文中渲染路径
CGContextDrawPath(context, kCGPathFillStroke); //指定模式下渲染路径

CGContextFillPath://填充路径
CGContextEOFillPath://奇偶填充

CGContextFillRect://
CGContextStrokeRect

/*画矩形*/
 CGContextStrokeRect(context,CGRectMake(100, 120, 10, 10));//画方框
CGContextFillRect(context,CGRectMake(120, 120, 10, 10));//填充框

CGContextFillRects
CGContextStrokeRectWithWidth
CGContextClearRect
CGContextFillEllipseInRect
CGContextStrokeEllipseInRect
CGContextStrokeLineSegments

修改剪裁路径

CGContextClip
CGContextEOClip
CGContextClipToMask
CGContextGetClipBoundingBox
CGContextClipToRect
CGContextClipToRects

设置颜色、色彩空间及阴影值

  • Quartz 中的颜色是用一组数值来表示。而颜色空间用于解析这些颜色信息,常用颜色空间有 RGB 、CMYK等。

  • Quartz 支持通用颜色空间、设备独立颜色空间、设备依赖颜色空间、索引颜色空间和模式(Pattern)颜色空间。

  • iOS不支持设备独立颜色空间和通用颜色空间。iOS应用程序必须使用设备颜色空间。

CGContextSetFillColorWithColor://设置填充颜色
CGContextSetStrokeColorWithColor://设置描边颜色

  • 调用如下函数来便捷的使用 CGColor 设置颜色值并使用 CGColor 指定的颜色空间
//设置填充颜色
 UIColor  *aColor = [UIColor blueColor];//blue蓝色
 CGContextSetFillColorWithColor(context, aColor.CGColor);//填充颜色
//设置描边颜色
aColor = [UIColor yellowColor];
CGContextSetStrokeColorWithColor(context, aColor.CGColor);//线框颜色

创建设备依赖颜色空间

Quartz 中的颜色是用一组数值来表示。而颜色空间用于解析这些颜色信息,常用颜色空间有 RGB 、CMYK等。

Quartz 支持通用颜色空间、设备独立颜色空间、设备依赖颜色空间、索引颜色空间和模式(Pattern)颜色空间。

iOS不支持设备独立颜色空间和通用颜色空间。iOS应用程序必须使用设备颜色空间。

  • CGColorSpaceCreateDeviceGray() 创建设备依赖灰度颜色空间。
  • CGColorSpaceCreateDeviceRGB() 创建设备依赖RGB颜色空间。
  • CGColorSpaceCreateDeviceCMYK() 创建设备依赖CMYK颜色
    空间。

CGContextSetFillColorSpace
CGContextSetStrokeColorSpace

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextSetFillColorSpace(context, colorSpace);
CGContextSetStrokeColorSpace(context, colorSpace);

CGContextSetFillColor
CGContextSetStrokeColor

//Device RGB. 
CGContextSetRGBStrokeColor(context, 1, 0, 0, 1);
CGContextSetRGBFillColor(context, 1, 0, 0, 1);

CGContextSetGrayFillColor
CGContextSetGrayStrokeColor

  • 调用如下函数来便捷的设置设备依赖CMYK颜色空间并设置颜色值。
//Device Gray.
CGContextSetGrayStrokeColor(context, 0.5, 1);
CGContextSetGrayFillColor(context, 0.5, 1);

CGContextSetRGBFillColor
CGContextSetRGBStrokeColor

CGContextSetRGBStrokeColor (context, 142.0/ 255.0, 161.0/ 255.0, 189.0/ 255.0, 1.0);

CGContextSetCMYKFillColor
CGContextSetCMYKStrokeColor

  • 调用如下函数来便捷的设置设备依赖CMYK颜色空间并设置颜色值。
//Device CMYK. 
CGContextSetCMYKStrokeColor(context, 1, 0, 0, 0, 1);
CGContextSetCMYKFillColor(context, 1, 0, 0, 0, 1);

CGContextSetRenderingIntent:设置再现意图

每个设备都有固定的可复制的颜色范围(gamut),这是设备的物理性质决定的。当图像从一个颜色空间向另一个颜色空间转换时,有些源设备颜色空间中呈现的颜色,不能在目标设备颜色空间中复制出来,这些不能复制的颜色叫色域外(out-of-gamut)颜色。比如 RGB 颜色空间比 CMYK 的颜色空间要大,有些在显示器上能显示的颜色不能在打印机上同样打印出来。因为我们不能在目标设备颜色空间中复制出色域外颜色,我们必须用一些其他颜色来替代他们。颜色空间转换时颜色替换调整的规则就是再现意图。更详细的说明可以查看 这里 和 这里

  • 再现意图用于指定如何将源颜色空间的颜色映射到图形上下文的目标颜色空间的颜色范围内。

  • 如果不显式的指定再现意图,Quartz 使用“相对色度再现意图”应用于所有绘制(不包含位图图像)。

  • 对于位图图像,Quartz默认使用“感知再现意图”。

  • 调用 CGContextSetRenderingIntent(context, kCGRenderingIntentDefault) 来设置再现意图。

  • 再现意图共有以下 5 种

typedef CF_ENUM (int32_t, CGColorRenderingIntent) {
    kCGRenderingIntentDefault,
    kCGRenderingIntentAbsoluteColorimetric,
    kCGRenderingIntentRelativeColorimetric,
    kCGRenderingIntentPerceptual,
    kCGRenderingIntentSaturation
};

1 kCGRenderingIntentDefault:默认再现意图。
2 kCGRenderingIntentAbsoluteColorimetric:绝对色度再现意图。将输出设备颜色域外的颜色映射为输出设备域内与之最接近的颜色。这可以产生一个裁减效果,因为色域外的两个不同的颜色值可能被映射为色域内的同一个颜色值。当图形使用的颜色值同时包含在源色域及目标色域内时,这种方法是最好的。常用于logo或者使用专色(spot color)时。
3 kCGRenderingIntentRelativeColorimetric:相对色度再现意图。转换所有的颜色(包括色域内的),以补偿图形上下文的白点与输出设备白点之间的色差。
4 kCGRenderingIntentPerceptual:感知再现意图。通过压缩图形上下文的色域来适应输出设备的色域,并保持源颜色空间的颜色之间的相对性。感知渲染意图适用于相片及其它复杂的高细度图片。
5 kCGRenderingIntentSaturation:饱和度再现意图。把颜色转换到输出设备色域内时,保持颜色的相对饱和度。结果是包含亮度、饱和度颜色的图片。饱和度意图适用于生成低细度的图片,如描述性图表。

CGContextDrawImage:在指定区域画图片

/*图片*/
    UIImage *image = [UIImage imageNamed:@"apple.jpg"];
    [image drawInRect:CGRectMake(60, 340, 20, 20)];//在坐标中画出图片
    //    [image drawAtPoint:CGPointMake(100, 340)];//保持图片大小在point点开始画图片,可以把注释去掉看看
    CGContextDrawImage(context, CGRectMake(100, 340, 20, 20), image.CGImage);

CGContextDrawTiledImage
CGContextGetInterpolationQuality:返回当前的图形上下文的插值(插值(Interpolation)是在不天生像素的环境下增长图像像素大小的一种方法,在周围像素色彩
的根蒂根基上用算术公式计算亡失像素的色彩。)质量水平
CGContextSetInterpolationQuality:设置图形上下文的插值质量水平。

设置阴影

CGContextSetShadowWithColor
CGContextSetShadow

阴影是如何工作的
Quartz中的阴影是图形状态的一部分。我们可以调用函数CGContextSetShadow来创建,并传入一个图形上下文、偏移值及模糊值。阴影被设置后,任何绘制的对象都有一个阴影,且该阴影在设备RGB颜色空间中呈现出黑色的且alpha值为1/3。换句话说,阴影是用RGBA值{0, 0, 0, 1.0/3.0}设置的。
我们可以调用函数CGContextSetShadowWithColor来设置彩色阴影,并传递一个图形上下文、 偏移值、模糊值有CGColor颜色对象。颜色值依赖于颜色空间。
如何在调用CGContextSetShadow或CGContextSetShadowWithColor之前保存了图形状态,我们可以通过恢复图形状态来关闭阴影。我们也可以通过设置阴影颜色为NULL来关闭阴影

阴影有三个属性:
x偏移值,用于指定阴影相对于图片在水平方向上的偏移值。
y偏移值,用于指定阴影相对于图片在竖直方向上的偏移值。
模糊(blur)值,用于指定图像是有一个硬边
CGContextSetShadow(context, CGSizeMake(10, -20), 10);

绘制渐变色

CGContextDrawLinearGradient
CGContextDrawRadialGradient

    //第二种填充方式
    CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
    CGFloat colors[] =
    {
        1,1,1, 1.00,
        1,1,0, 1.00,
        1,0,0, 1.00,
        1,0,1, 1.00,
        0,1,1, 1.00,
        0,1,0, 1.00,
        0,0,1, 1.00,
        0,0,0, 1.00,
    };
    CGGradientRef gradient = CGGradientCreateWithColorComponents
    (rgb, colors, NULL, sizeof(colors)/(sizeof(colors[0])*4));//形成梯形,渐变的效果
    CGColorSpaceRelease(rgb);
    //画线形成一个矩形
    //CGContextSaveGState与CGContextRestoreGState的作用
    /*
     CGContextSaveGState函数的作用是将当前图形状态推入堆栈。之后,您对图形状态所做的修改会影响随后的描画操作,但不影响存储在堆栈中的拷贝。在修改完成后,您可以通过CGContextRestoreGState函数把堆栈顶部的状态弹出,返回到之前的图形状态。这种推入和弹出的方式是回到之前图形状态的快速方法,避免逐个撤消所有的状态修改;这也是将某些状态(比如裁剪路径)恢复到原有设置的唯一方式。
     */
    CGContextSaveGState(context);
    CGContextMoveToPoint(context, 220, 90);
    CGContextAddLineToPoint(context, 240, 90);
    CGContextAddLineToPoint(context, 240, 110);
    CGContextAddLineToPoint(context, 220, 110);
    CGContextClip(context);//context裁剪路径,后续操作的路径
    //CGContextDrawLinearGradient(CGContextRef context,CGGradientRef gradient, CGPoint startPoint, CGPoint endPoint,CGGradientDrawingOptions options)
    //gradient渐变颜色,startPoint开始渐变的起始位置,endPoint结束坐标,options开始坐标之前or开始之后开始渐变
    CGContextDrawLinearGradient(context, gradient,CGPointMake
                                (220,90) ,CGPointMake(240,110),
                                kCGGradientDrawsAfterEndLocation);
    CGContextRestoreGState(context);// 恢复到之前的context
    
    //再写一个看看效果
    CGContextSaveGState(context);
    CGContextMoveToPoint(context, 260, 90);
    CGContextAddLineToPoint(context, 280, 90);
    CGContextAddLineToPoint(context, 280, 190);
    CGContextAddLineToPoint(context, 260, 190);
    CGContextClip(context);//裁剪路径
    //说白了,开始坐标和结束坐标是控制渐变的方向和形状

    CGContextDrawLinearGradient(context, gradient,CGPointMake
                                (260, 90) ,CGPointMake(260, 190),
                                kCGGradientDrawsAfterEndLocation);
    CGContextRestoreGState(context);// 恢复到之前的context
    
    //下面再看一个颜色渐变的圆
    //参数1:图形上下文
    //参数2:渐变色
    //参数3:开始中心点
    //参数4:开始半径
    //参数5:结束中心点
    //参数6:结束半径
    //参数7:渲染模式
    CGContextDrawRadialGradient(context, gradient, CGPointMake(400, 100), 0.0, CGPointMake(350, 100), 30, kCGGradientDrawsBeforeStartLocation);

CGContextDrawShading

绘制文本

CGContextSetCharacterSpacing
CGContextSetTextPosition
CGContextGetTextPosition
CGContextSetTextMatrix
CGContextGetTextMatrix
CGContextSetTextDrawingMode
CGContextSetFont
CGContextSetFontSize
CGContextShowGlyphsAtPositions

PDF

** CGContextDrawPDFPage**: 绘制一个PDF页面到当前的用户空间

建立一个基于页面的图形上下文

CGContextBeginPage
CGContextEndPage

管理图形上下文

CGContextRetain
CGContextRelease
CGContextSynchronize

锯齿功能

CGContextSetShouldAntialias:设置图形上下文的抗锯齿开启或关闭。
CGContextSetAllowsAntialiasing

字体展示功能

CGContextSetShouldSmoothFonts
CGContextSetAllowsFontSmoothing
CGContextSetShouldSubpixelPositionFonts
CGContextSetAllowsFontSubpixelPositioning
CGContextSetShouldSubpixelQuantizeFonts
CGContextSetAllowsFontSubpixelQuantization

使用透明图层

Quartz的透明层类似于许多流行的图形应用中的层。层是独立的实体。Quartz维护为每个上下文维护一个透明层栈,并且透明层是可以嵌套的。但由于层通常是栈的一部分,所以我们不能单独操作它们。

我们通过调用函数CGContextBeginTransparencyLayer来开始一个透明层,该函数需要两个参数:图形上下文与CFDictionary对象。字典中包含我们所提供的指定层额外信息的选项,但由于Quartz 2D API中没有使用字典,所以我们传递一个NULL。在调用这个函数后,图形状态参数保持不变,除了alpha值[默认设置为1]、阴影[默认关闭]、混合模式[默认设置为normal]、及其它影响最终组合的参数。

在开始透明层操作后,我们可以绘制任何想显示在层上的对象。指定上下文中的绘制操作将被当成一个组合对象绘制到一个透明背景上。这个背景被当作一个独立于图形上下文的目标缓存。

当绘制完成后,我们调用函数CGContextEndTransparencyLayer。Quartz将结合对象放入上下文,并使用上下文的全局alpha值、阴影状态及裁减区域作用于组合对象。

在透明层中绘制需要三步:

  1. 调用函数CGContextBeginTransparencyLayer
  2. 在透明层中绘制需要组合的对象
  3. 调用函数CGContextEndTransparencyLayer

CGContextBeginTransparencyLayer:直到相应的调用CGContextEndTransparencyLayer,在指定范围内的所有后续绘制操作组合到一个完全透明的背景(它被视为一个单独的目标缓冲区从上下文)
CGContextBeginTransparencyLayerWithRect
CGContextEndTransparencyLayer

//代码示例:
    CGContextBeginTransparencyLayer(context, NULL);
    
    CGFloat wd = 300;
    CGFloat ht = 300;
    CGContextSetFillColorWithColor(context, [UIColor greenColor].CGColor);
    CGContextFillRect(context, CGRectMake (wd/3 + 50, ht/2, wd/4, ht/4));

    CGContextEndTransparencyLayer(context);

用户空间与设备空间互换

CGContextGetUserSpaceToDeviceSpaceTransform
CGContextConvertPointToDeviceSpace
CGContextConvertPointToUserSpace
CGContextConvertSizeToDeviceSpace
CGContextConvertSizeToUserSpace
CGContextConvertRectToDeviceSpace
CGContextConvertRectToUserSpace

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

推荐阅读更多精彩内容