Quartz 2D 编程指南二:图形上下文

logo.PNG

Graphics Contexts

A graphics context represents a drawing destination. It contains drawing parameters and all device-specific information that the drawing system needs to perform any subsequent drawing commands. A graphics context defines basic drawing attributes such as the colors to use when drawing, the clipping area, line width and style information, font information, compositing options, and several others.

一个Graphics Context表示一个绘制目标。它包含绘制系统用于完成绘制指令的绘制参数和设备相关信息。Graphics Context定义了基本的绘制属性,如颜色、裁减区域、线条宽度和样式信息、字体信息、混合模式等

You can obtain a graphics context by using Quartz context creation functions or by using higher-level functions provided by one of the Mac OS X frameworks or the UIKit framework in iOS. Quartz provides functions for various flavors of Quartz graphics contexts including bitmap and PDF, which you can use to create custom content.

我们可以通过几种方式来获取Graphics Context:Quartz提供的创建函数、Mac OS X框架或IOS的UIKit框架提供的函数。Quartz提供了多种Graphics Context的创建函数,包括bitmap和PDF,我们可以使用这些Graphics Context创建自定义的内容。

This chapter shows you how to create a graphics context for a variety of drawing destinations. A graphics context is represented in your code by the data type CGContextRef, which is an opaque data type. After you obtain a graphics context, you can use Quartz 2D functions to draw to the context, perform operations (such as translations) on the context, and change graphics state parameters, such as line width and fill color.

本章介绍了如何为不同的绘制目标创建Graphics Context。在代码中,我们用CGContextRef来表示一个Graphics Context。当获得一个Graphics Context后,可以使用Quartz 2D函数在上下文(context)中进行绘制、完成操作(如平移)、修改图形状态参数(如线宽和填充颜色)等

Drawing to a View Graphics Context in iOS

To draw to the screen in an iOS application, you set up a UIView object and implement its drawRect: method to perform drawing. The view’s drawRect: method is called when the view is visible onscreen and its contents need updating. Before calling your custom drawRect: method, the view object automatically configures its drawing environment so that your code can start drawing immediately. As part of this configuration, the UIView object creates a graphics context (a CGContextRef opaque type) for the current drawing environment. You obtain this graphics context in your drawRect: method by calling the UIKit function UIGraphicsGetCurrentContext.

在iOS应用程序中,如果要在屏幕上进行绘制,需要创建一个UIView对象,并实现它的drawRect:方法。视图的drawRect:方法在视图显示在屏幕上及它的内容需要更新时被调用。在调用自定义的drawRect:后,视图对象自动配置绘图环境以便代码能立即执行绘图操作。作为配置的一部分,视图对象将为当前的绘图环境创建一个Graphics Context。我们可以通过调用UIGraphicsGetCurrentContext函数来获取这个Graphics Context。

The default coordinate system used throughout UIKit is different from the coordinate system used by Quartz. In UIKit, the origin is in the upper-left corner, with the positive-y value pointing downward. The UIView object modifies the CTM of the Quartz graphics context to match the UIKit conventions by translating the origin to the upper left corner of the view and inverting the y-axis by multiplying it by -1. For more information on modified-coordinate systems and the implications in your own drawing code, see Quartz 2D Coordinate Systems.
UIView objects are described in detail in View Programming Guide for iOS.

UIKit默认的坐标系统与Quartz不同。在UIKit中,原点位于左上角,y轴正方向为向下。UIView通过将修改Quartz的Graphics Context的CTM[原点平移到左下角,同时将y轴反转(y值乘以-1)]以使其与UIView匹配。

Creating a Window Graphics Context in Mac OS X

When drawing in Mac OS X, you need to create a window graphics context that’s appropriate for the framework you are using. The Quartz 2D API itself provides no functions to obtain a windows graphics context. Instead, you use the Cocoa framework to obtain a context for a window created in Cocoa.

在Mac OS X中绘制时,我们需要创建一个窗口Graphics Context。Quartz 2D API 没有提供函数来获取窗口Graphics Context。取而代之的是用Cocoa框架来获取一个窗口上下文。

You obtain a Quartz graphics context from within the drawRect: routine of a Cocoa application using the following line of code:** CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];**

我们可以在Cocoa应用程序的drawRect:中获取一个Quartz Graphics Context,如下代码所示:

CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];

