Quartz 2D

1.1 The Graphics Context

所有的东西都是画在CGContextRef类型的对象上

Quartz 2D中一些数据类型

  • CGPathRef:用于绘画的线
  • CGImageRef:一个矢量图片
  • CGLayerRef:一个绘画层,可以重复绘画和离屏绘画
  • CGPatternRef:用于重复绘画的
  • CGShadingRef和CGFrandientRef:用于渐变绘画
  • CGFunctionRef:回调方法,一般用户画渐变图形时使用
  • CGColorRef和CGColorSpaceRef:颜色描述
  • CGImageSourceRef和CGImageDestinationRef:将数据放到Quartz或取出
  • CGFontRef:用于画文字
  • CGPDFDictionaryRef,CGPDFObjectRef,CGPDFPageRef,CGPDFStream,CGPDFStringRef和CGPDFArrayRef:用于访问PDF元数据
  • CGPDFScannerRef和CGPDFContentStreamRef:用于解析PDF元数据

1.2 绘画状态

保存当前绘画状态CGContextSaveGState

恢复恢复状态CGContextSaveGState

1.3 Quartz 2D坐标系

屏幕快照 2017-05-09 下午6.41.58

屏幕快照 2017-05-09 下午6.47.39

2.1 在iOS中获取画布

  • UIView中实现drawRect:方法,视图会自动配置绘画环境,你可以直接在这个方法里进行绘画,系统已经自动将坐标系转换与UIKit相同的坐标系

  • 使用UIGraphicsGetCurrentContext获取CGContectRef对象,拿到画布进行绘画

CGContextRef ctxRef = UIGraphicsGetCurrentContext();

2.2 在MacOS中获取画布

CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];

这里需要自己转换坐标系

2.3 创建PDF画布

**有两种方式创建PDF画布

  1. CGPDFContextCreateWithURL指定PDF输出路径
CGContextRef MyPDFContextCreate(const CGRect *inMediaBox,CFStringRef path) {

    CGContextRef contextRef = NULL;
    CFURLRef url;
    //创建以保存PDF的路径
    url = CFURLCreateWithFileSystemPath(NULL, 
                                        path, 
                                        kCFURLPOSIXPathStyle, 
                                        false);

    if(url != NULL){

        contextRef = CGPDFContextCreateWithURL(url, 
                                               inMediaBox, 
                                               NULL);
        CFRelease(url);
    }
    return contextRef;
}

2.CGPDFContextCreate当你想讲PDF数据发送给用户是使用这个方法创建

CGContextRef MyPDFContextCreate (const CGRect *inMediaBox,
                                    CFStringRef path)
{
    CGContextRef        myOutContext = NULL;
    CFURLRef            url;
    CGDataConsumerRef   dataConsumer;

    url = CFURLCreateWithFileSystemPath (NULL,
                                        path,
                                        kCFURLPOSIXPathStyle,
                                        false);

    if (url != NULL)
    {
        dataConsumer = CGDataConsumerCreateWithURL (url);
        if (dataConsumer != NULL)
        {
            myOutContext = CGPDFContextCreate (dataConsumer,
                                               inMediaBox,
                                               NULL);
            CGDataConsumerRelease (dataConsumer);
        }
        CFRelease(url);
    }
    return myOutContext;
}

注:需要转换坐标系

在PDF画布上绘画

CGRect mediaBox;

mediaBox = CGRectMake (0, 0, myPageWidth, myPageHeight);
myPDFContext = MyPDFContextCreate (&mediaBox, CFSTR("test.pdf"));

CFStringRef myKeys[1];
CFTypeRef myValues[1];
myKeys[0] = kCGPDFContextMediaBox;
myValues[0] = (CFTypeRef) CFDataCreate(NULL,(const UInt8 *)&mediaBox, sizeof (CGRect));
CFDictionaryRef pageDictionary = CFDictionaryCreate(NULL, (const void **) myKeys,(const void **) myValues,1,&kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks);
CGPDFContextBeginPage(myPDFContext, &pageDictionary);
    // ********** Your drawing code here **********
    CGContextSetRGBFillColor (myPDFContext, 1, 0, 0, 1);
    CGContextFillRect (myPDFContext, CGRectMake (0, 0, 200, 100 ));
    CGContextSetRGBFillColor (myPDFContext, 0, 0, 1, .5);
    CGContextFillRect (myPDFContext, CGRectMake (0, 0, 100, 200 ));
CGPDFContextEndPage(myPDFContext);
CFRelease(pageDictionary);
CFRelease(myValues[0]);
CGContextRelease(myPDFContext);

2.4 创建矢量图画布

