CoreGraphics with Swift

概述

Core Graphics Framework是一套基于C的API框架,使用了Quartz作为绘图引擎。它提供了低级别、轻量级、高保真度的2D渲染。该框架可以用于基于路径的绘图、变换、颜色管理、脱屏渲染,模板、渐变、遮蔽、图像数据管理、图像的创建、遮罩以及PDF文档的创建、显示和分析。Quartz 2DCore Graphics Framework的一部分。能实现以下的功能

  1. 画图
  2. 在程序中提供图形编辑
  3. 生成或者展示位图
  4. 和PDF文档工作

Painter Model

一个画图模型就是将CALayer上的内容渲染到画布上,

Graphics context

一个GraphicsContext对象是一个数据类型,封装了Quartz图像到设备的信息,总共有5种类型的context:

  1. 窗口
  2. PDF
  3. 位图
  4. 打印
  5. 反锯齿
    这些类型主要是包括:绘制参数,设备相关信息。

图形API

OpenGL ES是跨平台的图形API,属于OpenGL的一个简化版本。QuartZ 2D是苹果公司开发的一套API.OpenGL ES是应用程序编程接口,该接口描述了方法、结构、函数应具有的行为以及应该如何被使用的语义。也就是说它只定义了一套规范,具体的实现由设备制造商根据规范去做.

Core Graphics API所有的操作都在一个上下文中进行

获取当前context的方法

调用UIGraphicsBeginImageContextWithOptions函数就可获得用来处理图片的图形上下文。利用该上下文,你就可以在其上进行绘图,并生成图片。调用UIGraphicsGetImageFromCurrentImageContext函数可从当前上下文中获取一个UIImage对象。记住在你所有的绘图操作后别忘了调用UIGraphicsEndImageContext函数关闭图形上下文。UIViewCALayer中的渲染方法(比如drawRectdispaly)中,默认就是当前的context上实现的,当需要在其他的方法上去对layer上进行的渲染的时候,需要获取当前的山下文,通过func UIGraphicsGetCurrentContext() -> CGContext?获取当前的上下文。

绘图形式

用Core Graphics之前需要指定一个用于绘图的图形上下文(CGContextRef),这个图形上下文会在每个绘图函数中都会被用到。用UIKit使用的时候自定义UIView,实现drawRect方法时,系统会自动的生成一个CGContextRef。总共有6种的绘图方式。

  1. 在UIView的子类方法drawRect:

        func drawRect(_ aRect: NSRect) { 
    
             var p = UIBezierPath(UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0,100,100)) 
    
             UIColor.blueColor().setFill() 
    
             p.fill()
    
          } 
    
    
  2. 使用Core Graphics,在drawRect中实现

    func drawRect(_ aRect: NSRect) {

     let con = UIGraphicsGetCurrentContext()
    
     CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100))
    
     CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor)
    
     CGContextFillPath(con);  
    

    }

  3. CALayer的drawLayer上实现 。 view调用setNeedsDisplay,或者view.layer调用display。和1相似

  4. 和2相似,CALayerdrawLayer上实现 通过提供的上下文

  5. UIGraphicsBeginImageContextWithOptions实现

     UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), NO, 0); 
     let p = UIBezierPath(bezierPathWithOvalInRect:CGRectMake(0,0,100,100)) 
     UIColor.blueColor().setFill() 
     p.fill()
     let  im = UIGraphicsGetImageFromCurrentImageContext()
     UIGraphicsEndImageContext()
    
  6. 使用 UIGraphicsBeginImageContextWithOptions基于Core Graphics实现

     UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), NO, 0); 
    
     let con = UIGraphicsGetCurrentContext(); 
    
     CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100)); 
    
     CGContextSetFillColorWithColor(con, UIColor.blueColor().CGColor); 
    
     CGContextFillPath(con); 
    
     let im = UIGraphicsGetImageFromCurrentImageContext(); 
    
     UIGraphicsEndImageContext();
    

Quartz2D的数据类型

下面列出了Quartz 2D包含的数据类型:
CGPathRef:用于向量图,可创建路径,并进行填充或描画(stroke)
CGImageRef:用于表示bitmap图像和基于采样数据的bitmap图像遮罩。
CGLayerRef:用于表示可用于重复绘制(如背景)和幕后(offscreen)绘制的绘画层
CGPatternRef:用于重绘图
CGShadingRefCGGradientRef:用于绘制渐变
CGFunctionRef:用于定义回调函数,该函数包含一个随机的浮点值参数。当为阴影创建渐变时使用该类型
CGColorRef, CGColorSpaceRef:用于告诉Quartz如何解释颜色
CGImageSourceRef,CGImageDestinationRef:用于在Quartz中移入移出数据
CGFontRef:用于绘制文本
CGPDFDictionaryRef, CGPDFObjectRef, CGPDFPageRef, CGPDFStream, CGPDFStringRef, CGPDFArrayRef:用于访问PDF的元数据
CGPDFScannerRef, CGPDFContentStreamRef:用于解析PDF元数据
CGPSConverterRef:用于将PostScript转化成PDF。在iOS中不能使用。