The method currentContext returns the NSGraphicsContext instance of the current thread. The method graphicsPort returns the low-level, platform-specific graphics context represented by the receiver, which is a Quartz graphics context. (Don’t get confused by the method names; they are historical.) For more information see NSGraphicsContext Class Reference.

currentContext方法在当前线程中返回NSGraphicsContext实例。graphicsPort方法返回一个低级别、平台相关的Graphics Context(Quartz Graphics Context)。

After you obtain the graphics context, you can call any of the Quartz 2D drawing functions in your Cocoa application. You can also mix Quartz 2D calls with Cocoa drawing calls. You can see an example of Quartz 2D drawing to a Cocoa view by looking atFigure 2-1. The drawing consists of two overlapping rectangles, an opaque red one and a partially transparent blue one. You’ll learn more about transparency in Color and Color Spaces. The ability to control how much you can “see through” colors is one of the hallmark features of Quartz 2D.

在获取到Graphics Context后,我们可以在Cocoa应用程序中调用任何Quartz 2D的绘制函数。我们同样可以将Quartz 2D与Cocoa绘制操作混合使用。如图2-1是一个在Cocoa视图中用Quartz 2D绘制的实例。绘图由两个长方形组成(一个不透明的红色长方形和半透明的蓝色长方形)。

[图片上传失败...(image-688a62-1534605915675)]

To create the drawing in Figure 2-1, you first create a Cocoa application Xcode project. In Interface Builder, drag a Custom View to the window and subclass it. Then write an implementation for the subclassed view, similar to what Listing 2-1 shows. For this example, the subclassed view is named MyQuartzView. The drawRect: method for the view contains all the Quartz drawing code. A detailed explanation for each numbered line of code appears following the listing.

为了实现图2-1实例,需要先创建一个Cocoa应用程序。在Interface Builder中,拖动一个Custom View到窗口中,并子类化。然后实现子类视图的,如代码清单2-1所示。视图的drawRect:包含了所有的Quartz绘制代码。

Note: The drawRect: method of the NSView class is invoked automatically each time the view needs to be drawn. To find out more about overriding the drawRect: method, see NSView Class Reference.

注:NSView的drawRect:方法在每次视图需要绘制时自动调用。

Listing 2-1 Drawing to a window graphics context

@implementation MyQuartzView
 
- (id)initWithFrame:(NSRect)frameRect
{
    self = [super initWithFrame:frameRect];
    return self;
}
 
- (void)drawRect:(NSRect)rect
{
    CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort]; //1
   // ********** 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

Here’s what the code does:

  • Obtains a graphics context for the view.
  • This is where you insert your drawing code. The four lines of code that follow are examples of using Quartz 2D functions.
  • Sets a red fill color that’s fully opaque. For information on colors and alpha (which sets opacity), see Color and Color Spaces.
  • Fills a rectangle whose origin is (0,0) and whose width is 200 and height is 100. For information on drawing rectangles, see Paths.
  • Sets a blue fill color that’s partially transparent.
  • Fills a rectangle whose origin is (0,0) and whose width is 100 and height is 200.

代码说明:
1.为视图获取一个Graphics Context
2.插入绘图代码的地方。以下四行是使用Quartz 2D函数的例子
3.设置完全不透明的红色填充色。
4.填充一个长方形,其原点为(0, 0), 大小为(200, 100)
5.设置半透明的蓝色填充色。
6.填充一个长方形,其原点为(0, 0), 大小为(100, 200)

Creating a PDF Graphics Context

When you create a PDF graphics context and draw to that context, Quartz records your drawing as a series of PDF drawing commands written to a file. You supply a location for the PDF output and a default media box—a rectangle that specifies bounds of the page. Figure 2-2 shows the result of drawing to a PDF graphics context and then opening the resulting PDF in Preview.

当创建一个PDF Graphics Context并绘制时,Quartz将绘制操作记录为一系列的PDF绘制命令并写入文件中。我们需要提供一个PDF输出的位置及一个默认的media box(用于指定页面边界的长方形)。图2-2显示了在PDF Graphics Context中绘制及在preview打开PDF的结果。

[图片上传失败...(image-ab7093-1534605915675)]

The Quartz 2D API provides two functions that create a PDF graphics context:

  • CGPDFContextCreateWithURL, which you use when you want to specify the location for the PDF output as a Core Foundation URL. Listing 2-2 shows how to use this function to create a PDF graphics context.
  • CGPDFContextCreate, which you use when you want the PDF output sent to a data consumer. (For more information see Data Management in Quartz 2D.) Listing 2-3 shows how to use this function to create a PDF graphics context.

