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

< Previous PageNext Page > Hide TOC

Using Transition Effects

Transitions are typically used between images in a slide show or to switch from one scene to another in video. These effects are rendered over time and require that you set up a timer. This section shows how to set up and apply the copy machine transition filter—CICopyMachine—to two still images. The copy machine transition creates a light bar similar to what you see in a copy machine. The light bar sweeps across the initial image to reveal the target image. Figure 2-5 shows what this filter looks like before, partway through, and after the transition from an image of ski boots to an image of a skier.


Figure 2-5  A copy machine transition from ski boots to a skier

A copy machine transition from ski boots to a skier

Transition filters require the following tasks:

  1. Create Core Image images (CIImage objects) to use for the transition.

  2. Set up and schedule a timer.

  3. Create a CIContext object.

  4. Create a CIFilter object for the filter to apply to the image.

  5. Set the default values for the filter.

  6. Set the filter parameters.

  7. Set the source and the target images to process.

  8. Calculate the time.

  9. Apply the filter.

  10. Draw the result.

  11. Repeat steps 8–10 until the transition is complete.

You’ll notice that many of these tasks are the same as those required to process an image using a filter other than a transition filter. What’s different is the need to set up a timer and to repeatedly draw the effect at various time intervals throughout the transition.

The awakeFromNib method, shown in Listing 2-8, gets two images (boots.jpg and skier.jpg) and sets them as the source and target images. Using the NSTimer class, a timer is set to repeat every 1/30 second. Note the variables thumbnailWidth and thumbnailHeight. These are used to constrain the rendered images to the view set up in Interface Builder.

Note:  The NSAnimation class, introduced in Mac OS X v10.4, implements timing for animation in Cocoa. If you use NSAnimation instead of NSTimer, you can set up more than one slide show to play transitions at the same time, using only one timing device. For more information see the documents NSAnimation Class Reference and Animation Programming Guide for Cocoa. See also the CIAnnotationCIAnnotation sample application.

Listing 2-8  Getting images and setting up a timer

- (void)awakeFromNib
{
    NSTimer    *timer;
    NSURL      *url;
 
    thumbnailWidth  = 340.0;
    thumbnailHeight = 240.0;
 
    url   = [NSURL fileURLWithPath: [[NSBundle mainBundle]
                    pathForResource: @"boots" ofType: @"jpg"]];
    [self setSourceImage: [CIImage imageWithContentsOfURL: url]];
 
    url   = [NSURL fileURLWithPath: [[NSBundle mainBundle]
                    pathForResource: @"skier" ofType: @"jpg"]];
    [self setTargetImage: [CIImage imageWithContentsOfURL: url]];
 
    timer = [NSTimer scheduledTimerWithTimeInterval: 1.0/30.0
                        target: self
                        selector: @selector(timerFired:)
                        userInfo: nil
                        repeats: YES];
 
    base = [NSDate timeIntervalSinceReferenceDate];
    [[NSRunLoop currentRunLoop] addTimer: timer
                                forMode: NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] addTimer: timer
                                forMode: NSEventTrackingRunLoopMode];
}

You set up a transition filter just as you’d set up any other filter. Listing 2-9 uses the method filterWithName: to create the filter. It then calls setDefaults to initialize all input parameters. The code sets the extent to correspond with the thumbnail width and height that is declared in the awakeFromNib: method shown in Listing 2-8.

The routine uses the thumbnail variables to specify the center of the effect. For this example, the center of the effect is the center of the image, but it doesn’t have to be.

Listing 2-9  Setting up the transition filter

- (void)setupTransition
{
    CIVector  *extent;
    float      w,h;
 
    w      = thumbnailWidth;
    h      = thumbnailHeight;
 
    extent = [CIVector vectorWithX: 0  Y: 0  Z: w  W: h];
 
    transition  = [CIFilter filterWithName: @"CICopyMachineTransition"];
    [transition setDefaults];
    [transition setValue: extent
                forKey: @"inputExtent"];
    [transition   retain];
 
}

The drawRect: routine for the copy machine transition effect is shown in Listing 2-10. This routine sets up a rectangle that’s the same size as the view and then sets up a floating-point value for the rendering time. If the CIContext object hasn’t already been created, the routine creates one. If the transition is not yet set up, the routine calls the setupTransition method (see Listing 2-9). Finally, the routine calls the drawImage:atPoint:fromRect: method, passing the image that should be shown for the rendering time. The imageForTransition: method, shown in Listing 2-11, applies the filter and returns the appropriate image for the rendering time.

Listing 2-10  The drawRect: method for the copy machine transition effect

- (void)drawRect: (NSRect)rectangle
{
    float   t;
    CGRect  cg = CGRectMake(NSMinX(rectangle), NSMinY(rectangle),
                    NSWidth(rectangle), NSHeight(rectangle));
 
    t   = 0.4*([NSDate timeIntervalSinceReferenceDate] - base);
    if(context == nil)
    {
        context = [CIContext contextWithCGContext:
                        [[NSGraphicsContext currentContext] graphicsPort]
                             options: nil];
        [context retain];
    }
    if(transition == nil)
        [self setupTransition];
     [context drawImage: [self imageForTransition: t + 0.1]
                         atPoint: cg.origin
                         fromRect: cg];
}

The imageForTransition: method figures out, based on the rendering time, which image is the source image and which one is the target image. It’s set up to allow a transition to repeatedly loop. If your application applies a transition that doesn’t loop, it would not need the if-else construction shown in Listing 2-11.

The routine sets the inputTime value based on the rendering time passed to the imageForTransition: method. It applies the transition, passing the output image from the transition to the crop filter (CICrop). Cropping ensures the output image fits in the view rectangle. The routine returns the cropped transition image to the drawRect: method, which then draws the image.

Listing 2-11  Applying the transition filter

- (CIImage *)imageForTransition: (float)t
{
    CIFilter  *crop;
 
    if(fmodf(t, 2.0) < 1.0f)
    {
        [transition setValue: sourceImage  forKey: @"inputImage"];
        [transition setValue: targetImage  forKey: @"inputTargetImage"];
    }
    else
    {
        [transition setValue: targetImage  forKey: @"inputImage"];
        [transition setValue: sourceImage  forKey: @"inputTargetImage"];
    }
 
    [transition setValue: [NSNumber numberWithFloat:
                            0.5*(1-cos(fmodf(t, 1.0f) * M_PI))]
                forKey: @"inputTime"];
 
    crop = [CIFilter filterWithName: @"CICrop"
                    keysAndValues: @"inputImage",
                            [transition valueForKey: @"outputImage"],
                    @"inputRectangle", [CIVector vectorWithX: 0  Y: 0
                                        Z: thumbnailWidth
                                        W: thumbnailHeight],
                    nil];
    return [crop valueForKey: @"outputImage"];
}

Each time the timer that you set up fires, the display must be updated. Listing 2-12 shows a timerFired: routine that does just that.

Listing 2-12  Using the timer to update the display

- (void)timerFired: (id)sender
{
    [self setNeedsDisplay: YES];
}

Finally, Listing 2-13 shows the housekeeping that needs to be performed if your application switches the source and target images, as the example does.

Listing 2-13  Setting source and target images

- (void)setSourceImage: (CIImage *)source
{
    [source retain];
    [sourceImage release];
    sourceImage = source;
}
 
- (void)setTargetImage: (CIImage *)target
{
    [target retain];
    [targetImage release];
    targetImage = target;
}


< Previous PageNext Page > Hide TOC


Last updated: 2008-06-09




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