Apple Developer Connection
Member Login Log In | Not a Member? Contact ADC

< Previous PageNext Page > Hide TOC

Using a CGShading Object

You set up a gradient by creating a CGShading object calling the function CGShadingCreateAxial or CGShadingCreateRadial, supplying the following parameters:

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:

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.

In this section:

Painting an Axial Gradient Using a CGShading Object
Painting a Radial Gradient Using a CGShading Object


Painting an Axial Gradient Using a CGShading Object

Axial and radial gradients require you to perform similar steps. This example shows 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.


Figure 8-11  An axial gradient that is clipped and painted

An axial gradient that is clipped and painted

To paint the axial gradient shown in the figure, follow these steps:

  1. “Set Up a CGFunction Object to Compute Color Values”

  2. “Create a CGShading Object for an Axial Gradient”

  3. “Clip the Context”

  4. “Paint the Axial Gradient Using a CGShading Object”

  5. “Release Objects”

Set Up a CGFunction Object to Compute Color Values

You can compute color values any way you’d like, as long as your color computation function takes three parameters:

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  A function that computes color component values

 
static void myCalculateShadingValues (void *info,
                            const float *in,
                            float *out)
{
    float v;
    size_t k, components;
    static const float 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  A function that creates a CGFunction object

 
static CGFunctionRef myGetFunction (CGColorSpaceRef colorspace)// 1
{
    size_t components;
    static const float input_value_range [2] = { 0, 1 };
    static const float output_value_ranges [8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
    static const CGFunctionCallbacks callbacks = { 0,// 2
                                &myCalculateShadingValues,
                                NULL };
 
    components = 1 + CGColorSpaceGetNumberOfComponents (colorspace);// 3
    return CGFunctionCreate ((void *) components, // 4
                                1, // 5
                                input_value_range, // 6
                                components, // 7
                                output_value_ranges, // 8
                                &callbacks);// 9
}

Here’s what the code does:

  1. Takes a color space as a parameter.

  2. 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.

  3. Calculates the number of color components in the color space and increments the value by 1 to account for the alpha value.

  4. Passes a pointer to the components value. This value is used by the callback myCalculateShadingValues.

  5. Specifies that 1 is the number of input values to the callback.

  6. Provides an array that specifies the valid intervals for the input. This array contains 0 and 1.

  7. Passes the number of output values, which is the number of color components plus alpha.

  8. 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.

  9. Passes a pointer to the callback structure declared and filled previously.

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  Code that sets up 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);

Clip the Context

When you paint a gradient, Quartz fills the current context. This 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  Code that adds 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);

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

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  A routine that paints an axial gradient using a CGShading object

void myPaintAxialShading (CGContextRef myContext,// 1
                            CGRect bounds)
{
    CGPoint     startPoint,
                endPoint;
    CGAffineTransform myTransform;
   float width = bounds.size.width;
    float 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:

  1. Takes as parameters a graphics context and a rectangle to draw into.

  2. 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 the Quartz draws into. You can think of this coordinate location as x at the far left side and y at 50% from the bottom.

  3. 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.

  4. Creates a color space for device RGB because this routine draws to the display.

  5. Creates a CGFunction object by calling the routine shown in Listing 8-7 and passing the color space you just created.

  6. 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.

  7. 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.

  8. Concatenates the transform you just set up with the graphics context passed to the routine.

  9. Saves the graphics state to enable you to restore this state later.

  10. Sets up a clipping area. This and the next two lines of code 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.

  11. Creates a path. This and the next three lines of code 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.

  12. Paints the gradient to the graphics context, transforming and clipping the gradient as described previously.

  13. Releases objects. This and the next two lines of code release all the objects you created.

  14. 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

This example shows how to use a CGShading object to produce the output shown in Figure 8-12.


Figure 8-12  A radial gradient creating using a CGShading object

A radial gradient creating using a CGShading object

To paint a radial gradient, follow these steps:

  1. “Set Up a CGFunction Object to Compute Color Values.”

  2. “Create a CGShading Object for a Radial Gradient”

  3. “Paint a Radial Gradient Using a CGShading Object”

  4. “Release Objects”

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 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  A function that computes color component values

static void  myCalculateShadingValues (void *info,
                                const float *in,
                                float *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 in “Set Up a CGFunction Object to Compute Color Values.”

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  Code that sets up a CGShading object for a radial gradient

    CGPoint startPoint, endPoint;
    float 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);

Paint a Radial 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.

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;
    float   startRadius,
            endRadius;
    CGAffineTransform myTransform;
    float width = bounds.size.width;
    float 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));
    CGContextSetRGBFillColor (myContext, 1, 1, 1, 1); // 12
    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:

  1. Takes as parameters a graphics context and a rectangle to draw into.

  2. 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 50% from the bottom.

  3. Assigns the radius of the starting circle. You can think of this as 10% of the width of user space.

  4. 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.

  5. 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.

  6. Creates a color space for device RGB because this routine draws to the display.

  7. 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.

  8. 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.

  9. 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.

  10. Concatenates the transform you just set up with the graphics context passed to the routine.

  11. Saves the graphics state to enable you to restore this state later.

  12. Sets up a clipping area. This and the next two lines of code 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.

  13. Paints the gradient to the graphics context transforming the gradient as described previously.

  14. Releases object. This and the next two lines of code release all the objects you created.

  15. 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.



< Previous PageNext Page > Hide TOC


Last updated: 2007-12-11




Did this document help you?
Yes: Tell us what works for you.

It’s good, but: Report typos, inaccuracies, and so forth.

It wasn’t helpful: Tell us what would have helped.
Get information on Apple products.
Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Copyright © 2007 Apple Inc.
All rights reserved. | Terms of use | Privacy Notice