A detailed explanation for each numbered line of code follows each listing.

Quartz 2D API提供了两个函数来创建PDF Graphics Context:

  • CGPDFContextCreateWithURL:当你需要用Core Foundation URL指定pdf输出的位置时使用该函数。代码清单2-2。
  • CGPDFContextCreate:当需要将pdf输出发送给数据用户时使用该方法。代码清单2-3显示了该函数的使用方法。

iOS Note: A PDF graphics context in iOS uses the default coordinate system provided by Quartz, without applying a transform to match the UIKit coordinate system. If your application plans on sharing drawing code between your PDF graphics context and the graphics context provided by UIView object, your application should modify the CTM of the PDF graphics context to modify the coordinate system. See Quartz 2D Coordinate Systems.

iOS中的PDF图形上下文使用Quartz提供的默认坐标系,而不应用变换来匹配UIKit坐标系。 如果您的应用程序计划在PDF图形上下文和UIView对象提供的图形上下文之间共享绘图代码,则应用程序应修改PDF图形上下文的CTM以修改坐标系。 参见Quartz 2D坐标系。

Listing 2-2 Calling CGPDFContextCreateWithURL to create a 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
}

Here’s what the code does:

  • Calls the Core Foundation function to create a CFURL object from the CFString object supplied to the MyPDFContextCreate function. You pass NULL as the first parameter to use the default allocator. You also need to specify a path style, which for this example is a POSIX-style pathname.
  • Calls the Quartz 2D function to create a PDF graphics context using the PDF location just created (as a CFURL object) and a rectangle that specifies the bounds of the PDF. The rectangle (CGRect) was passed to the MyPDFContextCreate function and is the default page media bounding box for the PDF.
  • Releases the CFURL object.
  • Returns the PDF graphics context. The caller must release the graphics context when it is no longer needed

代码说明:

  • 在MyPDFContextCreate函数中调用Core Foundation提供的CFString对象创建一个CFURL对象。 您将NULL作为第一个参数使用默认分配器。 您还需要指定路径样式,此示例为POSIX样式的路径名。
  • 以刚刚创建的PDF位置(CFURL对象)和创建MyPDFContextCreate 函数时指定的PDF内容为Quartz 2D 函数的参数来创建PDF上下文。
  • 释放CFURL对象。
  • 返回PDF图形上下文。当不再需要时,调用者必须释放图形上下文。

Listing 2-3 Calling CGPDFContextCreate to create a 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
}

Here’s what the code does:

  • Calls the Core Foundation function to create a CFURL object from the CFString object supplied to the MyPDFContextCreate function. You pass NULL as the first parameter to use the default allocator. You also need to specify a path style, which for this example is a POSIX-style pathname.
  • Creates a Quartz data consumer object using the CFURL object. If you don’t want to use a CFURL object (for example, you want to place the PDF data in a location that can’t be specified by a CFURL object), you can instead create a data consumer from a set of callback functions that you implement in your application. For more information, see Data Management in Quartz 2D.
  • Calls the Quartz 2D function to create a PDF graphics context passing as parameters the data consumer and the rectangle (of type CGRect) that was passed to the MyPDFContextCreate function. This rectangle is the default page media bounding box for the PDF.
  • Releases the data consumer.
  • Releases the CFURL object.
  • Returns the PDF graphics context. The caller must release the graphics context when it is no longer needed.

代码说明:

  • 在MyPDFContextCreate函数中调用Core Foundation提供的CFString对象创建一个CFURL对象。 您将NULL作为第一个参数使用默认分配器。 您还需要指定路径样式,此示例为POSIX样式的路径名。
  • 使用CFURL对象创建Quartz data cunsumer。 如果您不想使用CFURL对象(例如,要将PDF数据放置在CFURL对象无法指定的位置),那么在程序中可以从一组回调函数中创建data cunsumer。 了解更多信息,请参阅Quartz 2D中的数据管理。
  • 以data consumer和创建MyPDFContextCreate 函数时指定的PDF内容矩形为Quartz 2D 函数参数来创建PDF上下文。
  • 释放DataConsumer对象。
  • 释放CFURL对象。
  • 返回PDF图形上下文。当不再需要时,调用者必须释放图形上下文。

Listing 2-4 shows how to call the MyPDFContextCreate
routine and draw to it. A detailed explanation for each numbered line of code appears following the listing.

Listing 2-4 Drawing to a 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);

