Core Graphics(Quartz 2D)

2019-01-10  本文已影响6人  liboxiang

详情:南峰子博客 http://southpeak.github.io/2014/11/10/quartz2d-1/

图形上下文

Graphics Context是一个数据类型(CGContextRef),用于封装Quartz绘制图像到输出设备的信息。换句话说,我们可以简单地给Quartz绘图序列指定不同的Graphics Context,就可将相同的图像绘制到不同的设备上。我们不需要处理任何设备相关的计算;这些都由Quartz替我们完成。
Quartz提供了以下几种类型的Graphics Context:

CGContextSaveGState和CGContextRestoreGState
-(void)drawRect:(CGRect)rect{

    CGContextRef ctx=UIGraphicsGetCurrentContext();
    [[UIColor redColor] setStroke];                                 //红色
    
    CGContextSaveGState(UIGraphicsGetCurrentContext());
    
    CGContextAddEllipseInRect(ctx, CGRectMake(100, 100, 50, 50));
    CGContextSetLineWidth(ctx, 10);
    [[UIColor yellowColor] setStroke];                               //黄色
    CGContextStrokePath(ctx);
    
    CGContextRestoreGState(UIGraphicsGetCurrentContext());
    
    CGContextAddEllipseInRect(ctx, CGRectMake(200, 100, 50, 50));    //红色
    CGContextStrokePath(ctx);
}
295A3C3F-053D-4183-9B75-2FA46620B6CF.png
获取上下文的途径

Quartz 2D 坐标系统

在iOS 3.2及后续的版本中,当UIKit为你的应用程序创建一个绘图上下文时,也对上下文进行了额外的修改以匹配UIKit的约定。特别的,patterns和shadows(不被CTM影响)单独进行调整以匹配UIKit坐标系统。

内存管理:对象所有权

路径 Path

开始新的路径

CGContextBeginPath//开始新的路径,就的路径被放弃

CGContextMoveToPoint

直线

CGContextAddLineToPoint//添加一条直线到子路径中
CGContextAddLines//传递一个点数组给这个函数

CGContextAddArc//从圆中来创建一个曲线段
CGContextAddArcToPoint//弧线与当前点到点(x1, y1)连线相切,且与点(x1, y1)到点(x2, y2)连线相切

曲线(Bezier)

CGContextAddCurveToPoint


cubic_bezier_curve.gif

CGContextAddQuadCurveToPoint


quadratic_bezier_curve.gif
闭合路径

CGContextClosePath//该函数用一条直接来连接当前点与起始点,以使路径闭合

椭圆

CGContextAddEllipseInRect
添加到路径中的椭圆开始于一个move-to操作,结束于一个close-subpath操作,所有的移动方向都是顺时针。

矩形

CGContextAddRect//添加一个矩形到当前路径中
CGContextAddRects//添加一系列的矩形到当前路径

通过可变路径对象创建路径

我们也许想保留路径,特别是在绘制复杂场景时,我们需要反复使用。基于此,Quartz提供了两个数据类型来创建可复用路径—CGPathRef和CGMutablePathRef。我们可以调用函数CGPathCreateMutable来创建可变的CGPath对象,并可向该对象添加直线、弧、曲线和矩形。Quartz提供了一个类似于操作图形上下文的CGPath的函数集合。这些路径函数操作CGPath对象,而不是图形上下文。这些函数包括:

CGPathCreateMutable,取代CGContextBeginPath
CGPathMoveToPoint,取代CGContextMoveToPoint
CGPathAddLineToPoint,取代CGContexAddLineToPoint
CGPathAddLines,取代CGContextAddLines
CGPathAddCurveToPoint,取代CGContexAddCurveToPoint
CGPathAddQuadCurveToPoint,取代CGContexAddQuadCurveToPoint
CGPathAddEllipseInRect,取代CGContexAddEllipseInRect
CGPathAddArc,取代CGContexAddArc
CGPathAddArcToPoint,取代CGContexAddArcToPoint
CGPathAddRect,取代CGContexAddRect
CGPathAddRects,取代CGContexAddRects
CGPathCloseSubpath,取代CGContexClosePath

如果想要添加一个路径到图形上下文,可以调用CGContextAddPath。路径将保留在图形上下文中,直到Quartz绘制它。我们可以调用CGContextAddPath再次添加路径。

路径描边和填充

详情看南峰子博客 http://southpeak.github.io/2014/11/10/quartz2d-1/

裁剪路径

CGContextClip
CGContextEOClip
CGContextClipToRect
CGContextClipToRects
CGContextClipToMask

颜色和颜色空间

