Most performant way to change a uiimage saturation (during animation)

1k views Asked by At

I am trying to change a UIImage saturation during an animation. But it seems that it is too heavy/slow to do so this way:

-(void) changeSaturation:(CGFloat)value{

CIContext * context = [CIContext contextWithOptions:nil];

CIFilter *colorControlsFilter = [CIFilter filterWithName:@"CIColorControls"];

 CIImage *image = self.imageView.image.CIImage;

[colorControlsFilter setValue:image forKey:@"inputImage"];

[colorControlsFilter setValue:[NSNumber numberWithFloat:value] forKey:@"inputSaturation"];

CIImage *outputImage = [colorControlsFilter outputImage];

CGImageRef cgimg = [context createCGImage:outputImage
                                 fromRect:[outputImage extent]];

UIImage *newImage = [UIImage imageWithCGImage:cgimg];
self.imageView.image = newImage;

CGImageRelease(cgimg);}

This method is called everytime the view is dragged by the user (when the distance changes, the saturation changes as well), which obviously is not the right way to do it but I would like a similar behavior. I wonder if I can achieve this with a layer on top of the UIImageview.

Can anyone advise me on how to achieve my goal?

3

There are 3 answers

0
jonypz On BEST ANSWER

Turns out my main issue was that the view being dragged contained a big resolution image. My solution above works perfectly fine.

2
user2828120 On

1) Calculate filtered image in async method:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{

        UIImage * filtredImage = /*Your method*/

        dispatch_async(dispatch_get_main_queue(), ^{
             self.imageView.image = filtredImage;
        }
}

2) Smooth change image: use coreAnimation. self.layer - is top CALayer in your view

- (UIImage*)changeSaturation:(CGFloat)value
{
     //Your filter process
}
- (void)changeSaturation:(CGFloat)value duration:(CGFloat)duration
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{

            NSUInteger cadreCount = ceil(duration * 60);
            CGFloat incrimentValue = value / cadreCount;

            NSMutableArray * mutArrayAnimationValues = [NSMutableArray new];

            for (int cadreIndex = 0; cadreIndex <= cadreCount; cadreIndex++)
            {
                  CGFloat currentValue = incrimentValue * cadreIndex;
                  UIImage * imageForCurrentCadr = [self changeSaturation:currentValue];
                  [mutArrayAnimationValues addObject:(id)imageForCurrentCadr.CGImage];
            }

            dispatch_async(dispatch_get_main_queue(), ^{

                 self.layer.contents = (id)[mutArrayAnimationValues lastObject];

                 CAKeyframeAnimation *animation = [CAKeyframeAnimation               animationWithKeyPath:@"contents"];
                 [animation setValues:mutArrayAnimationValues];
                 [animation setDuration:duration];
                 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
                 [animation setFillMode:kCAFillModeBoth];
                 [pathAnimation setRemovedOnCompletion:NO];
                 [self.layer addAnimation:pathAnimation forKey:@"imageAnimation"];
            }
    }
}
0
Tim Bernikovich On

If you change saturation between 0.0 and 1.0, you can add Black&White (Grayscale) image above normal image and change it's alpha. It will be most performant.