Here’s what the code does:

  • Declares a variable for the rectangle that you use to define the PDF media box.
  • Sets the origin of the media box to (0,0) and the width and height to variables supplied by the application.
  • Calls the function MyPDFContextCreate (See Listing 2-3) to obtain a PDF graphics context, supplying a media box and a pathname. The macro CFSTR converts a string to a CFStringRef data type.
  • Sets up a dictionary with the page options. In this example, only the media box is specified. You don’t have to pass the same rectangle you used to set up the PDF graphics context. The media box you add here supersedes the rectangle you pass to set up the PDF graphics context.
  • Signals the start of a page. This function is used for page-oriented graphics, which is what PDF drawing is.
  • Calls Quartz 2D drawing functions. You replace this and the following four lines of code with the drawing code appropriate for your application.
  • Signals the end of the PDF page.
  • Releases the dictionary and the PDF graphics context when they are no longer needed.

You can write any content to a PDF that’s appropriate for your application—images, text, path drawing—and you can add links and encryption. For more information see PDF Document Creation, Viewing, and Transforming.

代码说明:

  • 声明用于定义PDF媒体框的矩形的变量mediaBox。
  • 设置mediaBox的原点为(0,0),根据应用程序设置mediaBox的宽和高。
  • 根据mediaBox和宏CFSTR定义的路径,调用MyPDFContextCreate方法创建PDF 上下文。
  • 设置PageOptions。在该例中,仅仅指定了mediaBox。不必重新设置和PDF上下文一样的矩形,若设置,PDF上下文的mediaBox将被取代。
  • 调用Quartz 2D绘制函数。以下四行绘制代码可替换以适用于你的程序。
  • PDF页面结束标志。
  • 释放dictionary和PDF上下文
    您可以将任何内容写入适合您的应用程序的PDF - 图像,文本,路径绘图 - 并且您可以添加链接和加密。 有关更多信息,请参阅PDF文档创建,查看和转换。

Creating a Bitmap Graphics Context

A bitmap graphics context accepts a pointer to a memory buffer that contains storage space for the bitmap. When you paint into the bitmap graphics context, the buffer is updated. After you release the graphics context, you have a fully updated bitmap in the pixel format you specify.

位图图形上下文接受指向包含位图存储空间的内存缓冲区的指针。 当您绘制到位图图形上下文中时,缓冲区将被更新。 释放图形上下文后,您将以指定的像素格式完全更新位图。

Note: Bitmap graphics contexts are sometimes used for drawing offscreen. Before you decide to use a bitmap graphics context for this purpose, see Core Graphics Layer Drawing. CGLayer objects (CGLayerRef) are optimized for offscreen drawing because, whenever possible, Quartz caches layers on the video card.

注意:位图图形上下文有时用于离屏渲染。 在决定为此目的使用位图图形上下文之前,请参阅Core Graphics Layer Drawing。 CGLayer对象(CGLayerRef)针对屏幕外绘图进行了优化,因为,Quartz会尽可能地将layers缓存到显卡上。

iOS Note: iOS applications should use the function UIGraphicsBeginImageContextWithOptions instead of using the low-level Quartz functions described here. If your application creates an offscreen bitmap using Quartz, the coordinate system used by bitmap graphics context is the default Quartz coordinate system. In contrast, if your application creates an image context by calling the function UIGraphicsBeginImageContextWithOptions, UIKit applies the same transformation to the context’s coordinate system as it does to a UIView object’s graphics context. This allows your application to use the same drawing code for either without having to worry about different coordinate systems. Although your application can manually adjust the coordinate transformation matrix to achieve the correct results, in practice, there is no performance benefit to doing so.

iOS注意:iOS应用程序应使用UIGraphicsBeginImageContextWithOptions函数,而不是使用此处描述的低级Quartz函数。 如果您的应用程序使用Quartz创建了屏幕外位图,则位图图形上下文使用的坐标系是默认的Quartz坐标系。 相反,如果您的应用程序通过调用函数UIGraphicsBeginImageContextWithOptions创建图像上下文,则UIKit将与上下文的坐标系统相同的转换与UIView对象的图形上下文相同。 这允许您的应用程序使用相同的绘图代码,无需担心不同的坐标系。 虽然您的应用程序可以手动调整坐标转换矩阵以获得正确的结果,但在实践中,没有性能优势。