Quartz中的颜色是用一组值来表示。而颜色空间用于解析这些颜色信息。


quartz-2d-table4-1.png
alpha值

alpha值为1.0时表示新对象是完全不透明的,值0.0表示新对象是完全透明的。
Quartz使用下面的公式来混合源颜色和目标颜色的组件:
destination = (alpha source) + (1 - alpha) destination

创建设备颜色空间

设备颜色空间主要用于IOS应用程序,因为其它颜色空间无法在IOS上使用。

返回CGColorSpaceRef类型数据

  • CGColorSpaceCreateDeviceGray:创建设备依赖灰度颜色空间
  • CGColorSpaceCreateDeviceRGB:创建设备依赖RGB颜色空间
  • CGColorSpaceCreateDeviceCMYK:创建设备依赖CMYK颜色空间
设置和创建颜色

我们可以使用CGContextSetFillColorSpace和CGContextSetStrokeColorSpace函数来设置填充和线框颜色空间,或者可以使用以下便利函数来设置设备颜色空间的颜色值。


quartz-2d-table4-2.png

我们可以调用CGColorCreate函数来创建CGColor对象,该函数需要两个参数:CGColorspace对象及颜色值数组。数组的最后一个值指定alpha值。

变换

修改CTM(Current Transformation Matrix)

我们可以通过操作CTM(current transformation matrix)来修改默认的用户空间。在创建图形上下文后,CTM是单位矩阵,我们可以使用 Quartz的变换函数来修改CTM,从而修改用户空间中的绘制操作。

CGContextTranslateCTM (myContext, w/4, 0);
CGContextScaleCTM (myContext, .25,  .5);
CGContextRotateCTM (myContext, radians ( 22.));
创建仿射变换

仿射变换操作在矩阵上,而不是在CTM上。我们可以使用这些函数来构造一个之后用于CTM(调用函数CGContextConcatCTM)的矩阵。仿射变换函数使用或者返回一个CGAffineTransform数据对象。


quartz-2d-table5-1.png

模式(Pattern)

模式(Pattern)是绘制操作的一个序列,这些绘制操作可以重复地绘制到一个图形上下文上。我们可以像使用颜色一样使用这些模式。当我们使用pattern来绘制时,Quartz将Page分割成模式单元格的集合,其中每个单元格的大小是模式图片的大小,并使用我们提供的回调函数来绘制这些单元格。

我们可以指定水平和竖直方向上两个单元格之间的间距,亦可以指定间距为负数,这样单元格便会重叠。

着色模式

绘制着色模式需要执行以下五步操作:

  • 写一个绘制着色模式单元格的回调函数
  • 设置着色模式的颜色空间
  • 设置着色模式的骨架(Anatomy)
  • 指定着色模式作为填充或描边模式
  • 使用着色模式绘制
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    UIGraphicsBeginImageContext(self.view.bounds.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    MyColoredPatternPainting(context, CGRectMake(100, 100, 200, 200));
    
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    self.view.layer.contents = (id)image.CGImage;
}

void MyDrawColoredPattern(void *info, CGContextRef myContext)
{
    CGFloat subunit = 5;
    
    CGSize size = {subunit, subunit};
    CGPoint point1 = {0,0}, point2 = {subunit, subunit}, point3 = {0,subunit}, point4 = {subunit,0};
    CGRect myRect1 = {point1, size}, myRect2 = {point2, size}, myRect3 = {point3, size}, myRect4 = {point4, size};
    
    CGContextSetRGBFillColor (myContext, 0, 0, 1, 0.5);
    CGContextFillRect (myContext, myRect1);
    CGContextSetRGBFillColor (myContext, 1, 0, 0, 0.5);
    CGContextFillRect (myContext, myRect2);
    CGContextSetRGBFillColor (myContext, 0, 1, 0, 0.5);
    CGContextFillRect (myContext, myRect3);
    CGContextSetRGBFillColor (myContext, .5, 0, .5, 0.5);
    CGContextFillRect (myContext, myRect4);
}

#define PSIZE 16
void MyColoredPatternPainting(CGContextRef myContext,CGRect rect)
{
    CGPatternRef    pattern;
    CGColorSpaceRef patternSpace;
    CGFloat         alpha = 1;
    static const CGPatternCallbacks callbacks = {0, &MyDrawColoredPattern, NULL};
    
    CGContextSaveGState (myContext);
    patternSpace = CGColorSpaceCreatePattern (NULL);
    CGContextSetFillColorSpace (myContext, patternSpace);
    CGColorSpaceRelease (patternSpace);
    pattern = CGPatternCreate(NULL, CGRectMake(0, 0, PSIZE, PSIZE),
                              CGAffineTransformIdentity, PSIZE, PSIZE,
                              kCGPatternTilingConstantSpacing,
                              false, &callbacks);
    CGContextSetFillPattern (myContext, pattern, &alpha);
    CGPatternRelease (pattern);
    CGContextFillRect (myContext,rect);
    CGContextRestoreGState (myContext);
}