CGPath路径的组成

  1. 线:实线,虚线--->通过CGPathAddLines(_:_:_:_:)等方法
  2. 点:通过moveToPoint(_:)等方法。
  3. 弧:CGContextAddArc(_:_:_:_:_:_:_:)CGContextAddArcToPoint(_:_:_:_:_:_:)等方法添加弧线
  4. 曲线:可以通过贝塞尔曲线获取CGContextAddArcToPoint(_:_:_:_:_:_:),或者自己定义一个贝塞尔曲线
  5. 最后可以选择闭合曲线CGContextClosePath(_:)

路径的创建

具体步骤是:

  1. 通过 UIGraphicsGetCurrentContext获取上下文,如果是在UIView和CALayer的渲染方法中,可以不用获取
  2. 在开始绘制路径前,调用函数CGContextBeginPath,用CGContextBeginPath来标记Quartz,因为context一个时间只能有一个path被使用,如果不使用的话,会丢弃之前的path
  3. 进行线和点的绘制,CGContextMoveToPoint CGContextAddLineToPoint
  4. 通过CGContextAddPath(_:_:)将路线加到上下文上

绘制路径

路径的绘制包括填充和描边,在绘制前可以先设置绘制属性,包括线宽,颜色等。

描边

描边的属性包括:线宽,连接点,是否为虚线。以下是一些常用的描边函数

描边函数

填充

  1. 填充区域:填充当前路径时,Quartz将路径包含的每个子路径都看作是闭合的。然后,使用这些闭合路径并计算填充的像素。但是如果路径是由几个重叠的部分组成或者路径包含多个子路径,则需一定的规则来定义填充区域(具体参考官方文档)
  2. 填充模式:Quartz默认使用普通混合模式(normal blend mode),也就是多种颜色重叠的时候,绘制模式。

路径剪裁

绘制时,Quartz只渲染裁剪区内的路径,裁剪区域内的闭合路径是可见的;而在区域外的部分是不可见的。

颜色与颜色空间

颜色空间是指一个设备所能表示的颜色,通过以下方法

  1. func deviceRGBColorSpace() -> NSColorSpace
  2. func deviceCMYKColorSpace() -> NSColorSpace
    颜色空间有许多种,常用有RGB,CMY,HSB,CMYK,BGR等.alpha表示存在的对象与新对象如何混合。
    CGContextSetAlpha可以指定全局alpha。

变化

在绘图空间上,分为用户空间和设备空间。context是作用在用户空间的,然后通过CTM(current transformation matrix)映射到设备空间。CTM有三种操作包括平移CGContextTranslateCTM(_:_:_:),旋转
CGContextRotateCTM(_:_:),缩放CGContextScaleCTM(_:_:_:)。从用户空间到设备空间的映射空间可以通过
CGContextGetUserSpaceToDeviceSpaceTransform(_:)获取到。

模式