You use the function CGBitmapContextCreate to create a bitmap graphics context. This function takes the following parameters:

  • data. Supply a pointer to the destination in memory where you want the drawing rendered. The size of this memory block should be at least (bytesPerRow*height) bytes.
  • width. Specify the width, in pixels, of the bitmap.
  • height. Specify the height, in pixels, of the bitmap.
  • bitsPerComponent. Specify the number of bits to use for each component of a pixel in memory. For example, for a 32-bit pixel format and an RGB color space, you would specify a value of 8 bits per component. See Supported Pixel Formats(Blow).
  • bytesPerRow. Specify the number of bytes of memory to use per row of the bitmap.
  • colorspace. The color space to use for the bitmap context. You can provide a Gray, RGB, CMYK, or NULL color space when you create a bitmap graphics context. For detailed information on color spaces and color management principles, see Color Management Overview. For information on creating and using color spaces in Quartz, see Color and Color Spaces. For information about supported color spaces, see Color Spaces and Bitmap Layout in the Bitmap Images and Image Masks chapter.
  • bitmapInfo. Bitmap layout information, expressed as a CGBitmapInfo constant, that specifies whether the bitmap should contain an alpha component, the relative location of the alpha component (if there is one) in a pixel, whether the alpha component is premultiplied, and whether the color components are integer or floating-point values. For detailed information on what these constants are, when each is used, and Quartz-supported pixel formats for bitmap graphics contexts and images, see Color Spaces and Bitmap Layout in the Bitmap Images and Image Masks chapter.

Tip: When you create a bitmap graphics context, you’ll get the best performance if you make sure the data and bytesPerRow are 16-byte aligned.

您可以使用函数CGBitmapContextCreate来创建位图图形上下文。 此功能需要以下参数:

  • data。提供指向您要绘制渲染目标的内存指针。 该内存块的大小应至少为(bytesPerRow * height)字节。
  • width。指定位图的宽度(以像素为单位)。
  • height。指定位图的高度(以像素为单位)。
  • bitsPerComponent。指定要用于内存中像素的每个组件的位数。 例如,对于32位像素格式和RGB颜色空间,您可以为每个组件指定一个8位的值。 请参阅支持的像素格式(Blow)。
  • bytesPerRow。指定位图中每行使用的内存的字节数。
  • colorspace。用于位图上下文的颜色空间。 您可以在创建位图图形上下文时提供灰色,RGB,CMYK或NULL颜色空间。 有关颜色空间和颜色管理原则的详细信息,请参阅颜色管理概述。 有关在Quartz中创建和使用颜色空间的信息,请参阅颜色和颜色空间。 有关支持的颜色空间的信息,请参阅位图图像和图像蒙版中的颜色空间和位图布局。
  • bitmapInfo。位图布局信息,表示为CGBitmapInfo常量,用于指定位图是否应包含alpha组件,像素中的alpha组件(如果有的话)的相对位置,是否预先生成alpha组件,以及颜色组件是整数或浮点值。有关这些常量的详细信息,使用每个常量,以及用于位图图形上下文和图像的Quartz支持的像素格式,请参阅位图图像和图像掩码中的颜色空间和位图布局。

提示:当您创建位图图形上下文时,如果确保数据和bytesPerRow为16字节对齐,则将获得最佳性能。

Listing 2-5 shows how to create a bitmap graphics context. When you draw into the resulting bitmap graphics context, Quartz records your drawing as bitmap data in the specified block of memory. A detailed explanation for each numbered line of code follows the listing

清单2-5显示了如何创建位图图形上下文。 当您绘制到生成的位图图形上下文时,Quartz将您的绘图作为位图数据记录在指定的内存块中。 列出每个编号代码行的详细说明。

Listing 2-5 Creating a 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, sizeof(uint8_t) );// 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
}

Here’s what the code does:

  • Declares a variable to represent the number of bytes per row. Each pixel in the bitmap in this example is represented by 4 bytes; 8 bits each of red, green, blue, and alpha.
  • Creates a generic RGB color space. You can also create a CMYK color space. See Color and Color Spaces for more information and for a discussion of generic color spaces versus device dependent ones.
  • Calls the calloc function to create and clear a block of memory in which to store the bitmap data. This example creates a 32-bit RGBA bitmap (that is, an array with 32 bits per pixel, each pixel containing 8 bits each of red, green, blue, and alpha information). Each pixel in the bitmap occupies 4 bytes of memory. In Mac OS X 10.6 and iOS 4, this step can be omitted—if you pass NULL as bitmap data, Quartz automatically allocates space for the bitmap.
  • Creates a bitmap graphics context, supplying the bitmap data, the width and height of the bitmap, the number of bits per component, the bytes per row, the color space, and a constant that specifies whether the bitmap should contain an alpha channel and its relative location in a pixel. The constant kCGImageAlphaPremultipliedLast
    indicates that the alpha component is stored in the last byte of each pixel and that the color components have already been multiplied by this alpha value. See The Alpha Value for more information on premultiplied alpha.
  • If the context isn’t created for some reason, frees the memory allocated for the bitmap data.
  • Releases the color space.
  • Returns the bitmap graphics context. The caller must release the graphics context when it is no longer needed

