Quartz 2D介绍

Quartz 2D简单介绍

一、什么是Quartz 2D

Quarz 2D是一个二维绘画引擎,同时支持ios和mac,其API是Core Graphics框架的,是纯C语言的。IOS系统提供的大部分控件的内容都是通过Quartz 2D画出来的,因此Quartz 2D的一个很重要的价值是:自定义view(自定义UI控件)。

二、一个重要的概念:图形上下文

图形上下文(Graphics context)是一个CGContextRef数据,其作用是:

保存绘图信息、绘图属性 绘制目标图案 输出绘制好的图案到输出目标去,即渲染到什么地方去(可以是PDF文件、bitmap或者显示器的窗口上)

这里写图片描述

三、自定义view,即绘制view

在view中实现- (void)drawRect:(CGRect)rect方法,然后在方法中:
1. 取得跟当前view相关联的图形上下文;
2. 绘制相应的图形内容
3. 利用图形上下文将绘制好的图形内容渲染显示到view上面

Quartz 2D简单使用

代码示例
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<code class = "hljs" java= "" >    //绘制步骤:1、获取当前视图的图形上下文 2、开始绘图 3、渲染绘制内容
 
     /**绘制直线**/
 
     //1、获取当前视图的图形上下文(图形上下文决定绘制的输出目标)
     CGContextRef ctx = UIGraphicsGetCurrentContext();
 
     //2、开始绘图
     //设置起点
     CGContextMoveToPoint(ctx, 20 , 50 );
     //设置终点
     CGContextAddLineToPoint(ctx, 300 , 50 );
     //设置线条属性
     //设置颜色
     CGContextSetStrokeColorWithColor(ctx, [UIColor purpleColor].CGColor);
     //另外一种设置颜色的方式
//    [[UIColor purpleColor] set];
     //设置线条宽度
     CGContextSetLineWidth(ctx, 10 );
     //设置线条起点和终点的样式为圆角
     CGContextSetLineCap(ctx, kCGLineCapRound);
 
     //3、将画布上绘制的内容渲染到view的layer上
     CGContextStrokePath(ctx);
 
 
     /**绘制三角形**/
 
     //设置起点
     CGContextMoveToPoint(ctx, 150 , 80 );
     //设置第一个拐点
     CGContextAddLineToPoint(ctx, 220 , 150 );
     //设置第二个拐点
     CGContextAddLineToPoint(ctx, 80 , 150 );
     //设置第三个点(终点)
//    CGContextAddLineToPoint(ctx, 150, 80);
     //可以用下面方法代替 缝合起点和终点
     CGContextClosePath(ctx);
     //设置线条的拐点转角样式为圆角
     CGContextSetLineJoin(ctx, kCGLineJoinRound);
     //渲染
     CGContextStrokePath(ctx);
 
 
     /**绘制空心四边形**/
     CGContextAddRect(ctx, CGRectMake( 40 , 200 , 200 , 100 ));
     //设置空心(线条)颜色
//    CGContextSetStrokeColorWithColor(ctx, [UIColor lightGrayColor].CGColor);
     //也可以这样设置颜色
     [[UIColor lightGrayColor] setStroke];
     //设置线条宽度
     CGContextSetLineWidth(ctx, 10 );
     //渲染(空心的)
     CGContextStrokePath(ctx);
 
     /**绘制实心四边形**/
     CGContextAddRect(ctx, CGRectMake( 40 , 320 , 200 , 100 ));
     //设置实心颜色
//    CGContextSetFillColorWithColor(ctx, [UIColor orangeColor].CGColor);
     [[UIColor orangeColor] setFill];
     //渲染(实心的)
     CGContextFillPath(ctx);
 
     /**绘制圆(可以用绘制椭圆的方式画圆)**/
     //参数依次为圆心x,圆心y,半径,开始位置的弧度,结束位置的弧度,绘制路径(1为顺时针,0为逆时针)
     //由于Quartz2D的坐标系是x轴向右,y轴向上,不同于UIKit坐标系。因此在不将Quartz2D坐标系翻转的情况下,画出来的图形是与原图形关于x轴对称的。
     CGContextAddArc(ctx, 100 , 520 , 50 , 0 , M_PI_2, 1 );
     CGContextStrokePath(ctx); //空心