模式(Pattern)是绘制操作的一个序列,这些绘制操作可以重复地绘制到一个图形上下文上。当用模式的时候,`Quartz将Page分割成模式单元格的集合,其中每个单元格的大小不是模式图片的大小,并使用我们提供的回调函数来绘制这些单元格。模式单元格之间可以设置间距,并且在模式单元格上被绘制

阴影(Shadow)

阴影包括阴影偏移(iOS和MacOS的坐标不一样),通过CGContextSetShadow(_:_:_:)CGContextSetShadowWithColor(_:_:_:_:)设置阴影。
具体的步骤如下:

  1. 保存图形状态CGContextSaveGState(_:)
  2. 调用CGContextSetShadow(_:_:_:)
  3. 使用阴影绘制对象
  4. 恢复图形状态

渐变

渐变对象主要是两个CGShadingRefCGGradientRef,CGGradientRef对象是对CGShadingRef的封装,区别是不需要提供渐变计算函数,只需要提供颜色和位置素组就好

CGGradientRef的创建和使用

使用过程:

  1. 创建CGGradientRef对象,提供一个颜色空间,以及两个以上的位置和颜色数组
  2. 调用CGContextDrawLinearGradient(_:_:_:_:_:)或者CGContextDrawRadialGradient(_:_:_:_:_:_:_:)
  3. 释放内存

使用CGGradient对象

一个CGGradient对象是一个渐变的抽象定义—它简单地指定了颜色值和位置,但没有指定几何形状。我们可以在轴向和径向几何形状中使用这个对象。作为一个抽象定义,CGGradient对象可能比CGShading对象更容易重用。没有将几何形状存储在CGGradient对象中,这样允许我们使用相同的颜色方案来绘制不同的几何图形,而不需要为多个图形创建多个CGGradient对象。

因为Quartz为我们计算渐变,使用一个CGGradient对象来创建和绘制一个渐变则更直接,只需要以下几步:

创建一个CGGradient对象,提供一个颜色空间,一个饱含两个或更多颜色组件的数组,一个包含两个或多个位置的数组,和两个数组中元素的个数。
调用CGContextDrawLinearGradient或CGContextDrawRadialGradient函数并提供一个上下文、一个CGGradient对象、绘制选项和开始结束几何图形来绘制渐变。
当不再需要时释放CGGradient对象。

let locations = 2;
let locations[2] = { 0.0, 1.0 };
let components[8] = { 1.0, 0.5, 0.4, 1.0, 0.8, 0.8, 0.3, 1.0 }
let myColorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
let myGradient = CGGradientCreateWithColorComponents (myColorspace, components,locations, num_locations);
// myStartPoint, myEndPoint起始点和终点
CGContextDrawLinearGradient (myContext, myGradient, myStartPoint, myEndPoint, 0)

CGShadingRef的创建和使用

CGShadingRef分为创建和使用

CGShadingRef的创建

  1. 获取颜色空间(CGColorSpace
  2. 设置起始点和终点
  3. 起始半径和终止半径
  4. CGFunctionCreate这个是关键:需要保证绘制到特低点的颜色值
  5. 一个bool值,指定是否用纯色来绘制起始点和终点的扩展区(可选值)

CGShadingRef的使用

  1. 设置CGFunction对象计算颜色值 : 也就是计算每一个位置的颜色
  2. 创建CGShadingRef对象
  3. 裁剪上下文
  4. 使用CGShading对象:CGFunctionEvaluate的重写
  5. 释放对象

透明

Quartz为每一个上下文维护一个透明层栈,绘制透明层的步骤如下

  1. 调用CGContextBeginTransparencyLayer(_:_:)方法表明开始绘制透明
  2. 在透明层中绘制需要的组合对象
  3. 调用CGContextEndTransparencyLayer(_:)后,透明才会被绘制上去

CGImageRef类型

CGImageRef是CGGraphics 的一种重要的数据类型,主要处理的是位图与图像的遮罩。创建一个位图(CGImageRef)时,Quartz使用以下信息:

  1. 位图数据源:可以是一个Quartz数据提供者或者是一个Quartz图像源。
  2. 可选的解码数组。(Decode Array)
  3. 插值设置:这是一个布尔值,指定Quartz在重置图像大小时是否使用插值算法。
  4. 渲染意图:指定如何映射位于图形上下文中的目标颜色空间中的颜色。该值在图像遮罩中不需要。
  5. 图像尺寸
  6. 像素格式,包括每个分量中的位数,每个像素的位数和每行中的字节数。

如何创建

创建一个位图对象主要有以下几种方法:

  1. 依赖于图像的数据源
  2. 常用CGImageCreate来创建一个我吐
  3. 从PNG,JPEG创建一个CGImage对象,主要通过:CGImageSourceCreateWithURL(_:_:)CGImageSourceCreateImageAtIndex(_:_:_:)
  4. 从位图上下文获取CGImage对象。调用CGBitmapContextCreateImage(_:)
  5. 从一个已存在图像中创建子图像CGImageCreateWithImageInRect(_:_:)

遮罩

一个位图图像遮罩定义了如何转换颜色,而不是使用哪些颜色。函数CGImageCreateWithMask通过将图像遮罩使用到一个图像上的方式来创建一个图像。

总结

首先CoreGraphics是Quartz的一部分,这部分大多数是C语言结构的,Swift和OC的使用上没有太大区别。而在获取到了上下文后,关键的是如何去确定Path,贝塞尔曲线等等,这部分也可以通过专门的工具,在这推荐一个好的软件PaintCode,能直接导出Swift代码和OC代码。具体可以参考用Sketch和PaintCode快速得到绘制代码。let's Swift

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

推荐阅读更多精彩内容