代码说明:

  • 声明一个变量来表示每行的字节数。 该示例中位图中的每个像素由4个字节表示; 每个红色,绿色,蓝色和阿尔法都有8位。
  • 创建一个通用的RGB颜色空间。 您也可以创建一个CMYK颜色空间。 有关更多信息,请参阅颜色和颜色空间,以及有关通用色彩空间与依赖于设备的颜色空间的讨论。
  • 调用calloc函数来创建和清除存储位图数据的内存块。此示例创建一个32位RGBA位图(即,每像素32位的数组,每个像素包含红,绿,蓝和阿尔法信息的8位)。位图中的每个像素占用4个字节的内存。在Mac OS X 10.6和iOS 4中,可以省略此步骤 - 如果将NULL作为位图数据传递,Quartz会自动为位图分配空间。
  • 创建位图图形上下文,提供位图数据,位图的宽度和高度,每个组件的位数,每行的字节数,颜色空间以及指定位图是否应包含Alpha通道的常数像素中的相对位置常数kCGImageAlphaPremultipliedLast表示α分量存储在每个像素的最后一个字节中,并且颜色分量已经乘以此Alpha值。有关预乘α的更多信息,请参阅Alpha值。
  • 如果由于某种原因未创建上下文,则释放分配给位图数据的内存。
  • 释放颜色空间。
  • 返回位图上下文。当不再需要时,调用者必须释放图形上下文。

Listing 2-6 shows code that calls MyCreateBitmapContext
to create a bitmap graphics context, uses the bitmap graphics context to create a CGImage
object, then draws the resulting image to a window graphics context. Figure 2-3 shows the image drawn to the window. A detailed explanation for each numbered line of code follows the listing.

清单2-6显示了调用MyCreateBitmapContext创建位图图形上下文的代码,使用位图图形上下文来创建CGImage对象,然后将生成的图像绘制到窗口图形上下文。 图2-3显示了绘制到窗口的图像。 列出每个编号代码行的详细说明。

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);

Here’s what the code does:

  • Declares a variable to store the origin and dimensions of the bounding box into which Quartz will draw an image created from the bitmap graphics context.
  • Sets the origin of the bounding box to (0,0)
    and the width and height to variables previously declared, but whose declaration are not shown in this code.
  • Calls the application-supplied function MyCreateBitmapContext (see Listing 2-5) to create a bitmap context that is 400 pixels wide and 300 pixels high. You can create a bitmap graphics context using any dimensions that are appropriate for your application.
  • Calls Quartz 2D functions to draw into the bitmap graphics context. You would replace this and the next four lines of code with drawing code appropriate for your application.
  • Creates a Quartz 2D image (CGImageRef) from the bitmap graphics context.
  • Draws the image into the location in the window graphics context that is specified by the bounding box. The bounding box specifies the location and dimensions in user space in which to draw the image.
    This example does not show the creation of the window graphics context. See Creating a Window Graphics Context in Mac OS X for information on how to create one.
  • Gets the bitmap data associated with the bitmap graphics context.
  • Releases the bitmap graphics context when it is no longer needed.
  • Free the bitmap data if it exists.
  • Releases the image when it is no longer needed

代码说明:

  • 声明一个变量来存储边界框的起点和尺寸,Quartz将绘制从位图图形上下文创建的图像。
  • 将边框的原点设置为(0,0),并将宽度和高度设置为先前声明的变量,但其声明未显示在此代码中。
  • 调用应用程序提供的函数MyCreateBitmapContext(参见清单2-5)来创建一个400像素宽和300像素高的位图上下文。 您可以使用适合您的应用程序的任何维度创建位图图形上下文。
  • 调用Quartz 2D函数来绘制位图图形上下文。以下四行绘制代码可自定义。
  • 从位图图形上下文创建Quartz 2D图像(CGImageRef)
  • 将图像绘制到由边界框指定的窗口图形上下文中的位置。边界框指定用于绘制图像的用户空间中的位置和尺寸。
    此示例不显示窗口图形上下文的创建。有关如何创建一个的信息,请参阅在Mac OS X中创建窗口图形上下文。
  • 获取与位图图形上下文相关联的位图数据
  • 释放位图数据(如果存在)。
  • 当不再需要时释放图像。

