Gradients
Quartz provides two opaque data types for creating gradients—CGShadingRef and CGGradientRef. You can use either of these to create axial or radial gradients. A gradient is a fill that varies from one color to another.
Quartz提供CGShadingRef和CGGradientRef两种透明数据类型来创建渐变。我们可以使用它们来创建轴向和径向渐变。这里的渐变指的是颜色渐变。
An axial gradient (also called a linear gradient) varies along an axis between two defined end points. All points that lie on a line perpendicular to the axis have the same color value.
轴向渐变就是线性渐变,定义了轴线两端点的变化,所有垂直于轴线的竖线上的颜色值都是相同的。
A radial gradient is a fill that varies radially along an axis between two defined ends, which typically are both circles. Points share the same color value if they lie on the circumference of a circle whose center point falls on the axis. The radius of the circular sections of the gradient are defined by the radii of the end circles; the radius of each intermediate circle varies linearly from one end to the other.
径向渐变是两个端点的轴向渐变,两个端点通常是圆。
This chapter provides examples of the sorts of linear and radial gradients you can create with Quartz, compares the two approaches you can take to painting gradients, and then shows how to use each opaque data type to create a gradient.
本章提供了可以使用Quartz创建的各种线性和径向渐变的示例,比较两种绘制渐变的方法,然后介绍怎么使用之前提到的两种不透明数据类型来创建渐变。
Axial and Radial Gradient(轴向和径向渐变)
Quartz functions provide a rich vocabulary for creating gradient effects. This section shows some of the results you can achieve. The axial gradient in Figure 8-1 varies between one endpoint that is a shade of orange and another that is a shade of yellow. In this case, the axis is at a 45 degree angle with respect to the origin.
Quartz函数提供了一个丰富的功能来创建渐变效果。图8-1是由橙色到黄色的轴向渐变。在这个例子中渐变轴相对原点倾斜了45度角。
Quartz also lets you specify colors and locations along an axis to create more complex axial gradients, as shown in Figure 8-2. The color at the starting point is a shade of red and the color at the ending point is a shade of violet. However, there are also five locations on the axis whose color is set to orange, yellow, green, blue, and indigo, respectively. You can think of the result as six sequential linear gradients along the same axis. Although the axis used here is the same as that used in Figure 8-1 (45 degree angle), it doesn’t have to be. The angle of the axis is defined by the starting and ending point that you provide.
Quartz也允许我们制定一系列的颜色和位置,来创建更复杂的轴向渐变。如图8-2所示。起始点是红色,结束点是紫罗兰色。同时轴上的其他五个位置的颜色分别是橙色,黄色,蓝色,靛色。我们可以将他们看作是一条轴被分为六段的线性渐变。其中轴线的倾斜角度我们可以自定义,不必一定和图8-1的一样。
Figure 8-3 shows a radial gradient that varies between a small, bright red circle and a larger black one.
图8-3显示了一个径向渐变,它从明亮的红色小圆渐变到黑色的大圆。
With Quartz, you are not restricted to creating gradients based on color changes; you can vary only the alpha, or you can vary the alpha along with the other color components. Figure 8-4 shows a gradient whose red, green, and blue components remain constant as the alpha value varies from 1.0 to 0.1.
在Quartz中,我们可以自定义渐变的颜色值,我们可以通过修改或创建alpha值来创建透明渐变。图8-4展示了颜色不变,透明度从1.0到0.1的渐变。
Note: If you vary a gradient using alpha, you will not be able to capture that gradient when drawing to a PDF content. Because of this, such a gradient can’t be printed. If you need to draw a gradient to a PDF, use an alpha of 1.0.
提示:使用alpha的渐变不能被绘制在PDF上面,因此我们在绘制渐变到PDF上是要设置alpha为1。
You can position the circles in a radial gradient to create a variety of shapes. If one circle is partially or fully outside the other, Quartz creates a conical surface for circles that have unequal circumferences, and a cylindrical surface for circles that have equal circumferences. A common use of a radial gradient is to create a shaded sphere, as shown in Figure 8-5. In this case, a single point (a circle with a radius of 0) lies within a larger circle.
我们可以把一个圆放在一个径向渐变里来创建形状。如果一个圆是另一个的一部分或者在另一个的外面,则Quartz创建了圆锥和圆柱。径向渐变通常被用来创建带有阴影的球体,如8-5所示。这个案例中,一个大圆包含了一个点,这个点是直径为0的圆。
You can create more complex effects by nesting several radial gradients similar to the shape shown in Figure 8-6. The toroidal portion of the shape is created using concentric circles.
我们可以像图8-6一样通过嵌套几个径向渐变擦混构建更复杂的效果。它使用通信员创建图形中的环形部分。
A Comparison of CGShading and CGGradient Objects(CGShading和CGGradient比较)
A Comparison of CGShading and CGGradient Objects
With two type of objects available for creating gradients, you might be wondering which one is best to use. This section helps answer that question.
我们有两个对象类型来创建渐变,你可能想知道哪一个更好用,本节就告诉你。
The CGShadingRef opaque data type gives you control over how the color at each point in the gradient is computed. Before you can create a CGShading object, you must create a CGFunction object CGFunctionRef that defines a function for computing colors in the gradient. Writing a custom function gives you the freedom to create smooth gradients, such as those shown in Figure 8-1, Figure 8-3, and Figure 8-5 or more unconventional effects, such as that shown in Figure 8-12.
CGShadingRef这个并不透明数据类型给我们更多控制权限,来确定如何计算端点的颜色。在我们使用CGShading之前,我们不需创建一个CGFunction对象(CGFunctionRef),这个对象定义了一个用于计算渐变颜色的函数。写一个函数让我们能够创建平滑的渐变,如图8-1,8-3,8-5及更多非传统效果,如8-12所示。
When you create a CGShading object, you specify whether it is axial (linear) or radial. Along with the gradient calculation function (encapsulated as a CGFunction object) you also supply a color space, and starting and ending points or radii, depending on whether you draw an axial or radial gradient. At drawing time, you simply pass the CGShading object along with the drawing context to the function CGContextDrawShading. Quartz invokes your gradient calculation function for each point in the gradient.
当创建CGShading对象时,我们需要指定是轴向渐变还是径向渐变。除了计算函数外,我们还要提供一个颜色空间,起始点和结束点,如果是径向渐变那么还要给出半径。在绘制时,我们提供CGShading对象和上下文给CGContextDrawShading函数。Quartz根据渐变函数来绘制渐变。
A CGGradient object is a subset of a CGShading object that’s designed with ease-of-use in mind. The CGGradientRef
opaque data type is straightforward to use because Quartz calculates the color at each point in the gradient for you—you don’t supply a gradient calculation function. When you create a gradient object, you provide an array of locations and colors. Quartz calculates a gradient for each set of contiguous locations, using the color you assign to each location as the end points for the gradient. You can set a gradient object to use a single starting and ending location, as shown in Figure 8-1, or you can provide a number of points to create an effect similar to what’s shown in Figure 8-2. The ability to provide more than two locations is an advantage over using a CGShading object, which is limited to two locations.
CGGradient是CGShading的子集,其更易于使用。CGGradient更加易用的原因是Quartz会计算渐变中每个顶点的颜色值,不需要我们提供渐变计算函数。当创建渐变对象时,我们只需要提供位置数组和其对应的颜色数组,Quartz根据我们提供的位置和对应颜色数组来计算渐变。我们可以只设置一个起点和一个结束点来创建渐变对象,如图8-1,也可以设置一组端点来创建类似图8-2的效果。这个特性比被显示使用两个位置的CGShading很有优势。
When you create a CGGradient object, you simply set up a color space, locations, and a color for each location. When you draw to a context using a gradient object, you specify whether Quartz should draw an axial or radial gradient. At drawing time, you specify starting and ending points or radii, depending on whether you draw an axial or radial gradient, in contrast to CGShading objects, whose geometry is defined at creation time, not at drawing time.
当创建CGGradient对象时,我们需要指定颜色空间,位置及其对应的颜色值。当使用渐变对象绘制上下文时 ,我们需要指定是轴向渐变还是径向渐变。在绘制时,我们还要提供一个颜色空间,起始点和结束点,如果是径向渐变那么还要给出半径。
CGShading创建的渐变类型在创建时定义,而不是绘制时。
Table 8-1 summarizes the differences between the two opaque data types.
Table 8-1 Differences between CGShading and CGGradient objects
CGGradient | CGShading |
---|---|
Can use the same object to draw axial and radial gradients. | Need to create separate objects for axial and radial gradients. |
Set the geometry of the gradient at drawing time. | Set the geometry of the gradient at object creation time. |
Quartz calculates the colors for each point in the gradient. | You must supply a callback function that calculates the colors for each point in the gradient. |
Easy to define more than two locations and colors. | Need to design your callback to use more than two locations and colors, so it takes a bit more work on your part. |
Extending Color Beyond the End of a Gradient(扩展渐变端点外部的颜色)
When you create a gradient, you have the option of filling the space beyond the ends of the gradient with a solid color. Quartz uses the color defined at the boundary of the gradient as the fill color. You can extend beyond the start of a gradient, the end of a gradient, or both. You can apply the option to an axial or a radial gradient created using either a CGShading object or a CGGradient object. Each type of object supplies constants you can use to set the extension option, as you’ll see in Using a CGGradient Object and Using a CGShading Object.
创建渐变时,您可以选择使用纯色填充超出渐变结束的部分。 Quartz使用渐变边界定义的颜色作为填充颜色。 你可以扩展渐变的开始,渐变的结束或者开始和结束。 你也可以使用GGShading和CGGradient提供的选项来扩展轴向或径向渐变。如Using a CGGradient Object和Using a CGShading Object章节所示.
Figure 8-7 shows an axial gradient that extends at both the starting and ending locations. The line in the figure shows the axis of the gradient. As you can see, the fill colors correspond to the colors at the starting and ending points.
图8-7是轴向扩展渐变
Figure 8-8 compares a radial gradient that does not use the extension options with one that uses extension options for both the starting and ending locations. Quartz takes the starting and ending color values and uses those solid colors to extend the surface as shown. The figure shows the starting and ending circles, and the axis of the gradient.
图8-8比较了未使用径向渐变扩展和使用径向渐变扩展。
Using a CGGradient Object(使用CGGradient对象)
The CGGradient object is an abstract definition of a gradient—it simply specifies colors and locations, but not the geometry. You can use this same object for both axial and radial geometries. As an abstract definition, CGGradient objects are perhaps more readily reusable than their counterparts, CGShading objects. Not having the geometry locked in the CGGradient object allows for the possibility of iteratively painting gradients based on the same color scheme without the need for also tying up memory resources in multiple CGGradient objects.
CGGradient对象是渐变的抽象定义,它只是指定颜色和位置,而不是几何。 您可以使用相同的对象用于轴向和径向几何。 作为抽象的定义,CGGradient对象可能比其对应的CGShading对象更容易重用。 CGGradient对象中没有将几何锁定在一起可以允许基于相同颜色方案迭代绘制渐变的可能性,而不需要在多个CGGradient对象中绑定内存资源。
Because Quartz calculates the gradient for you, using a CGGradient object to create and draw a gradient is fairly straightforward, requiring these steps:
- Create a CGGradient object, supplying a color space, an array of two or more color components, an array of two or more locations, and the number of items in each of the two arrays.
- Paint the gradient by calling either CGContextDrawLinearGradient or CGContextDrawRadialGradient and supplying a context, a CGGradient object, drawing options, and the stating and ending geometry (points for axial gradients or circle centers and radii for radial gradients).
- Release the CGGradient object when you no longer need it.
因为Quartz为您计算渐变,因此使用CGGradient对象创建和绘制渐变是相当简单的,需要这些步骤:
- 创建一个CGGradient对象,提供一个颜色空间,一个两个或更多个颜色组件的数组,两个或更多个位置的数组,以及两个数组中每一个的项数。
- 通过调用CGContextDrawLinearGradient或CGContextDrawRadialGradient并提供上下文,CGGradient对象,绘图选项以及说明和结束几何(轴向渐变或圆心以及径向渐变半径的点)来绘制渐变。
- 不再需要时释放CGGradient对象。
A location is a CGFloat value in the range of 0.0 to 1.0, inclusive, that specifies the normalized distance along the axis of the gradient. A value of 0.0 specifies the starting point of the axis, while 1.0 specifies the ending point of the axis. Other values specify a proportion of the distance, such as 0.25 for one-fourth of the distance from the starting point and 0.5 for the halfway point on the axis. At a minimum, Quartz uses two locations. If you pass NULL for the locations array, Quartz uses 0 for the first location and 1 for the second.
位置是0.0到1.0(含)范围内的CGFloat值,用于指定沿梯度轴的归一化距离。值0.0表示轴的起始点,而1.0表示轴的终点。其他值指定距离的比例,例如距离起点的距离的四分之一为0.25,在轴上的中点为0.5。至少,Quartz使用两个位置。如果对位置数组传递NULL,则Quartz对于第一个位置使用0,在第二个位置使用1。
The number of color components per color depends on the color space. For onscreen drawing, you’ll use an RGB color space. Because Quartz draws with alpha, each onscreen color has four components—red, green, blue, and alpha. So, for onscreen drawing, the number of elements in the color component array that you provide must contain four times the number of locations. Quartz RGBA color components can vary in value from 0.0 to 1.0, inclusive.
每种颜色的颜色分量的数量取决于颜色空间。对于屏幕绘图,您将使用RGB颜色空间。因为Quartz绘制了Alpha,每个屏幕颜色都有四个组件 - 红色,绿色,蓝色和alpha。因此,对于屏幕绘图,您提供的颜色组件数组中的元素数量必须包含四倍的位置数。 Quartz RGBA颜色分量的值可以在0.0到1.0之间变化。
Listing 8-1 is a code fragment that creates a CGGradient object. After declaring the necessary variables, the code sets the locations and the requisite number of color components (for this example, 2 X 4 = 8). It creates a generic RGB color space. (In iOS, where generic RGB color spaces are not available, your code should call CGColorSpaceCreateDeviceRGB instead.) Then, it passes the necessary parameters to the function CGGradientCreateWithColorComponents. You can also use the function CGGradientCreateWithColors which is convenient if your application sets up CGColor objects.
清单8-1是创建CGGradient对象的代码片段。声明必要变量后,代码设置颜色分量的位置和必需数量(例如,2 X 4 = 8)。它创建一个通用的RGB颜色空间。 (在iOS中,通用的RGB颜色空间不可用,您的代码应该调用CGColorSpaceCreateDeviceRGB。)然后,它将必要的参数传递给CGGradientCreateWithColorComponents函数。您还可以使用函数CGGradientCreateWithColors,如果您的应用程序设置了CGColor对象,这是方便的。
Listing 8-1 Creating a CGGradient object
CGGradientRef myGradient;
CGColorSpaceRef myColorspace;
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGFloat components[8] = { 1.0, 0.5, 0.4, 1.0, // Start color
0.8, 0.8, 0.3, 1.0 }; // End color
myColorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
myGradient = CGGradientCreateWithColorComponents (myColorspace, components,
locations, num_locations);
After you create a CGGradient object, you can use it to paint an axial or linear gradient. Listing 8-2 is a code fragment that declares and sets the starting and ending points for a linear gradient and then paints the gradient. Figure 8-1 shows the result. The code does not show how to obtain the CGContext object (myContext).
我们在创建CGGradient对象之后,可以使用它来绘制径向或轴向渐变。8-2展示了一个有起点有终点的线性渐变,图8-1是其结果。代码不显示如何获取CGContent对象。
Listing 8-2 Painting an axial gradient using a CGGradient object
CGPoint myStartPoint, myEndPoint;
myStartPoint.x = 0.0;
myStartPoint.y = 0.0;
myEndPoint.x = 1.0;
myEndPoint.y = 1.0;
CGContextDrawLinearGradient (myContext, myGradient, myStartPoint, myEndPoint, 0);
Listing 8-3 is a code fragment that uses the CGGradient object created in Listing 8-1 to paint the radial gradient shown in Figure 8-9. This example illustrates the result of extending the area of the gradient by filling it with a solid color.
8-3显示用CGGradient对象创建项图8-9所示的径向渐变。这个例子展示了用纯色填充渐变区域的结果。
Listing 8-3 Painting a radial gradient using a CGGradient object
CGPoint myStartPoint, myEndPoint;
CGFloat myStartRadius, myEndRadius;
myStartPoint.x = 0.15;
myStartPoint.y = 0.15;
myEndPoint.x = 0.5;
myEndPoint.y = 0.5;
myStartRadius = 0.1;
myEndRadius = 0.25;
CGContextDrawRadialGradient (myContext, myGradient, myStartPoint,
myStartRadius, myEndPoint, myEndRadius,
kCGGradientDrawsAfterEndLocation);
The radial gradient shown in Figure 8-4 was created using the variables shown in Listing 8-4.
Listing 8-4 The variables used to create a radial gradient by varying alpha
CGPoint myStartPoint, myEndPoint;
CGFloat myStartRadius, myEndRadius;
myStartPoint.x = 0.2;
myStartPoint.y = 0.5;
myEndPoint.x = 0.65;
myEndPoint.y = 0.5;
myStartRadius = 0.1;
myEndRadius = 0.25;
size_t num_locations = 2;
CGFloat locations[2] = { 0, 1.0 };
CGFloat components[8] = { 0.95, 0.3, 0.4, 1.0,
0.95, 0.3, 0.4, 0.1 };
Listing 8-5 shows the variables used to create the gray gradient shown in Figure 8-10, which has three locations.
Listing 8-5 The variables used to create a gray gradient
size_t num_locations = 3;
CGFloat locations[3] = { 0.0, 0.5, 1.0};
CGFloat components[12] = { 1.0, 1.0, 1.0, 1.0,
0.5, 0.5, 0.5, 1.0,
1.0, 1.0, 1.0, 1.0 };
Using a CGShading Object(使用CGShading对象)
You set up a gradient by creating a CGShading object calling the function CGShadingCreateAxial or CGShadingCreateRadial, supplying the following parameters:
- A CGColorSpace object that describe the color space for Quartz to use when it interprets the color component values your callback supplies.
- Starting and ending points. For axial gradients, these are the starting and ending coordinates (in user space) of the axis. For radial gradients, these are the coordinates of the center of the starting and ending circles.
- Starting and ending radii (only for a radial gradient) for the circles used to define the gradient area.
- A CGFunction object, which you obtain by calling the function CGFunctionCreate
, discussed later in this section. This callback routine must return a color to draw at a particular point. - Boolean values that specify whether to fill the area beyond the starting or ending points with a solid color.
The CGFunction object you supply to the CGShading creation functions contains a callbacks structure and all the information Quartz needs to implement your callback. Perhaps the trickiest part of setting up a CGShading object is creating the CGFunction object. When you call the function CGFunctionCreate, you supply the following:
- A pointer to any data your callback needs.
- The number of input values to your callback. Quartz requires that your callback takes one input value.
- An array of floating-point values. Quartz supplies your callback with only one element in this array. An input value can range from 0, for the color at the start of the gradient, to 1, for the color at the end of the gradient.
- The number of output values provided by your callback. For each input value, your callback must supply a value for each color component and an alpha value to designate opacity. The color component values are interpreted by Quartz in the color space you create and supply to the CGShading creation function. For example, if you are using an RGB color space, you supply the value 4 as the number of output values (R, G, B, and A).
- An array of floating-point values that specify each of the color components and an alpha value.
- A callbacks data structure that contains the version of the structure (set this field to 0
), your callback for generating color component values, and an optional callback to release the data supplied to your callback in the info parameter. If you were to name your callback myCalculateShadingValues, it would look like this:
void myCalculateShadingValues (void *info, const CGFloat *in, CGFloat *out)
After you create the CGShading object, you can set up additional clipping if you need to do so. Then, call the function CGContextDrawShading
to paint the clipping area of the context with the gradient. When you call this function, Quartz invokes your callback to obtain color values that span the range from the starting point to the ending point.
When you no longer need the CGShading object, you release it by calling the function CGShadingRelease.
Painting an Axial Gradient Using a CGShading Object and Painting a Radial Gradient Using a CGShading Object provide step-by-step instructions on writing code that uses a CGShading object to draw a gradient.
Painting an Axial Gradient Using a CGShading Object(使用CGShading绘制轴向渐变)
Axial and radial gradients require you to perform similar steps. This example shows how to draw an axial gradient using a CGShading object, create a semicircular clipping path in a graphics context, then paint the gradient to the clipped context to achieve the output shown in Figure 8-11.
To paint the axial gradient shown in the figure, follow the steps explained in these sections:
- Set Up a CGFunction Object to Compute Color Values
- Create a CGShading Object for an Axial Gradient
- Clip the Context
- Paint the Axial Gradient Using a CGShading Object
- Release Objects
1. Set Up a CGFunction Object to Compute Color Values
You can compute color values any way you like, as long as your color computation function takes three parameters:
- void *info. This is NULL or a pointer to data you pass to the CGShading creation function.
- const CGFloat *in. Quartz passes the in
array to your callback. The values in the array must be in the input value range defined for your CGFunction object. For this example, the input range is 0 to 1; see Listing 8-7. - CGFloat *out. Your callback passes the out
array to Quartz. It contains one element for each color component in the color space, and an alpha value. Output values should be in the output value range defined for your CGFunction object. For this example, the output range is 0 to 1; see Listing 8-7.
For more information on these parameters, see CGFunctionEvaluateCallback.
Listing 8-6 shows a function that computes color component values by multiplying the values defined in a constant array by the input value. Because the input value ranges from 0 through 1, the output values range from black (for RGB, the values 0, 0, 0), through (1, 0, .5) which is a purple hue. Note that the last component is always set to 1
, so that the colors are always fully opaque.
Listing 8-6 Computing color component values
static void myCalculateShadingValues (void *info,
const CGFloat *in,
CGFloat *out)
{
CGFloat v;
size_t k, components;
static const CGFloat c[] = {1, 0, .5, 0 };
components = (size_t)info;
v = *in;
for (k = 0; k < components -1; k++)
*out++ = c[k] * v;
*out++ = 1;
}
After you write your callback to compute color values, you package it as part of a CGFunction object. It’s the CGFunction object you supply to Quartz when you create a CGShading object. Listing 8-7 shows a function that creates a CGFunction object that contains the callback from Listing 8-6. A detailed explanation for each numbered line of code appears following the listing.
Listing 8-7 Creating a CGFunction object
static CGFunctionRef myGetFunction (CGColorSpaceRef colorspace)// 1
{
size_t numComponents;
static const CGFloat input_value_range [2] = { 0, 1 };
static const CGFloat output_value_ranges [8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
static const CGFunctionCallbacks callbacks = { 0,// 2
&myCalculateShadingValues,
NULL };
numComponents = 1 + CGColorSpaceGetNumberOfComponents (colorspace);// 3
return CGFunctionCreate ((void *) numComponents, // 4
1, // 5
input_value_range, // 6
numComponents, // 7
output_value_ranges, // 8
&callbacks);// 9
}
Here’s what the code does:
- Takes a color space as a parameter.
- Declares a callbacks structure and fills it with the version of the structure (0), a pointer to your color component calculation callback, and NULL for the optional release function.
- Calculates the number of color components in the color space and increments the value by 1 to account for the alpha value.
- Passes a pointer to the numComponents value. This value is used by the callback myCalculateShadingValues to determine the number of components to compute.
- Specifies that 1 is the number of input values to the callback.
- Provides an array that specifies the valid intervals for the input. This array contains 0 and 1.
- Passes the number of output values, which is the number of color components plus alpha.
- Provides an array that specifies the valid intervals for each output value. This array specifies, for each component, the intervals 0 and 1. Because there are four components, there are eight elements in this array.
- Passes a pointer to the callback structure declared and filled previously.
2. Create a CGShading Object for an Axial Gradient
To create a CGShading object, you call the function CGShadingCreateAxial, as shown in Listing 8-8, passing a color space, starting and ending points, a CGFunction object, and a Boolean value that specifies whether to fill the area beyond the starting and ending points of the gradient.
Listing 8-8 Creating a CGShading object for an axial gradient
CGPoint startPoint,
endPoint;
CGFunctionRef myFunctionObject;
CGShadingRef myShading;
startPoint = CGPointMake(0,0.5);
endPoint = CGPointMake(1,0.5);
colorspace = CGColorSpaceCreateDeviceRGB();
myFunctionObject = myGetFunction (colorspace);
myShading = CGShadingCreateAxial (colorspace,
startPoint, endPoint,
myFunctionObject,
false, false);
3. Clip the Context
When you paint a gradient, Quartz fills the current context. Painting a gradient is different from working with colors and patterns, which are used to stroke and fill path objects. As a result, if you want your gradient to appear in a particular shape, you need to clip the context accordingly. The code in Listing 8-9 adds a semicircle to the current context so that the gradient is painted into that clip area, as shown in Figure 8-11.
If you look carefully, you’ll notice that the code should result in a half circle, whereas the figure shows a half ellipse. Why? You’ll see, when you look at the entire routine in A Complete Routine for an Axial Gradient Using a CGShading Object, that the context is also scaled. More about that later. Although you might not need to apply scaling or a clip in your application, these and many other options exist in Quartz 2D to help you achieve interesting effects.
Listing 8-9 Adding a semicircle clip to the graphics context
CGContextBeginPath (myContext);
CGContextAddArc (myContext, .5, .5, .3, 0,
my_convert_to_radians (180), 0);
CGContextClosePath (myContext);
CGContextClip (myContext);
4. Paint the Axial Gradient Using a CGShading Object
Call the function CGContextDrawShading to fill the current context using the color gradient specified in the CGShading object:
CGContextDrawShading (myContext, myShading);
5. Release Objects
You call the function CGShadingRelease
when you no longer need the CGShading object. You also need to release the CGColorSpace object and the CGFunction object as shown in Listing 8-10.
Listing 8-10 Releasing objects
CGShadingRelease (myShading);
CGColorSpaceRelease (colorspace);
CGFunctionRelease (myFunctionObject);
A Complete Routine for an Axial Gradient Using a CGShading Object
The code in Listing 8-11 shows a complete routine that paints an axial gradient, using the CGFunction object set up in Listing 8-7 and the callback shown in Listing 8-6. A detailed explanation for each numbered line of code appears following the listing.
Listing 8-11 Painting an axial gradient using a CGShading object
void myPaintAxialShading (CGContextRef myContext,// 1
CGRect bounds)
{
CGPoint startPoint,
endPoint;
CGAffineTransform myTransform;
CGFloat width = bounds.size.width;
CGFloat height = bounds.size.height;
startPoint = CGPointMake(0,0.5); // 2
endPoint = CGPointMake(1,0.5);// 3
colorspace = CGColorSpaceCreateDeviceRGB();// 4
myShadingFunction = myGetFunction(colorspace);// 5
shading = CGShadingCreateAxial (colorspace, // 6
startPoint, endPoint,
myShadingFunction,
false, false);
myTransform = CGAffineTransformMakeScale (width, height);// 7
CGContextConcatCTM (myContext, myTransform);// 8
CGContextSaveGState (myContext);// 9
CGContextClipToRect (myContext, CGRectMake(0, 0, 1, 1));// 10
CGContextSetRGBFillColor (myContext, 1, 1, 1, 1);
CGContextFillRect (myContext, CGRectMake(0, 0, 1, 1));
CGContextBeginPath (myContext);// 11
CGContextAddArc (myContext, .5, .5, .3, 0,
my_convert_to_radians (180), 0);
CGContextClosePath (myContext);
CGContextClip (myContext);
CGContextDrawShading (myContext, shading);// 12
CGColorSpaceRelease (colorspace);// 13
CGShadingRelease (shading);
CGFunctionRelease (myShadingFunction);
CGContextRestoreGState (myContext); // 14
}
Here’s what the code does:
- Takes as parameters a graphics context and a rectangle to draw into.
- Assigns a value to the starting point. The routine calculates values based on a user space that varies from 0 to 1. You’ll scale the space later for the window that Quartz draws into. You can think of this coordinate location as x at the far left side and y at 50% from the bottom.
- Assigns a value to the ending point. You can think of this coordinate location as x at the far right side and y at 50% from the bottom. As you can see, the axis for the gradient is a horizontal line.
- Creates a color space for device RGB because this routine draws to the display.
- Creates a CGFunction object by calling the routine shown in Listing 8-7 and passing the color space you just created.
- Creates a CGShading object for an axial gradient. The last two parameters are false
, to signal that Quartz should not fill the area beyond the starting and ending points. - Sets up an affine transform that is scaled to the height and width of the window used for drawing. Note that the height is not necessarily equal to the width. In this example, because the two aren’t equal, the end result is elliptical rather than circular.
- Concatenates the transform you just set up with the graphics context passed to the routine.
- Saves the graphics state to enable you to restore this state later.
- Sets up a clipping area. This line and the next two lines clip the context to a rectangle that is filled with white. The effect is that the gradient is drawn to a window with a white background.
- Creates a path. This line and the next three lines set up an arc that is half a circle and adds it to the graphics context as a clipping area. The effect is that the gradient is drawn to an area that is half a circle. However, the circle will be transformed by the height and width of the window (see step 8), resulting in a final effect of a gradient drawn to a half ellipse. As the window is resized by the user, the clipping area is resized.
- Paints the gradient to the graphics context, transforming and clipping the gradient as described previously.
- Releases objects. This line and the next two lines release all the objects you created.
- Restores the graphics state to the state that existed before you set up the filled background and clipped to half a circle. The restored state is still transformed by the width and height of the window.
Painting a Radial Gradient Using a CGShading Object(使用CGShading绘制径向渐变)
This example shows how to use a CGShading object to produce the output shown in Figure 8-12.
To paint a radial gradient, follow the steps explained in the following sections:
- Set Up a CGFunction Object to Compute Color Values.
- Create a CGShading Object for a Radial Gradient
- Paint a Radial Gradient Using a CGShading Object
- Release Objects
1. Set Up a CGFunction Object to Compute Color Values
There is no difference between writing functions to compute color values for radial and axial gradients. In fact, you can follow the instruction outlined for axial gradients in Set Up a CGFunction Object to Compute Color Values. Listing 8-12 calculates color so that the color components vary sinusoidally, with a period based on frequency values declared in the function. The result seen in Figure 8-12 is quite different from the colors shown in Figure 8-11. Despite the differences in color output, the code in Listing 8-12 is similar to Listing 8-6 in that each function follows the same prototype. Each function takes one input value and calculates N values, one for each color component of the color space plus an alpha value.
Listing 8-12 Computing color component values
static void myCalculateShadingValues (void *info,
const CGFloat *in, CGFloat *out)
{
size_t k, components;
double frequency[4] = { 55, 220, 110, 0 };
components = (size_t)info;
for (k = 0; k < components - 1; k++)
*out++ = (1 + sin(*in * frequency[k]))/2;
*out++ = 1; // alpha
}
Recall that after you write a color computation function, you need to create a CGFunction object, as described for axial values in Set Up a CGFunction Object to Compute Color Values.
2. Create a CGShading Object for a Radial Gradient
To create a CGShading object or a radial gradient, you call the function CGShadingCreateRadial, as shown in Listing 8-13, passing a color space, starting and ending points, starting and ending radii, a CGFunction object, and Boolean values to specify whether to fill the area beyond the starting and ending points of the gradient.
Listing 8-13 Creating a CGShading object for a radial gradient
CGPoint startPoint, endPoint;
CGFloat startRadius, endRadius;
startPoint = CGPointMake(0.25,0.3);
startRadius = .1;
endPoint = CGPointMake(.7,0.7);
endRadius = .25;
colorspace = CGColorSpaceCreateDeviceRGB();
myShadingFunction = myGetFunction (colorspace);
CGShadingCreateRadial (colorspace, startPoint, startRadius, endPoint, endRadius, myShadingFunction, false, false);
3. Paint the Axial Gradient Using a CGShading Object
Calling the function CGContextDrawShading fills the current context using the specified color gradient specified in the CGShading object.
CGContextDrawShading (myContext, shading);
Notice that you use the same function to paint a gradient regardless of whether the gradient is axial or radial.
5. Release Objects
You call the function CGShadingRelease when you no longer need the CGShading object. You also need to release the CGColorSpace object and the CGFunction object as shown in Listing 8-14.
Listing 8-14 Code that releases objects
CGShadingRelease (myShading);
CGColorSpaceRelease (colorspace);
CGFunctionRelease (myFunctionObject);
A Complete Routine for Painting a Radial Gradient Using a CGShading Object
The code in Listing 8-15 shows a complete routine that paints a radial gradient using the CGFunction object set up in Listing 8-7 and the callback shown in Listing 8-12. A detailed explanation for each numbered line of code appears following the listing.
Listing 8-15 A routine that paints a radial gradient using a CGShading object
void myPaintRadialShading (CGContextRef myContext,// 1
CGRect bounds);
{
CGPoint startPoint,
endPoint;
CGFloat startRadius,
endRadius;
CGAffineTransform myTransform;
CGFloat width = bounds.size.width;
CGFloat height = bounds.size.height;
startPoint = CGPointMake(0.25,0.3); // 2
startRadius = .1; // 3
endPoint = CGPointMake(.7,0.7); // 4
endRadius = .25; // 5
colorspace = CGColorSpaceCreateDeviceRGB(); // 6
myShadingFunction = myGetFunction (colorspace); // 7
shading = CGShadingCreateRadial (colorspace, // 8
startPoint, startRadius,
endPoint, endRadius,
myShadingFunction,
false, false);
myTransform = CGAffineTransformMakeScale (width, height); // 9
CGContextConcatCTM (myContext, myTransform); // 10
CGContextSaveGState (myContext); // 11
CGContextClipToRect (myContext, CGRectMake(0, 0, 1, 1)); // 12
CGContextSetRGBFillColor (myContext, 1, 1, 1, 1);
CGContextFillRect (myContext, CGRectMake(0, 0, 1, 1));
CGContextDrawShading (myContext, shading); // 13
CGColorSpaceRelease (colorspace); // 14
CGShadingRelease (shading);
CGFunctionRelease (myShadingFunction);
CGContextRestoreGState (myContext); // 15
}
Here’s what the code does:
- Takes as parameters a graphics context and a rectangle to draw into.
- Assigns a value to the center of the starting circle. The routine calculates values based on a user space that varies from 0 to 1. You’ll scale the space later for the window Quartz draws into. You can think of this coordinate location as x at 25% from the left and y at 30% from the bottom.
- Assigns the radius of the starting circle. You can think of this as 10% of the width of user space.
- Assigns a value to the center of the ending circle. You can think of this coordinate location as x at 70% from the left and y at 70% from the bottom.
- Assigns the radius of the ending circle. You can think of this as 25% of the width of user space. The ending circle will be larger than the starting circle. The conical shape will be oriented from left to right, tipped upwards.
- Creates a color space for device RGB because this routine draws to the display.
- Creates a CGFunctionObject by calling the routine shown in Listing 8-7 and passing the color space you just created. However, recall that you’ll use the color calculation function shown in Listing 8-12.
- Creates a CGShading object for a radial gradient. The last two parameters are false, to signal that Quartz should not fill the area beyond the starting and ending points of the gradient.
- Sets up an affine transform that is scaled to the height and width of the window used for drawing. Note that the height is not necessarily equal to the width. In fact, the transformation will change whenever the user resizes the window.
- Concatenates the transform you just set up with the graphics context passed to the routine.
- Saves the graphics state to enable you to restore this state later.
- Sets up a clipping area. This line and the next two lines clip the context to a rectangle that is filled with white. The effect is that the gradient is drawn to a window with a white background.
- Paints the gradient to the graphics context transforming the gradient as described previously.
- Releases object. This line and the next two lines release all the objects you created.
- Restores the graphics state to the state that existed before you set up the filled background. The restored state is still transformed by the width and height of the window.
代码如下:
- 传入参数图形上下文和要绘制的矩形。
- 将值赋值给起始圆的中心。该例程基于从0到1的用户空间来计算值。您将缩放Quartz绘制的窗口的空间。你可以把这个坐标位置看作x,左边是25%,y从底部的30%。
- 赋值起始圆的半径。您可以将其视为用户空间宽度的10%。
- 给圆圈的中心赋值。你可以将这个坐标位置看作x,从左边70%,y从底部的70%。
- 赋值圆圈的半径。您可以将其视为用户空间宽度的25%。结束圆将大于起始圆。锥形形状将从左向右定向,向上倾斜。
- 创建设备RGB的颜色空间,因为此例程绘制到显示器。
- 通过调用清单8-7所示的例程并传递刚创建的颜色空间来创建CGFunctionObject。但是,请记住,您将使用如清单8-12所示的颜色计算功能。
- 为径向渐变创建CGShading对象。最后两个参数为false,表示Quartz不应填写超出渐变起点和终点的区域。
- 设置一个缩放到用于绘制的窗口的高度和宽度的仿射变换。请注意,高度不一定等于宽度。事实上,只要用户调整窗口大小,转换就会改变。
- 将您刚刚设置的变换与传递给例程的图形上下文相连。
- 保存图形状态,以便稍后恢复此状态。
- 设置一个裁剪区域。这行和接下来的两行将上下文剪切为填充有白色的矩形。效果是将渐变绘制到具有白色背景的窗口。
- 绘制梯度到图形上下文,如前所述转换梯度。
- 发布对象此行和下两行释放您创建的所有对象。
- 将图形状态恢复为设置填充背景之前存在的状态。恢复的状态仍然被窗口的宽度和高度变化。
See Also 参阅
CGGradient Reference describes the functions that create CGGradient objects.
CGShading Reference describes the functions that create CGShading objects.
CGFunction Reference describes the functions needed to calculate gradient colors for a CGShading object.
CGContext Reference describes the functions that draw to a context with CGGradient and CGShading objects