//创建Bitmap Graphics Context
CGContextRef MyCreateBitmapContext (int pixelsWide,
                            int pixelsHigh)
{
    CGContextRef    context = NULL;
    CGColorSpaceRef colorSpace;
    void *          bitmapData;
    int             bitmapByteCount;
    int             bitmapBytesPerRow;

    bitmapBytesPerRow   = (pixelsWide * 4);
    bitmapByteCount     = (bitmapBytesPerRow * pixelsHigh);

    colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    bitmapData = calloc( bitmapByteCount, sizeof(uint8_t) );
    if (bitmapData == NULL)
    {
        fprintf (stderr, "Memory not allocated!");
        return NULL;
    }
    //bitmapData可以传NULL,系统会自动申请内存空间
    context = CGBitmapContextCreate (bitmapData,
                                    pixelsWide,
                                    pixelsHigh,
                                    8,      // bits per component
                                    bitmapBytesPerRow,
                                    colorSpace,
                                    kCGImageAlphaPremultipliedLast);
    if (context== NULL)
    {
        free (bitmapData);
        fprintf (stderr, "Context not created!");
        return NULL;
    }
    CGColorSpaceRelease( colorSpace );

    return context;
}
CGRect myBoundingBox;

myBoundingBox = CGRectMake (0, 0, myWidth, myHeight);
myBitmapContext = MyCreateBitmapContext (400, 300);
// ********** Your drawing code here ********** 
CGContextSetRGBFillColor (myBitmapContext, 1, 0, 0, 1);
CGContextFillRect (myBitmapContext, CGRectMake (0, 0, 200, 100 ));
CGContextSetRGBFillColor (myBitmapContext, 0, 0, 1, .5);
CGContextFillRect (myBitmapContext, CGRectMake (0, 0, 100, 200 ));
myImage = CGBitmapContextCreateImage (myBitmapContext);
CGContextDrawImage(myContext, myBoundingBox, myImage);
char *bitmapData = CGBitmapContextGetData(myBitmapContext); 
CGContextRelease (myBitmapContext);
if (bitmapData) free(bitmapData); 
CGImageRelease(myImage);

3. 路径的创建和绘画

点:使用 CGContextMoveToPoint方法移动到一个点,作为一条路径的七点

//第一个参数是画布,第二三的事点的x,y坐标
CGContextMoveToPoint(ctx, 0, 0);

线:画一条线需要有一个起点和一个终点,代用方法 CGContextAddLineToPoint来制定一个终点,也可以调用 CGContextAddLines添加多条线,数组中第一个点是起点,其他的都是终点

//这是画一条线的完整代码
CGContextMoveToPoint(ctx, 0, 0);    //指定起点
CGContextAddLineToPoint(ctx, 10, 10);   //指定终点
//这是添加多条线
CGPoint points[] = {CGPointMake(50, 50),CGPointMake(100, 50),CGPointMake(100, 100),CGPointMake(50, 100)};
CGContextAddLines(ctx, points, 4);

**弧:**Quartz提供了两个方法创建弧, CGContextAddArcCGContextAddArcToPoint

//需要给一个圆的中心点x、y坐标和圆的半径,还有圆的起始角度和结束角度,以及一个顺时针逆时针参数,默认情况下1代表顺时针,0代表逆时针,但是在UIView的drawRect方法里由于系统自动旋转了坐标系,此时0代表顺时针,1代表逆时针
CGContextAddArc(ctx, 100, 100, 50, 0, M_PI_4*3, 1);

//CGContextAddArcToPoint需要一个当前点和两个弧线的点以及一个圆半径,不建议使用此方法创建弧线,计算难度大
CGContextMoveToPoint(ctx, 80, 50);
CGContextAddArcToPoint(ctx, 50, 50, 80, 100, 100);

曲线:

//使用CGContextAddCurveToPoint创建需要有一个起点一个终点还有两个控制点,示意图如下
CGContextMoveToPoint(ctx, 50, 100);
CGContextAddCurveToPoint(ctx, 60, 30, 80, 150, 90, 100);

屏幕快照 2017-05-10 下午5.10.05

    //使用CGContextAddQuadCurveToPoint创建曲线要有起点终点和一个控制点
    CGContextMoveToPoint(ctx, 50, 100);
    CGContextAddQuadCurveToPoint(ctx, 80, 50, 100, 100);

屏幕快照 2017-05-10 下午5.12.49

闭合路径:使用CGContextClosePath方法可以将当前路径封闭,系统会从当前点画一条直线指向路径起始点

椭圆:调用CGContextAddEllipseInRect方法,指定一个矩形的的frame,创建矩形的内切圆

矩形:调用CGContextAddRect方法即可

——————— 创建路径 ———————
  1. 创建一个路径之前需要先调用CGContextBeginPath
  2. 指定路径的起点,必须调用CGContextMoveToPoint方法指定起点,让后才可以添加直线、弧线和曲线等
  3. 如果想要闭合路径需要调用CGContextClosePath方法,调用该方法后,默认后面所画的就是一条新的路径
  4. 画弧线是,系统会自动在当前点和弧线的起始点之间画一条直线,弧线的终点变为当前点
  5. 添加椭圆或者矩形就是添加一个封闭的路径
  6. 创建路径并不是绘画路径,想要绘画路径必须调用用于绘画的方法,填充或者画线