[图片上传失败...(image-137405-1534605915675)]

Supported Pixel Formats

Table 2-1 summarizes the pixel formats that are supported for bitmap graphics context, the associated color space (cs
), and the version of Mac OS X in which the format was first available. The pixel format is specified as bits per pixel (bpp) and bits per component (bpc). The table also includes the bitmap information constant associated with that pixel format. See CGImage Reference for details on what each of the bitmap information format constants represent.

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 or 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, kCGImageAlphaNoneSkipLastor kCGBitmapFloatComponents Mac OS X
RGB 128 bpp, 32 bpc, kCGImageAlphaPremultipliedLast or 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, kCGImageAlphaNoneor kCGBitmapFloatComponents Mac OS X
Anti-Aliasing

Bitmap graphics contexts support anti-aliasing, which is the process of artificially correcting the jagged (or aliased) edges you sometimes see in bitmap images when text or shapes are drawn. These jagged edges occur when the resolution of the bitmap is significantly lower than the resolution of your eyes. To make objects appear smooth in the bitmap, Quartz uses different colors for the pixels that surround the outline of the shape. By blending the colors in this way, the shape appears smooth. You can see the effect of using anti-aliasing in Figure 2-4. You can turn anti-aliasing off for a particular bitmap graphics context by calling the function CGContextSetShouldAntialias. The anti-aliasing setting is part of the graphics state.

You can control whether to allow anti-aliasing for a particular graphics context by using the function CGContextSetAllowsAntialiasing. Pass true to this function to allow anti-aliasing; false not to allow it. This setting is not part of the graphics state. Quartz performs anti-aliasing when the context and the graphic state settings are set to true.

位图图形上下文支持抗锯齿功能,这是人工修正当您绘制文字或形状时您有时在位图图像中看到的锯齿状(或别名)边缘的过程。 当位图的分辨率显著低于您的眼睛的分辨率时,会发生这些锯齿状边缘。 为了使对象在位图中显得平滑,Quartz对于围绕形状轮廓的像素使用不同的颜色。 通过以这种方式混合颜色,形状看起来平滑。 您可以在图2-4中看到使用抗锯齿的效果。 您可以通过调用函数CGContextSetShouldAntialias来关闭特定位图图形上下文的反锯齿。 抗锯齿设置是图形状态的一部分。

您可以通过使用函数CGContextSetAllowsAntialiasing来控制是否允许特定图形上下文的反锯齿。传递此功能以允许反锯齿;假不允许它。此设置不是图形状态的一部分。当上下文和图形状态设置设置为true时,Quartz会执行抗锯齿。

Figure 2-4 A comparison of aliased and anti-aliasing drawing

Obtaining a Graphics Context for Printing(打印图形上下文)

Cocoa applications in Mac OS X implement printing through custom NSView subclasses. A view is told to print by invoking its print: method. The view then creates a graphics context that targets a printer and calls its drawRect: method. Your application uses the same drawing code to draw to the printer that it uses to draw to the screen. It can also customize the drawRect: call to an image to the printer that is different from the one sent to the screen.

For a detailed discussion of printing in Cocoa, see Printing Programming Guide for Mac.

Mac OS X中的Cocoa应用程序通过NSView子类实现打印。 通过调用其print:方法,可以看到打印的视图。 然后该视图创建一个打印图形上下文,并调用其drawRect:方法。 您的应用程序可以使用相同的绘图代码绘制到用于绘制到屏幕的打印机。 它还可以自定义drawRect:将图片映射到不同的打印设备。

代码实例:

#import <UIKit/UIKit.h>

@interface CGView : UIView

@end

// 创建PDFContext
CGContextRef MyCrearePDFContent(const CGRect *inMediaBox, CFStringRef path){
    CGContextRef context = NULL;
    CFURLRef url;
    CGDataConsumerRef dataConsumer;

    url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, false);
    if(url != NULL){
        dataConsumer = CGDataConsumerCreateWithURL(url);
        if(dataConsumer != NULL){
            context = CGPDFContextCreate(dataConsumer, inMediaBox, NULL);
        }
        CGDataConsumerRelease(dataConsumer);
    }
    CFRelease(url);
    return context;
}

