画像のトリミングとリサイズ

PoinではGPUImageというライブラリを使っていたのですが、Poin 1.5.0リリースのタイミングでライブラリのバージョンを0.1.2に上げたら、見事にバグりました。1.5.1で修正されます。
そんなわけで、トリムとリサイズをGPUImageなしで書き直したメモ。

## イメージのリサイズ
リサイズのロジックは非常に単純で、UIGraphicsGetCurrentContextを作ってイメージを書くだけです。新しく作成されるイメージのorientationはUIImageOrientationUpになります。

下記はUIImageのカテゴリとして作成することを想定しています。

```objc
- (UIImage*) btk_ImageResized : (CGSize)size
{
    if(CGSizeEqualToSize(self.size, size) && self.imageOrientation == UIImageOrientationUp){
        // No need to resize nor convert orientation
        return self;
    }
    UIGraphicsBeginImageContext(size);
    @try {
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
        [self drawInRect:CGRectMake(0, 0, size.width, size.height)];
        return UIGraphicsGetImageFromCurrentImageContext();
    }
    @finally {
        UIGraphicsEndImageContext();
    }
}
```

## イメージのトリム
イメージトリムを行う場合は、CGImageCreateWithImageInRectを使うのが一番効率が良いと思います。ただ、この場合にはOrientationは元の画像と変わらないので、逆に切取り領域を変換する必要があります。

```objc
- (UIImage*) btk_ImageClipped : (CGRect)rect
{
    // Convert rect to match orientation
    rect = CGRectApplyAffineTransform(rect, self.btk_TransformForOrientation);
    
    CGImageRef cgImage;
    @try {
        cgImage = CGImageCreateWithImageInRect(self.CGImage, rect);
        return [UIImage imageWithCGImage:cgImage
                                   scale:self.scale
                             orientation:self.imageOrientation];
    }
    @finally {
        CGImageRelease(cgImage);
    }
}

- (CGAffineTransform)btk_TransformForOrientation
{
    CGAffineTransform t = CGAffineTransformIdentity;
    CGSize visibleImageSize = self.size;
    CGSize originalImageSize = self.size;
    switch (self.imageOrientation) {
        case UIImageOrientationLeft:          // 90 deg CCW
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:         // 90 deg CW
        case UIImageOrientationRightMirrored:
            originalImageSize = CGSizeMake(visibleImageSize.height, visibleImageSize.width);
            break;
        default:
            originalImageSize = visibleImageSize;
    }
    
    t = CGAffineTransformTranslate(t, originalImageSize.width / 2, originalImageSize.height / 2);
    switch (self.imageOrientation) {
        case UIImageOrientationDownMirrored:
            t = CGAffineTransformScale(t, -1, 1);
        case UIImageOrientationDown:          // 180 deg rotation
            t = CGAffineTransformRotate(t, M_PI);
            break;
        case UIImageOrientationLeftMirrored:
            t = CGAffineTransformScale(t, -1, 1);
        case UIImageOrientationLeft:          // 90 deg CCW
            t = CGAffineTransformRotate(t, M_PI/2);
            break;
        case UIImageOrientationRightMirrored:
            t = CGAffineTransformScale(t, -1, 1);
        case UIImageOrientationRight:         // 90 deg CW
            t = CGAffineTransformRotate(t, -M_PI/2);
            break;
        case UIImageOrientationUpMirrored:
            t = CGAffineTransformScale(t, -1, 1);
        case UIImageOrientationUp:
        default:
            break;
    }
    t = CGAffineTransformTranslate(t, -visibleImageSize.width / 2, -visibleImageSize.height / 2);
    return t;
}
```

## 非同期化
画像処理は重いのでバックグランドで行うことが望ましいです。
このような場合、メインのロジックは同期で書いておき、下記のように非同期化すると使い回しが楽になります。

```objc
- (void) btk_ImageResized : (CGSize)size
                 complete : (void (^)(UIImage *image))complete
{
    if(!complete){
        return;
    }
    if(![NSThread isMainThread]){
        complete([self btk_ImageResized:size]);
        return;
    }
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        UIImage *filteredImage = [self btk_ImageResized:size];
        dispatch_async(dispatch_get_main_queue(), ^
                       {
                           complete(filteredImage);
                       });
    });
}
```