注:如果想要重复绘画路径就需要把路径保存,这是可以使用CGPathRef创建路径,然后调用 CGContextAddPath将路径添加到画布上,CGPathRef的绘画方法如下

  • CGPathCreateMutable替换CGContextBeginPath
  • CGPathMoveToPoint替换 CGContextMoveToPoint
  • CGPathAddLineToPoint替换 CGContextAddLineToPoint
  • CGPathAddCurveToPoint替换 CGContextAddCurveToPoint
  • CGPathAddEllipseInRect替换 CGContextAddEllipseInRect
  • CGPathAddArc替换 CGContextAddArc
  • CGPathAddRect替换 CGContextAddRect
  • CGPathCloseSubpath替换 CGContextClosePath
——————— 描绘路径 ————————

一些影响划线的属性:

属性设置属性的方法
Line widthCGContextSetLineWidth
Line joinCGContextSetLineJoin
Line capCGContextSetLineCap
Miter limitCGContextSetMiterLimit
Line dash patternCGContextSetLineDash
Stroke color spaceCGContextSetStrokeColorSpace
Stroke colorCGContextSetStrokeColor“CGContextSetStrokeColorWithColor
Stroke patternCGContextSetStrokePattern

Line join styles

StyleAppearance
Miter joinMiter join
Round joinRound join
Bevel joinBevel join

Line cap styles

StyleAppearance
Butt capButt cap
Round capRound cap
Projecting square capProjecting square cap

Line dash pattern

//设置虚线的方法
void CGContextSetLineDash ( CGContextRef ctx,CGFloat phase,const CGFloat lengths[],size_t count);
//phase表示虚线往左移动的距离,和二进制的左移类似
//lengths表示虚线的展示方式
//count表示lengths中前几个数字有效,count不能超过lengths中的数据量超过就不好控制

几种情况的对比

CGFloat lengths[] = {30,10};
CGContextSetLineDash(ctx, 0, lengths, 2);
//lengths是里是虚线实现交替,30实线,10跳过,30实线,10跳过,如此反复,count表示使用lengths里

屏幕快照 2017-05-12 上午11.36.03

CGFloat lengths[] = {30,10,5};
CGContextSetLineDash(ctx, 0, lengths, 2);//lengths中前两个数字有效

屏幕快照 2017-05-12 上午11.53.31

CGFloat lengths[] = {30,10,5};
CGContextSetLineDash(ctx, 0, lengths, 3);
//30实现,10跳过,5实线,30跳过,10实线,5跳过,如此虚幻,所有虚线最好设置lengths个数为偶数比较合适

屏幕快照 2017-05-12 上午11.54.48

CGFloat lengths[] = {30,10,5};
CGContextSetLineDash(ctx, 15, lengths, 3);
//设置了phase,虚线想做以15,后面自动补全

屏幕快照 2017-05-12 上午11.57.29

描绘路径的方法

FunctionDescription
CGContextStrokePath描绘当前路径
CGContextStrokeRect描绘一个指定的矩形
CGContextStrokeRectWithWidth描绘一个指定的矩形并指定线的宽度
CGContextStrokeEllipseInRect描绘一个指定的矩形的内切椭圆
CGContextStrokeLineSegments描绘几个线段,数组必须是偶数,每一对包括线段的起点和终点
CGContextDrawPath设置描绘模式,比如划线、填充等
——————— 填充路径 ————————

这里写图片描述

填充有两种规则:

  • 上图左边图形Winding-number规则,假设逆时针方向圈数加1,顺时针防线减1,看不中不同封闭区域的圈数,圈数为0的区域不填充,圈数不为0的区域填充。
  • 上图右边Even-odd规则,圈数是奇数就填充,圈数是偶数就不填充,方向不会影响填充结果
FunctionDescription
CGContextEOFillPath使用Even-odd规则填充
CGContextFillPath使用Winding-number规则填充
CGContextFillRect画一个矩形,并使用Winding-number规则填充改矩形
CGContextFillRects画若干矩形,并使用Winding-number规则填充这些矩形
CGContextFillEllipseInRect画一个内切圆,并使用Winding-number规则填充改圆
CGContextDrawPath指定模式绘画,如果选择 kCGPathFillStroke 则使用 Winding-number规则填充,如果选择kCGPathEOFillStroke,则使用Even-odd规则
————————— 剪切路径 —————————

你可以通过设置剪切区域来显示某个特定区域的图像,多用于图片剪裁

//需要先设置剪切区域在绘图,剪切区域是绘制属性,和填充颜色、线条宽度一样,需要先设置再使用
CGContextAddArc(ctx, 150, 200, 80, 0, M_PI*2, 0);
CGContextEOClip(ctx);

CGContextFillRect(ctx, rect);
FunctionDescription
CGContextClip使用Winding-number规则剪切
CGContextEOClip使用Even-odd规则剪切
CGContextClipToRect画一个矩形,并使用Winding-number规则jin进行剪切
CGContextClipToRects画若干矩形,并使用Winding-number规则剪切
CGContextClipToMask使用图片作为遮罩剪切
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值