// 创建BitmapContext
CGContextRef MyCreateBitmapContent(int pixelsWide, int pixelsHigh){
    CGContextRef content = NULL;
    CGColorSpaceRef colorSpace;
    void  *bitmapData;
    NSUInteger bitmapBytesCount;
    NSUInteger bitmapBytesPerRow;

    bitmapBytesPerRow = pixelsWide * 4;
    bitmapBytesCount = bitmapBytesPerRow * pixelsHigh;

    colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    bitmapData = calloc(bitmapBytesCount, sizeof(uint8_t));
    if(bitmapData == NULL){
        fprintf(stderr, "Memory not allocated");
        return NULL;
    }
    content = CGBitmapContextCreate(bitmapData,
                                    pixelsWide,
                                    pixelsHigh,
                                    8,
                                    bitmapBytesPerRow,
                                    colorSpace,
                                    kCGImageAlphaPremultipliedLast);
    if(content == NULL){
        free(bitmapData);
        fprintf(stderr, "Context not created");
        return NULL;
    }
    CGColorSpaceRelease(colorSpace);

    return content;
}

@implementation CGView

- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if(self){

    }
    return self;
}

- (void)drawRect:(CGRect)rect{
    CGContextRef content = UIGraphicsGetCurrentContext();
    CGContextRef myPDFContext;
    CGContextRef myBitmapContext;
    CGRect mediaBox = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);

    /*
    //1, View上下文绘制
    CGContextSetRGBFillColor(content, 1, 0, 0, 1);
    CGContextFillRect(content, CGRectMake(100, 0, 100, 100));
    CGContextSetRGBFillColor(content, 1, 1, 0, 1);
    CGContextFillRect(content, CGRectMake(0, 100, 100, 100));
     */

    //2, PDF上下文绘制
    /*
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *path = [paths[0] stringByAppendingString:@"/test.pdf"];
    myPDFContext = MyCrearePDFContent(&mediaBox, (__bridge CFStringRef)(path));

    CFStringRef myKeys[1];
    CFTypeRef myValues[1];
    myKeys[0] = kCGPDFContextMediaBox;
    myValues[0] = CFDataCreate(NULL,(const UInt8 *)&mediaBox, sizeof(CGRect));

    // 设置pdf page参数
    CFDictionaryRef pageDictionary = CFDictionaryCreate(NULL,
                                                        (const void **)myKeys,
                                                        (const void **)myValues, 1,
                                                        &kCFTypeDictionaryKeyCallBacks,
                                                        &kCFTypeDictionaryValueCallBacks);
    // PDF开始绘制
    CGPDFContextBeginPage(myPDFContext, pageDictionary);

    // PDF绘制
    CGContextSetRGBFillColor(myPDFContext, 1, 0, 0, 1);
    CGContextFillRect(myPDFContext, CGRectMake(100, 0, 100, 100));
    CGContextSetRGBFillColor(myPDFContext, 1, 1, 0, 1);
    CGContextFillRect(myPDFContext, CGRectMake(0, 100, 100, 100));

    // PDF结束绘制
    CGPDFContextEndPage(myPDFContext);
    // PDF释放
    CFRelease(pageDictionary);
    CFRelease(myValues[0]);
    CGContextRelease(myPDFContext);
     */


    // 3, Bitmap上下文绘制
    myBitmapContext = MyCreateBitmapContent(mediaBox.size.width, mediaBox.size.height);

    // bitmap绘制
    CGContextSetRGBFillColor(myBitmapContext, 1, 0, 0, 1);
    CGContextFillRect(myBitmapContext, CGRectMake(100, 0, 100, 100));
    CGContextSetRGBFillColor(myBitmapContext, 1, 1, 0, 1);
    CGContextFillRect(myBitmapContext, CGRectMake(0, 100, 100, 100));

    //bitmap渲染
    CGImageRef myImage = CGBitmapContextCreateImage(myBitmapContext);
    CGContextDrawImage(content, rect, myImage);

    //bitmap释放
    char *bitmapData = CGBitmapContextGetData(myBitmapContext);
    CGContextRelease(myBitmapContext);
    if(bitmapData){ free(bitmapData); }
    CGImageRelease(myImage);
}

@end

推荐阅读更多精彩内容