转载请带上出处, 谢谢.
一个 Graphics Context 代表一个绘制目标, 它包含绘制系统用于完成绘制指令的绘制参数和设备相关信息, 一个 Graphics Context 在绘制时定义了一些基本绘制属性如颜色, 裁剪区域, 线条宽度和样式信息, 字体信息, compositing options(合成选项?), 等.
你可以通过使用 Quartz 的context creation 函数或者通过 Mac OS X frameworks 或者iOS中的 UIKit framework 提供的更高级的函数获得. Quartz 为多种 Quartz graphics contexts 提供了创建函数, 其中包括 bitmap 和 PDF, 你可以用这些函数创建自定义的 content.
本章介绍了如何为不同的绘制目标创建不同的Graphics Context. 在代码中, 我们用 CGContextRef 表示一个 Graphics Context , CGContextRef 是一个不透明数据类型, 在你获得了 Graphics Context 后, 你可以使用 Quartz 2D 的函数在 Context 中绘制, 在 context 上执行操作 (例如 translations) , 改变 graphics state 参数, 例如线宽和填充色等.
Drawing to a View Graphics Context in iOS
要在一个 iOS 程序的屏幕上进行绘制, 需要设置一个 UIView 对象 并且实现它的 drawRect(完成绘制的方法). 视图的 drawRect 方法: 当一个视图在屏幕上可见并且它的内容需要被更新时这个方法被调用. 在调用你自定义的 drawRect 方法前: 视图对象自动的配置它的绘制环境以便你的代码能够立即开始绘制. 作为配置的一部分, UIView 对象为当前的绘制环境创建了一个 Graphics Context ( CGContextRef 类型). 你可以在你的drawRect方法中通过使用 UIKit 中的 UIGraphicsGetCurrentContext 函数获得这个 Graphics Context.
UIKit 中的默认坐标系与 Quartz 中的坐标系不同. 在 UIKit 中, 原点在左上角, Y轴正轴朝下. UIView 对象修改 Quartz Graphics Context 的 CTM (当前转换矩阵) (原点移到左下角并且将Y周的值乘以-1(反转Y轴))来匹配 UIKit. 更多关于修改坐标系和坐标系在你绘制代码中的含义的信息, 参见 Quartz 2D Coordinate Systems.
UIView 对象的详情请参见 View Programming Guide for iOS.
Creating a Window Graphics Context in Mac OS X
在 Mac OS X 中进行绘制时, 你需要创建一个 window graphics context. 而 Quartz 2D API 并没有提供获取 windows graphics context 的方法. 取而代之, 你可以使用 Cocoa framework 来创建 windows graphics context.
你可以使用以下代码在 drawRect 中获取一个 Quartz graphics context :
CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];
currentContext方法返回当前线程中的 NSGraphicsContext 实例. graphicsPort 方法返回低级别平台相关的 graphics context (Quartz Graphics Context). (不要被这些方法名所迷惑; 它们已经很老了). 更多信息参见 NSGraphicsContext Class Reference.
在你获得 graphics context之后, 你可以在你的 Cocoa 应用中调用任何 Quartz 2D 的绘制函数. 你也可以将Quartz 2D与Cocoa绘制操作混合使用, 如 图2-1 是一个在Cocoa视图中用Quartz 2D绘制的实例。绘图由两个长方形组成(一个不透明的红色长方形和半透明的蓝色长方形). 你可以在Color and Color Spaces中学到更多关于可视度的东西, 这种能够控制你能够透视多少颜色的能力是 Quartz 2D 的一个标志性特征.
图 2-1 一个包含Quartz 2D绘制的Cocoa视图
要实现** 图 2-1 **的绘制, 你需要先创建一个 Cocoa 应用程序 Xcode 项目. 在 Interface Builder 中, 拖一个自定义View到窗口中并子类化. 然后如 表 2-1 所示实现这个视图. 在这个例子中, 子类视图叫做 MyQuartzView. 这个视图的 drawRect 包含了所有的 Quartz 绘制代码. 每一行代码的解释也在下表中.
Note: NSView的drawRect:方法在每次视图需要绘制时自动调用. 更多重写drawRect方法的信息参见 NSView Class Reference.
表 2-1 在 window graphics context 中绘制
@implementation MyQuartzView
- (id)initWithFrame:(NSRect)frameRect {
self = [super initWithFrame:frameRect];
return self;
}
- (void)drawRect:(NSRect)rect {
CGContextRef myContext = [[NSGraphicsContext // 1
currentContext] graphicsPort];
// ********** Your drawing code here ********** // 2
CGContextSetRGBFillColor (myContext, 1, 0, 0, 1); // 3
CGContextFillRect (myContext, CGRectMake (0, 0, 200, 100 )); // 4
CGContextSetRGBFillColor (myContext, 0, 0, 1, .5); // 5
CGContextFillRect (myContext, CGRectMake (0, 0, 100, 200)); // 6
}
@end
代码说明:
- 为视图获取一个 Graphics Context.
- 这是插入绘图代码的地方。以下四行是使用 Quartz 2D 函数的例子.
- 设置完全不透明的红色填充. 更多关于 colors 和 alpha (设置透明度) 的信息, 参见 Color and Color Spaces.
- 填充一个原点在 (0,0) 宽200高100的长方形. 更多关于绘制矩形的信息, 参见 Paths.
- 设置半透明的蓝色填充.
- 填充一个原点在 (0,0) 款100高200的长方形.
Creating a PDF Graphics Context
当你创建一个PDF Graphics Context并绘制时, Quartz将绘制操作记录为一系列的PDF绘制命令并写入文件中. 你需要提供一个PDF的输出位置和一个默认的Media Box—用于指定Page边界的一个长方形. 图 2-2 展示了在PDF Graphics Context中绘制及在preview打开PDF的结果.
图 2-2 通过 CGPDFContextCreateWithURL 创建一个PDF文件
Quartz 2D API 提供了2种创建 PDF graphics context的方式:
• CGPDFContextCreateWithURL, 当你需要用Core Foundation URL指定pdf输出的位置时使用该函数. ** 表 2-2 **展示了怎么使用这个函数来创建 PDF graphics context.
• CGPDFContextCreate, 当需要将PDF输出给数据用户时使用该方法. (更多信息参见 Data Management in Quartz 2D.) ** 表 2-3 **展示了怎么使用这个函数来创建 PDF graphics context.
每一行代码的解释也在每个表的下方.
iOS Note: 在iOS中 PDF graphics context 使用的是 Quartz 提供的默认坐标系, 而不用应用转换来匹配 UIKit 坐标系. 如果你的应用程序希望在你的 PDF graphics context 和通过 UIView 对象 提供的 graphics context 之间共享绘制代码, 你的应用程序应该修改 PDF graphics context 的 CTM 来修改坐标系. 参见 Quartz 2D Coordinate Systems.
表 2-2 调用 CGPDFContextCreateWithURL 方法创建 PDF graphics context
CGContextRef MyPDFContextCreate (const CGRect *inMediaBox,
CFStringRef path) {
CGContextRef myOutContext = NULL;
CFURLRef url;
url = CFURLCreateWithFileSystemPath (NULL, // 1
path,
kCFURLPOSIXPathStyle,
false);
if (url != NULL) {
myOutContext = CGPDFContextCreateWithURL (url, // 2
inMediaBox,
NULL);
CFRelease(url); // 3
}
return myOutContext; // 4
}
代码说明:
- 调用 Core Foundation 的方法通过 MyPDFContextCreate 方法提供的CFString对象来创建一个 CFURL 对象. 你传递NULL作为第一个参数以使用 default allocator. 你也需要指定一个路径风格, 这个例子使用的是POSIX-style路径名.
- 使用刚才创建的PDF的位置 ( CFURL 对象) 和一个指定了 PDF 边界的矩形, 调用 Quartz 2D 函数创建一个 PDF graphics context . 这个矩形(CGRect) 是通过 MyPDFContextCreate 函数传递进来并且它是 PDF 的默认边界.
- 释放 CFURL 对象
- 返回 PDF graphics context. 调用方必须在不再需要的时候释放 Graphics Context.
表 2-3 调用 CGPDFContextCreate 来创建 PDF graphics context
CGContextRef MyPDFContextCreate (const CGRect *inMediaBox,
CFStringRef path) {
CGContextRef myOutContext = NULL;
CFURLRef url;
CGDataConsumerRef dataConsumer;
url = CFURLCreateWithFileSystemPath (NULL, // 1
path,
kCFURLPOSIXPathStyle,
false);
if (url != NULL) {
dataConsumer = CGDataConsumerCreateWithURL (url); // 2
if (dataConsumer != NULL) {
myOutContext = CGPDFContextCreate (dataConsumer, // 3
inMediaBox,
NULL);
CGDataConsumerRelease (dataConsumer); // 4
}
CFRelease(url); // 5
}
return myOutContext; // 6
}
代码说明:
- 调用 Core Foundation 的方法通过 MyPDFContextCreate 方法提供的 CFString 对象创建 CFURL 对象. 传递NULL作为第一个参数以使用 default allocator. 你也需要指定一个路径风格, 这个例子使用的是POSIX-style路径名.
- 使用 CFURL 对象创建一个 Quartz data consumer 对象. 如果你不想使用 CFURL 对象 (比如, 你想把 PDF 数据放到一个地址这个地址就不能用CFURL对象指定), 您可以从您在应用程序中实现的回调函数的集合来创建一个 data consumer, 更多信息, 参见 Data Management in Quartz 2D.
- 使用 MyPDFContextCreate方法传入的指定了 PDF 边界的矩形 (CGRect 类型)和 data consumer 创建 PDF graphics context.
- 释放 data consumer.
- 释放 CFURL 对象.
- 返回 PDF graphics context. 调用方必须在不再需要的时候释放 Graphics Context.
**表 2-4 **展示了怎么调用 MyPDFContextCreate 程序并且在上面绘制. 每一行代码的解释在这个表的下方.
Listing 2-4 在PDF graphics context上进行绘制
CGRect mediaBox; // 1
mediaBox = CGRectMake (0, 0, myPageWidth, myPageHeight); // 2
myPDFContext = MyPDFContextCreate (&mediaBox, CFSTR("test.pdf")); // 3
CFStringRef myKeys[1]; // 4
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); // 5
// ********** Your drawing code here ********** // 6
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); // 7
CFRelease(pageDictionary); // 8
CFRelease(myValues[0]);
CGContextRelease(myPDFContext);
代码解释:
- 为你用来定义PDF 边界的矩形声明一个变量.
- 设置边界盒子的原点为 (0,0) 和宽高, 宽高变量通过程序提供.
- 提供一个PDF 边界的矩形和路径名来调用 表 2-3 的MyPDFContextCreate方法获得 PDF graphics context. CFSTR 宏将一个字符串转换成 CFStringRef 数据类型.
- 用 page options 设置一个字典. 在这个例子中, 只有边界矩形是指定的. 你不需要把你设置 PDF graphics context 用的矩形直接传过来. 把矩形加在这里将会替代设置 PDF graphics context 用的矩形.
- 设置开始的 page. 这个方法用于PDF绘制的page.
- 调用 Quartz 2D 绘制方法. 你可以用你的应用程序的绘图代码替换以下四行代码.
- 标注结束 PDF.
- 在字典和 PDF graphics context 不需要的时候释放他们.
你可以在PDF中写入各种信息例如—图片, 文字, 绘制路径—并且你可以添加链接和加密. 更多详情请参见 PDF Document Creation, Viewing, and Transforming.
Creating a Bitmap Graphics Context
A bitmap graphics context 接受一个指向内存缓冲区的指针(包含位图存储空间). 当我们绘制 bitmap graphics context 时, 缓存被更新. 在你释放这个 graphics context 之后, 你将得到一个我们指定像素格式的全新的位图.
Note: Bitmap graphics contexts 有时候用于离屏绘制, 在你大选在这种时候使用 bitmap graphics context 之前, 请看 Core Graphics Layer Drawing. CGLayer 对象 (CGLayerRef) 是离屏绘制最好的, 因为, 在任何可能的时候, Quartz 会在显卡上缓存层.
iOS Note: iOS 程序应该使用 UIGraphicsBeginImageContextWithOptions方法取代 Quartz 的底层方法. 如果你的应用使用 Quartz 创建了一个离屏位图, bitmap graphics context 使用的将会是 Quartz 的默认坐标系, 相反, 如果你的应用通过调用 UIGraphicsBeginImageContextWithOptions 创建图片, UIKit将会对坐标系统使用与UIView对象的图形上下文一样的转换. 这允许应用程序使用相同的绘制代码而不需要担心坐标系统问题. 虽然我们的应用程序可以手动调整CTM达到相同的效果, 但这种做没有任何好处.
你使用 CGBitmapContextCreate 创建一个 bitmap graphics context. 这个函数有以下参数:
• data. 一个指向内存目标的指针,该内存用于存储需要渲染的图形数据。内存块的大小至少需要(bytePerRow * height)字节。
• width. 位图的宽度, 单位像素.
• height. 位图的高度, 单位像素.
•bitsPerComponent. 指定内存中一个像素的每个组件使用的位数。例如,一个32位的像素格式和一个rgb颜色空间,我们可以指定每个组件为8位. 参见 Supported Pixel Formats.
• bytesPerRow. 指定位图每行的字节数.
Tip: 当你创建一个 bitmap graphics context 时, 如果你保证 data 和 bytesPerRow 是 16-byte 对齐.你想会得到最好的性能
• colorspace. 颜色空间用于bitmap graphics context。在创建bitmap Graphics Context时,我们可以使用灰度(gray), RGB, CMYK, NULL颜色空间. 更多关于颜色空间和颜色管理原则的信息, 参见 Color Management Overview. 更多在 Quartz 中创建和使用颜色空间的信息, 参见 Color and Color Spaces. 更多关颜色空间的信帮助息, 参见 Bitmap Images and Image Masks 章节中的 Color Spaces and Bitmap Layout.
• bitmapInfo. 位图的信息,这些信息用于指定位图是否需要包含alpha组件,像素中alpha组件的相对位置(如果有的话),alpha组件是否是预乘的,及颜色组件是整型值还是浮点值. 有关这些常量是什么, 在什么时候用和 Quartz 支持的像素格式(bitmap graphics contexts 和图像)的详细信息, 参见 Bitmap Images and Image Masks 章节中的 Color Spaces and Bitmap Layout.
表 2-5 展示了怎么创建一个 bitmap graphics context. 当你在返回的 bitmap graphics context 上绘制时, Quartz 将绘图记录到内存中指定的块中. 代码解释在表下.
Listing 2-5 创建一个bitmap graphics context
CGContextRef MyCreateBitmapContext (int pixelsWide,
int pixelsHigh) {
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
void * bitmapData;
int bitmapByteCount;
int bitmapBytesPerRow;
bitmapBytesPerRow = (pixelsWide * 4); // 1
bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); // 2
bitmapData = calloc( bitmapByteCount ); // 3
if (bitmapData == NULL) {
fprintf (stderr, "Memory not allocated!");
return NULL;
}
context = CGBitmapContextCreate (bitmapData, // 4
pixelsWide,
pixelsHigh,
8, // bits per component
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedLast);
if (context== NULL) {
free (bitmapData); // 5
fprintf (stderr, "Context not created!");
return NULL;
}
CGColorSpaceRelease( colorSpace ); // 6
return context; // 7
}
代码解释:
- 声明一个代表每行字节数的变量. 在这个例子中的位图中的每个像素表示为4个字节; 8 位分别是 red, green, blue, 和 alpha.
- 创建一个通用的 RGB 颜色空间. 你也可以创建一个 CMYK 颜色空间, 更多信息和对于泛型颜色空间与设备依赖性的讨论, 参见 Color and Color Spaces.
- 调用 calloc 方法为储存bitmap data清理并分配一块内存空间. 这个例子创建了一个32-bit RGBA bitmap (也就是说, 一个每像素32位的数组, 每个像素包含八位, 分别是 red, green, blue, 和 alpha 信息). 位图中的每一个像素占用4个字节的内存. 在 Mac OS X 10.6 和 iOS 4 中, 这一步可以省略—如果你传递 NULL 作为 bitmap data, Quartz 自动为位图分配空间.
- 创建一个 bitmap graphics context, 提供位图数据, 位图的宽度和高度, 每一个组件的比特数, 每行的字节数, 颜色空间, 和一个指定位图是否应该包含alpha通道和它在一个像素的相对位置的常数. kCGImageAlphaPremultipliedLast 表名alpha储存在一个像素的最后一本分并且颜色组件已被这个阿尔法值相乘. 参见 The Alpha Value.
- 如果因为一些原因内容没有被创建, 释放用于bitmap data的内存分配.
- 释放 color space.
- 返回 bitmap graphics context. 调用方必须在不需要的时候释放graphics context.
表 2-6 展示了调用 MyCreateBitmapContext 来创建 bitmap graphics context , 使用 bitmap graphics context 来创建一个 CGImage 对象, 然后在 window graphics context 上绘制这个图的代码. ** 图 2-3 **展示了Window上画的图. 代码解释在表后.
Listing 2-6 Drawing to a bitmap graphics context
CGRect myBoundingBox; // 1
myBoundingBox = CGRectMake (0, 0, myWidth, myHeight); // 2
myBitmapContext = MyCreateBitmapContext (400, 300); // 3
// ********** Your drawing code here ********** // 4
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); // 5
CGContextDrawImage(myContext, myBoundingBox, myImage); // 6
char *bitmapData = CGBitmapContextGetData(myBitmapContext); // 7
CGContextRelease (myBitmapContext); // 8
if (bitmapData) free(bitmapData); // 9
CGImageRelease(myImage); // 10
代码解释:
- 声明一个变量储存边框盒子的原点和尺寸, Quartz 将通过 bitmap graphics context 绘制一个图像.
- 设置边框盒子的原点为 (0,0) 并且设置先前声明的变量的宽度和高度, 但其声明没有在本代码中显示.
- 调用程序提供的 MyCreateBitmapContext 方法(参见 表 2-5) 去创建一个400像素宽300像素高的位图内容. 你可以使用任何使用的程序的尺寸来创建 bitmap graphics context.
- 调用 Quartz 2D 方法在 bitmap graphics context上绘制. 你可以使用你应用程序上的绘制代码来替换下面四行.
- 通过 bitmap graphics context 创建一个 Quartz 2D 图像 (CGImageRef).
- 通过边框盒子提供的位置把这个图像绘制到 window graphics context 上. 边框盒子指定在用户空间中绘制图像的位置和尺寸. 这个例子没有展示 window graphics context 的创建. 关于创建的信息参见 Creating a Window Graphics Context in Mac OS X .
- 获取与 bitmap graphics context 有关联的位图数据信息.
- 释放 bitmap graphics context, 在他不需要再试用的时候.
- 释放 bitmap data , 如果它存在.
- 在 image 不需要在使用的时候释放.
图 2-3 An image created from a bitmap graphics context and drawn to a window graphics context
Supported Pixel Formats
表 2-1 总结了位图Graphics Context支持的像素格式,相关的颜色空间及像素格式支持的Mac OS X最早版本。像素格式用bpp(每像素的位数)和bpc(每个组件的位数)来表示。表格同时也包含与像素格式相关的位图信息常量. 更多关于位图信息格式常量表示什么的详细信息参见CGImage Reference.
Table 2-1 Pixel formats supported for bitmap graphics contexts
CS | Pixel format and bitmap information constant | Availability | |
---|---|---|---|
Null | 8 bpp, 8 bpc, kCGImageAlphaOnly | Mac OS X, iOS | |
Gray | 8 bpp, 8 bpc,kCGImageAlphaNone | Mac OS X, iOS | |
Gray | 8 bpp, 8 bpc,kCGImageAlphaOnly | Mac OS X, iOS | |
Gray | 16 bpp, 16 bpc, kCGImageAlphaNone | Mac OS X | |
Gray | 32 bpp, 32 bpc, kCGImageAlphaNone | kCGBitmapFloatComponents | Mac OS X |
RGB | 16 bpp, 5 bpc, kCGImageAlphaNoneSkipFirst | Mac OS X, iOS | |
RGB | 32 bpp, 8 bpc, kCGImageAlphaNoneSkipFirst | Mac OS X, iOS | |
RGB | 32 bpp, 8 bpc, kCGImageAlphaNoneSkipLast | Mac OS X, iOS | |
RGB | 32 bpp, 8 bpc, kCGImageAlphaPremultipliedFirst | Mac OS X, iOS | |
RGB | 32 bpp, 8 bpc, kCGImageAlphaPremultipliedLast | Mac OS X, iOS | |
RGB | 64 bpp, 16 bpc, kCGImageAlphaPremultipliedLast | Mac OS X | |
RGB | 64 bpp, 16 bpc, kCGImageAlphaNoneSkipLast | Mac OS X | |
RGB | 128 bpp, 32 bpc, kCGImageAlphaNoneSkipLast |kCGBitmapFloatComponents | Mac OS X | |
RGB | 128 bpp, 32 bpc, kCGImageAlphaPremultipliedLast | kCGBitmapFloatComponents | Mac OS X |
CMYK | 32 bpp, 8 bpc, kCGImageAlphaNone | Mac OS X | |
CMYK | 64 bpp, 16 bpc, kCGImageAlphaNone | Mac OS X | |
CMYK | 128 bpp, 32 bpc, kCGImageAlphaNone | kCGBitmapFloatComponents | Mac OS X |
Anti-Aliasing
Bitmap graphics contexts 支持抗锯齿, 这是人工修改锯齿 (或者别的名字) 边缘的过程, 有时当文字或形状被绘制时你会在位图图像上看到. 当位图的分辨率显着低于你的眼睛的分辨率这种锯齿边的情况会发生. 为了使对象在位图中显示光滑, Quartz 使用不同的颜色来填充形状周边的像素. 通过这种方式来混合颜色, 使形状看起来更平滑. 你可以在 图 2-4 中看到抗锯齿效果. 你可以通过调用CGContextSetShouldAntialias来关闭位图Graphics Context的抗锯齿效果。抗锯齿设置是图形状态的一部分.
可以调用函数CGContextSetAllowsAntialiasing来控制一个特定Graphics Context是否支持反锯齿; false表示不支持。该设置不是图形状态的一部分. 当上下文及图形状态设置为true时, Quartz执行反锯齿.
Figure 2-4 A comparison of aliased and anti-aliasing drawing
Obtaining a Graphics Context for Printing
Mac OS X中的Cocoa应用程序通过自定义的NSView子类来实现打印. 一个视图通过调用print:方法来进行打印。然后视图以打印机为目标创建一个Graphics Context,并调用drawRect:方法. 你的应用程序使用与屏幕相同的绘制代码绘制打印机器. 我们同样可以自定义drawRect:将图形绘制到打印机与绘制到屏幕不同.(实际上我并没有看懂)
在Cocoa下的详细的印刷讨论, 参见 Printing Programming Guide for Mac.