//    CGContextFillPath(ctx); //实心
 
     /**绘制椭圆**/
     CGContextAddEllipseInRect(ctx, CGRectMake( 230 , 400 , 120 , 200 ));
     CGContextSetStrokeColorWithColor(ctx, [UIColor blueColor].CGColor);
     CGContextStrokePath(ctx);
 
     /**绘制弧线**/
//    CGContextAddArcToPoint(ctx, 100, 200, 100, 200, 50);
     CGContextAddArc(ctx, 200 , 200 , 100 , 0 , M_PI, 1 );
     CGContextSetStrokeColorWithColor(ctx, [UIColor cyanColor].CGColor);
     CGContextStrokePath(ct
</code>
绘制结果

这里写图片描述

图形上下文栈

一、绘制原理

举个例子,假如要绘制两条线,一条红色一条默认的黑色。先绘制红色线,绘制完毕渲染上去后,再去绘制第二条。绘制第二条的时候如果不重新设置绘画颜色,那么绘制出来的线条也是红色的。也就是说,绘制属性如果不对其清空(即重新设置)是默认保留在图形上下文上的。因此可以这样理解:
一个图形上下文有3块区域,分别是绘制属性,图像信息,绘制区域:

绘制属性 :包括画笔的颜色、线条宽度、是否圆角等等;  绘图信息 :比如要绘制一条直线,那么保存了线条的起点和终点,绘制圆,保存了半径、中点坐标、起点终点、方向等等。也就是说,绘图信息中保存的是绘图路径,这个在下面会详细介绍。  绘图区域 :这个区域不是指视图上的区域,而是图形上下文上的绘制区域,因为绘画是在图形上下文上绘制好之后才被渲染到视图的制定区域上的。

二、保存图形上下文绘制属性

前面说过如果要绘制多个不同属性的图形,那么每次渲染好一个图形后就要重新设置绘制属性,通常绘制多个图形都是这样做的。有时候可能用到一个简单的方法:即在绘制一个图形前先保存当前图形上下文中的绘制属性,这个绘制属性会被保存到图形上下文栈上,如果下次需要绘制同样属性的图形,直接把这个绘制属性从栈顶取出来(恢复)就好了。需要注意的是保存一次只能取一次,可以保存多次,但是每次只从栈顶取

代码示例
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<code> /**保存绘制属性(以绘制3条线为例,第一条第三条属性一致)**/
//第一条
CGContextMoveToPoint(ctx, 20 , 300 );
CGContextAddLineToPoint(ctx, 200 , 300 );
//设置绘制属性
CGContextSetLineWidth(ctx, 10 );
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetStrokeColorWithColor(ctx, [UIColor orangeColor].CGColor);
CGContextStrokePath(ctx);
 
//第二条线
//先保存当前的绘制属性
CGContextSaveGState(ctx);
CGContextMoveToPoint(ctx, 20 , 400 );
CGContextAddLineToPoint(ctx, 200 , 400 );
//设置新的绘制属性
CGContextSetLineWidth(ctx, 5 );
CGContextSetLineCap(ctx, kCGLineCapButt);
CGContextSetStrokeColorWithColor(ctx, [UIColor blueColor].CGColor);
CGContextStrokePath(ctx);
 
//第三条线
//取出(恢复)之前保存的绘制属性
CGContextRestoreGState(ctx);
CGContextMoveToPoint(ctx, 20 , 500 );
CGContextAddLineToPoint(ctx, 200 , 500 );
CGContextStrokePath(ctx);
</code>
绘制结果

这里写图片描述

矩阵操作

一、矩阵操作介绍

矩阵操作主要有旋转操作、缩放操作、平移操作,是以视图左上角为原点进行的。对矩阵的操作一定要在绘制之前完成,不然绘制完了再操作无效。

二、代码说明
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<code class = "hljs" objectivec= "" >    CGContextRef ctx = UIGraphicsGetCurrentContext();
 
     //矩阵旋转45度(参数为图形上下文、旋转角度)是以左上角为旋转点的
     //设置矩阵操作要在绘制前完成
//    CGContextRotateCTM(ctx, M_PI_4);
 
     //缩放(参数为图形上下文,x方向缩放比例,y方向缩放比例)
//    CGContextScaleCTM(ctx, 0.5, 0.5);
 
     //平移(参数为图形上下文,x方向平移距离,y方向平移距离)
     CGContextTranslateCTM(ctx, 100 , 100 );
 
     CGContextAddRect(ctx, CGRectMake( 100 , 100 , 100 , 100 ));
 
     //标记
     NSString *loc1 = @1 ;
     NSString *loc2 = @2 ;
     [loc1 drawAtPoint:CGPointMake( 99 , 99 ) withAttributes:nil];
     [loc2 drawAtPoint:CGPointMake( 201 , 99 ) withAttributes:nil];
 
     CGContextStrokePath(ctx);</code>

剪切操作

一、剪切介绍

指剪切掉指定区域意外的部分,只保留该区域内的内容。
原则:先设置好剪切区域,或者说剪切方法,再去绘制相关内容。

二、代码相关
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<code class = "hljs" objectivec= "" >    CGContextRef ctx = UIGraphicsGetCurrentContext();
     //剪切自定义区域意外的部分(以剪切方法为三角形,剪切内容为图片为例)
//    //    CGContextAddEllipseInRect(ctx, CGRectMake(100, 100, 50, 50));
//    CGContextMoveToPoint(ctx, 100, 100);
//    CGContextAddLineToPoint(ctx, 60, 150);
//    CGContextAddLineToPoint(ctx, 140, 150);
//    CGContextClosePath(ctx);
 
//    CGContextClip(ctx);
 
     //剪切指定矩形区域意外的部分
     CGContextClipToRect(ctx, CGRectMake( 80 , 100 , 10 , 10 ));
     UIImage *image = [UIImage imageNamed: @google ];
     [image drawAtPoint:CGPointMake( 80 , 100 )];</code>

绘图路径

一、绘图路径介绍

我们之前画一条直线,都是直接设置好它的起点和终点,然后就开始画了。画一个圆,设置好圆心半径起点终点和方向即可。事实上,我们设置好这些绘图信息后,系统会默认创建一条绘图路径,画图就是根据这条路径来画的。一条线对应一条路径,一个圆对应另一条路径。那么我们自然可以通过手动创建路径的方式绘制,需要绘制几个图案,就要创建几条路径。

二、注意点

A、Quartz2D中所有通过creat/copy/retain方法创建出来的值都要释放,以path为例:

CGPathRelease(path);或 CFRelease(path);

B、可以将要绘制的所有路径加入到图形上下文后,最后一次性渲染。

三、代码示例

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<code class = "hljs" php= "" >    //绘制一条直线的两种方法(两种方式是等效的)
     CGContextRef ctx = UIGraphicsGetCurrentContext();
     CGContextMoveToPoint(ctx, 20 , 200 );
     CGContextAddLineToPoint(ctx, 300 , 200 );
     CGContextStrokePath(ctx);
 
     //手动创建路径绘制
     //创建一条路径
     CGMutablePathRef path = CGPathCreateMutable();
     //添加绘图信息到路径
     CGPathMoveToPoint(path, NULL, 20 , 300 );
     CGPathAddLineToPoint(path, NULL, 300 , 300 );
     //将路径添加到图形上下文
     CGContextAddPath(ctx, path);
 
 
     //创建另一条路径
     CGMutablePathRef path2 = CGPathCreateMutable();
     CGPathAddEllipseInRect(path2, NULL, CGRectMake( 100 , 400 , 100 , 100 ));
     CGContextAddPath(ctx, path2);
     CGContextStrokePath(ctx);
 
     //渲染所有路径对应的图案
     CGContextStrokePath(ctx);
 
     //Quartz2D中所有通过creat/copy/retain方法创建出来的值都要释放
     CGPathRelease(path);
     CGPathRelease(path2);
     //或者
//    CFRelease(path);</code>

运行结果

这里写图片描述

Bitmap

一、bitmap图形上下文

Quartz2D提供了以下几种类型的图形上下文

Bitmap Graphics Context PDF Graphics Context Window Graphics Context Layer Graphics Context Printer Graphics Context

常用的是Bitmap Graphics Context。所谓Bitmap,其实就是UIImage,这也是最常用到的图形上下文,通常用它来生成一张图片。步骤如下:

创建一个bitmap图形上下文(有两种方式)
A.UIGraphicsBeginImageContext(<#CGSize size#>);
B.UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale);

这两种方法都可以创建一个bitmap图形上下文,但是第一种创建的图片清晰度和质量没有第二种好。方法二接收的参数依次为:

size:指定创建出来的bitmap尺寸大小 opaque:是否透明,YES表示不透明,NO透明 scale:缩放比例,0为不缩放

获取创建好的bitmap图形上下文然后在上面做文章(画图)

取出绘制好的图片 关闭bitmap图形上下文 释放需要释放的变量

代码示例(截屏)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<code class = "hljs" objectivec= "" > //点击按钮截屏
- ( void )actionShot:(UIButton *)sender{
 
     //可以隐藏按钮,渲染完后显示回来
     self.buttonShot.hidden = YES;
 
     //创建图形上下文
     UIGraphicsBeginImageContextWithOptions(CGSizeMake(self.view.frame.size.width, self.view.frame.size.height), NO, 0 );
     //获取图形上下文并将当前屏幕渲染到图形上下文上
     AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
     [delegate.window .layer renderInContext:UIGraphicsGetCurrentContext()];
     //从图形上下文中取出绘制好的图片
     UIImage *screenImage = UIGraphicsGetImageFromCurrentImageContext();
     //关闭图形上下文
     UIGraphicsEndImageContext();
 
     self.buttonShot.hidden = NO;
 
//    //截屏完毕 有时候可能想获取屏幕中指定区域的图片,如下操作
//    //得到截屏的cgimage
     CGImageRef image = screenImage.CGImage;
 
     //设置目标区域,注意这里需要考虑retina分辨率的放大倍数,以iphone6plus为例,在原尺寸的基础上*3,这里就不判断了。
     CGRect rect = CGRectMake( 0 , 0 , screenImage.size.width* 3 , screenImage.size.height* 3 );
     //取出目标区域的图片
     CGImageRef targetImage = CGImageCreateWithImageInRect(image, rect);
     //最终图片
     UIImage *finalImage = [UIImage imageWithCGImage:targetImage];
 
 
     //保存到相册
     UIImageWriteToSavedPhotosAlbum(finalImage, self, @selector (image: didFinishSavingWithError:contextInfo:), nil);
 
     //保存到沙盒
     NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
     NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
     [dateFormatter setDateFormat: @yyyy -MM-dd HH:mm:ss];
     NSString *currentTime = [dateFormatter stringFromDate:[NSDate date]];
     NSString *imagePath = [path stringByAppendingPathComponent:[NSString stringWithFormat: @ScreenShot_ %@.png,currentTime]];
     NSData *imageDate = UIImagePNGRepresentation(finalImage);
     [imageDate writeToFile:imagePath atomically:YES];
 
     CGImageRelease(targetImage);
}
</code>
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<code class = "hljs" objectivec= "" > //保存至相册后的回调
- ( void )image: (UIImage *) image didFinishSavingWithError: (NSError *) error contextInfo: ( void *) contextInfo
{
     NSString *msg = nil ;
     if (error != NULL){
         msg = @保存图片失败 ;
     } else {
         msg = @保存图片成功 ;
     }
     UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@保存图片结果提示
                                                     message:msg
                                                    delegate:self
                                           cancelButtonTitle:@确定
                                           otherButtonTitles:nil];
     [alert show];
}</code>