模版模式

与绘制着色模式类似,绘制模板模式也有5个步骤:

  • 写一个绘制模板模式单元格的回调函数
  • 设置模板模式的颜色空间
  • 设置模板模式的骨架(Anatomy)
  • 指定模板模式作为填充或描边模式
  • 使用模板模式绘制

绘制模板模式与绘制着色模式的区别在于设置颜色信息。

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    UIGraphicsBeginImageContext(self.view.bounds.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    MyStencilPatternPainting(context, nil);
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    self.view.layer.contents = (id)image.CGImage;
}

#define PSIZE 16

#pragma mark - 模版模式

static void MyDrawStencilStar (void *info, CGContextRef myContext)
{
    int k;
    double r, theta;
    
    r = 0.8 * PSIZE / 2;
    theta = 2 * M_PI * (2.0 / 5.0); // 144 degrees
    
    CGContextTranslateCTM (myContext, PSIZE/2, PSIZE/2);
    
    CGContextMoveToPoint(myContext, 0, r);
    for (k = 1; k < 5; k++) {
        CGContextAddLineToPoint (myContext,
                                 r * sin(k * theta),
                                 r * cos(k * theta));
    }
    CGContextClosePath(myContext);
    CGContextFillPath(myContext);
}

void MyStencilPatternPainting (CGContextRef myContext,
                               const Rect *windowRect)
{
    CGPatternRef pattern;
    CGColorSpaceRef baseSpace;
    CGColorSpaceRef patternSpace;
    static const CGFloat color[4] = { 0, 1, 0, 1 };
    static const CGPatternCallbacks callbacks = {0, &MyDrawStencilStar, NULL};
    
    baseSpace = CGColorSpaceCreateDeviceRGB ();
    patternSpace = CGColorSpaceCreatePattern (baseSpace);
    CGContextSetFillColorSpace (myContext, patternSpace);
    CGColorSpaceRelease (patternSpace);
    CGColorSpaceRelease (baseSpace);
    pattern = CGPatternCreate(NULL, CGRectMake(0, 0, PSIZE, PSIZE),
                              CGAffineTransformIdentity, PSIZE, PSIZE,
                              kCGPatternTilingConstantSpacing,
                              false, &callbacks);
    CGContextSetFillPattern (myContext, pattern, color);
    CGPatternRelease (pattern);
    CGContextFillRect (myContext,CGRectMake (0,0,PSIZE*20,PSIZE*20));
}

阴影

  • x偏移值,用于指定阴影相对于图片在水平方向上的偏移值。
  • y偏移值,用于指定阴影相对于图片在竖直方向上的偏移值。
  • 模糊(blur)值,用于指定图像是有一个硬边,还是一个漫射边
绘制阴影
  • 保存图形状态
  • 调用函数CGContextSetShadow,传递相应的值
  • 使用阴影绘制所有的对象
  • 恢复图形状态
  • 保存图形状态
  • 创建一个CGColorSpace对象,确保Quartz能正确地解>* 析阴影颜色
  • 创建一个CGColor对象来指定阴影的颜色
  • 调用CGContextSetShadowWithColor,并传递相应的值
  • 使用阴影绘制所有的对象
  • 恢复图形状态
void MyDrawWithShadows (CGContextRef myContext, float wd, float ht)
{
    CGSize          myShadowOffset = CGSizeMake (-15,  20);
    CGFloat           myColorValues[] = {1, 0, 0, .6};
    CGColorRef      myColor;
    CGColorSpaceRef myColorSpace;
    
    CGContextSaveGState(myContext);
    
    CGContextSetShadow (myContext, myShadowOffset, 5);
    
    // Your drawing code here
    CGContextSetRGBFillColor (myContext, 0, 1, 0, 1);
    CGContextFillRect (myContext, CGRectMake (wd/3 + 75, ht/2 , wd/4, ht/4));
    
    myColorSpace = CGColorSpaceCreateDeviceRGB ();
    myColor = CGColorCreate (myColorSpace, myColorValues);
    CGContextSetShadowWithColor (myContext, myShadowOffset, 5, myColor);
    
    // Your drawing code here
    CGContextSetRGBFillColor (myContext, 0, 0, 1, 1);
    CGContextFillRect (myContext, CGRectMake (wd/3-75,ht/2-100,wd/4,ht/4));
    
    CGColorRelease (myColor);
    CGColorSpaceRelease (myColorSpace);
    
    CGContextRestoreGState(myContext);
}

渐变

透明层

透明层(TransparencyLayers)通过组合两个或多个对象来生成一个组合图形。
在开始透明层操作后,我们可以绘制任何想显示在层上的对象。指定上下文中的绘制操作将被当成一个组合对象绘制到一个透明背景上。这个背景被当作一个独立于图形上下文的目标缓存。
当绘制完成后,我们调用函数CGContextEndTransparencyLayer。Quartz将结合对象放入上下文,并使用上下文的全局alpha值、阴影状态及裁减区域作用于组合对象。
在透明层中绘制需要三步:

  • 调用函数CGContextBeginTransparencyLayer
  • 在透明层中绘制需要组合的对象
  • 调用函数CGContextEndTransparencyLayer
void MyDrawTransparencyLayer (CGContextRef myContext, float wd,float ht)
{
    CGSize myShadowOffset = CGSizeMake (10, -20);
    CGContextSetShadow (myContext, myShadowOffset, 10);
    CGContextBeginTransparencyLayer (myContext, NULL);
    
    // Your drawing code here
    CGContextSetRGBFillColor (myContext, 0, 1, 0, 1);
    CGContextFillRect (myContext, CGRectMake (wd/3+ 50,ht/2 ,wd/4,ht/4));
    CGContextSetRGBFillColor (myContext, 0, 0, 1, 1);
    CGContextFillRect (myContext, CGRectMake (wd/3-50,ht/2-100,wd/4,ht/4));
    CGContextSetRGBFillColor (myContext, 1, 0, 0, 1);
    CGContextFillRect (myContext, CGRectMake (wd/3,ht/2-50,wd/4,ht/4));
    CGContextEndTransparencyLayer (myContext);
}

位图与图像遮罩

一个位图是一个像素数组。每一个像素表示图像中的一个点。
一个图像遮罩也是一个位图,它指定了一个绘制区域,而不是颜色。从效果上来说,一个图像遮罩更像一个模块,它指定在page中绘制颜色的位置。Quartz使用当前的填充颜色来绘制一个图像遮罩。一个颜色遮罩可以有1-8位的深度。

一个图像遮罩的采样如同一个反转的alpha值。一个图像遮罩采样值(S):

  • 为1时,则不会绘制对应的图像样本。
  • 为0时,则允许完全绘制对应的图像样本。
  • 0和1之间的值,则让对应的图像样本的alpha的值为(1-S)。

用于遮罩的图像的采样也是操作alpha值。一个图像采样值(S):

  • 为1时,则允许完全绘制对应的图像样本。
  • 为0时,则不会绘制对应的图像样本。
  • 0和1之间的值,则让对应的图像样本的alpha的值为S。

函数CGImageCreateWithMaskingColors有两个参数:

  • 一个图像,它不能是遮罩图像,也不能是使用过图像遮罩或颜色遮罩的图像。
  • 一个颜色分量数组,指定了一个颜色或一组颜色值,以用于遮罩图像。


    chroma_key.gif
UIGraphicsBeginImageContext(self.view.bounds.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
UIImage *oneImage = [UIImage imageNamed:@"one.png"];
    UIImage *twoImage = [UIImage imageNamed:@"two.png"];
CGImageRef maskRef = oneImage.CGImage;
    CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
                                        CGImageGetHeight(maskRef),
                                        CGImageGetBitsPerComponent(maskRef),
                                        CGImageGetBitsPerPixel(maskRef),
                                        CGImageGetBytesPerRow(maskRef),
                                        CGImageGetDataProvider(maskRef), NULL, false);
    CGContextClipToMask(context, CGRectMake(50, 100, 200, 200), mask);
    [twoImage drawInRect:CGRectMake(50, 100, 200, 200)];
    
    
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    CGImageRelease(mask);
1D7B2315-6322-4CAD-8B48-A5BC4F808662.png
UIGraphicsBeginImageContext(self.view.bounds.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    UIImage *oneImage = [UIImage imageNamed:@"one.png"];
    UIImage *twoImage = [UIImage imageNamed:@"two.png"];
    CGContextClipToMask(context, CGRectMake(50, 100, 200, 200), oneImage.CGImage);
    [twoImage drawInRect:CGRectMake(50, 100, 200, 200)];
    
    
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
6705F5B9-5D1C-496F-B97F-4240A6A204E2.png

层绘制

层适合于以下几种情况:

上一篇下一篇

猜你喜欢

热点阅读