Quartz2D内存管理

前面讲绘图路径的时候提到过内存管理,下面再总结一下:

凡是使用含有“create”或“copy”的函数创建的对象,使用完毕后必需释放,否则将导致内存泄露。不含“create”或“copy”的则不需要释放。 如果retain了一个对象,不再使用时需要release。已CGColorSpace为例,如果创建了一个CGColorSpace对象,则使用函数CGColorSpaceRetain和CGColorSpaceRelease来retain和release对象。也可以使用Core Foundation的CFRetain和CFRelease。注意不能传递NULL值给这些函数。
在使用Python来安装geopandas包时,由于geopandas依赖于几个其他的Python库(如GDAL, Fiona, Pyproj, Shapely等),因此安装过程可能需要一些额外的步骤。以下是一个基本的安装指南,适用于大多数用户: 使用pip安装 确保Python和pip已安装: 首先,确保你的计算机上已安装了Python和pip。pip是Python的包管理工具,用于安装和管理Python包。 安装依赖库: 由于geopandas依赖于GDAL, Fiona, Pyproj, Shapely等库,你可能需要先安装这些库。通常,你可以通过pip直接安装这些库,但有时候可能需要从其他源下载预编译的二进制包(wheel文件),特别是GDAL和Fiona,因为它们可能包含一些系统级的依赖。 bash pip install GDAL Fiona Pyproj Shapely 注意:在某些系统上,直接使用pip安装GDAL和Fiona可能会遇到问题,因为它们需要编译一些C/C++代码。如果遇到问题,你可以考虑使用conda(一个Python包、依赖和环境管理器)来安装这些库,或者从Unofficial Windows Binaries for Python Extension Packages这样的网站下载预编译的wheel文件。 安装geopandas: 在安装了所有依赖库之后,你可以使用pip来安装geopandas。 bash pip install geopandas 使用conda安装 如果你正在使用conda作为你的Python包管理器,那么安装geopandas和它的依赖可能会更简单一些。 创建一个新的conda环境(可选,但推荐): bash conda create -n geoenv python=3.x anaconda conda activate geoenv 其中3.x是你希望使用的Python版本。 安装geopandas: 使用conda-forge频道来安装geopandas,因为它提供了许多地理空间相关的包。 bash conda install -c conda-forge geopandas 这条命令会自动安装geopandas及其所有依赖。 注意事项 如果你在安装过程中遇到任何问题,比如编译错误或依赖问题,请检查你的Python版本和pip/conda的版本是否是最新的,或者尝试在不同的环境中安装。 某些库(如GDAL)可能需要额外的系统级依赖,如地理空间库(如PROJ和GEOS)。这些依赖可能需要单独安装,具体取决于你的操作系统。 如果你在Windows上遇到问题,并且pip安装失败,尝试从Unofficial Windows Binaries for Python Extension Packages网站下载相应的wheel文件,并使用pip进行安装。 脚本示例 虽然你的问题主要是关于如何安装geopandas,但如果你想要一个Python脚本来重命名文件夹下的文件,在原始名字前面加上字符串"geopandas",以下是一个简单的示例: python import os # 指定文件夹路径 folder_path = 'path/to/your/folder' # 遍历文件夹中的文件 for filename in os.listdir(folder_path): # 构造原始文件路径 old_file_path = os.path.join(folder_path, filename) # 构造新文件名 new_filename = 'geopandas_' + filename # 构造新文件路径 new_file_path = os.path.join(folder_path, new_filename) # 重命名文件 os.rename(old_file_path, new_file_path) print(f'Renamed "{filename}" to "{new_filename}"') 请确保将'path/to/your/folder'替换为你想要重命名文件的实际文件夹路径。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值