UIImage高效能圓角繪製,壓縮,截圖,幀圖片獲取,修改原有image底色等各種功能持續更新

Deft_MKJing宓珂璟發表於2016-12-18

蒐集一些UIImage的各種使用功能


1.圖片壓縮

- (UIImage *)imageByScalingAndCroppingForSourceImage:(UIImage *)sourceImage targetSize:(CGSize)targetSize
{
    UIImage *newImage = nil;
    CGSize imageSize = sourceImage.size;
    CGFloat width = imageSize.width;
    CGFloat height = imageSize.height;
    CGFloat targetWidth = targetSize.width;
    CGFloat targetHeight = targetSize.height;
    CGFloat scaleFactor = 0.0;
    CGFloat scaledWidth = targetWidth;
    CGFloat scaledHeight = targetHeight;
    CGPoint thumbnailPoint = CGPointMake(0.0,0.0);
    if (CGSizeEqualToSize(imageSize, targetSize) == NO)
    {
        CGFloat widthFactor = targetWidth / width;
        CGFloat heightFactor = targetHeight / height;
        
        if (widthFactor > heightFactor)
            scaleFactor = widthFactor; // scale to fit height
        else
            scaleFactor = heightFactor; // scale to fit width
        scaledWidth  = width * scaleFactor;
        scaledHeight = height * scaleFactor;
        
        // center the image
        if (widthFactor > heightFactor)
        {
            thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
        }
        else
            if (widthFactor < heightFactor)
            {
                thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
            }
    }
    UIGraphicsBeginImageContext(targetSize); // this will crop
    CGRect thumbnailRect = CGRectZero;
    thumbnailRect.origin = thumbnailPoint;
    thumbnailRect.size.width  = scaledWidth;
    thumbnailRect.size.height = scaledHeight;
    
    [sourceImage drawInRect:thumbnailRect];
    
    newImage = UIGraphicsGetImageFromCurrentImageContext();
    if(newImage == nil) NSLog(@"could not scale image");
    
    //pop the context to get back to the default
    UIGraphicsEndImageContext();
    return newImage;
}




2.通過UIView或者UIColor建立UIImage

+ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size
{
    @autoreleasepool {
        CGRect rect = CGRectMake(0, 0, size.width, size.height);
        UIGraphicsBeginImageContext(rect.size);
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextSetFillColorWithColor(context,
                                       color.CGColor);
        CGContextFillRect(context, rect);
        UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        
        return img;
    }
}

+ (UIImage *)imageFromView:(UIView *)theView
{
    UIGraphicsBeginImageContext([theView bounds].size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [[theView layer] renderInContext:context];
    UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return theImage;
}



3.給UIImage增加高斯模糊

- (UIImage*)blurredImage:(CGFloat)blurAmount
{
    if (blurAmount < 0.0 || blurAmount > 1.0) {
        blurAmount = 0.5;
    }
    
    int boxSize = (int)(blurAmount * 40);
    boxSize = boxSize - (boxSize % 2) + 1;
    
    CGImageRef img = self.CGImage;

    vImage_Buffer inBuffer, outBuffer;
    vImage_Error error;
    
    void *pixelBuffer;
    
    CGDataProviderRef inProvider = CGImageGetDataProvider(img);
    CFDataRef inBitmapData = CGDataProviderCopyData(inProvider);
    
    inBuffer.width = CGImageGetWidth(img);
    inBuffer.height = CGImageGetHeight(img);
    inBuffer.rowBytes = CGImageGetBytesPerRow(img);
    
    inBuffer.data = (void*)CFDataGetBytePtr(inBitmapData);

    pixelBuffer = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));
    
    outBuffer.data = pixelBuffer;
    outBuffer.width = CGImageGetWidth(img);
    outBuffer.height = CGImageGetHeight(img);
    outBuffer.rowBytes = CGImageGetBytesPerRow(img);
    
    error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
    
    if (!error) {
        error = vImageBoxConvolve_ARGB8888(&outBuffer, &inBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
    }
    
    if (error) {
#ifdef DEBUG
        NSLog(@"%s error: %zd", __PRETTY_FUNCTION__, error);
#endif
        
        return self;
    }
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    
    CGContextRef ctx = CGBitmapContextCreate(outBuffer.data,
                                             outBuffer.width,
                                             outBuffer.height,
                                             8,
                                             outBuffer.rowBytes,
                                             colorSpace,
                                             (CGBitmapInfo)kCGImageAlphaNoneSkipLast);
    
    CGImageRef imageRef = CGBitmapContextCreateImage (ctx);
    
    UIImage *returnImage = [UIImage imageWithCGImage:imageRef];
    
    CGContextRelease(ctx);
    CGColorSpaceRelease(colorSpace);
    
    free(pixelBuffer);
    CFRelease(inBitmapData);
    
    CGImageRelease(imageRef);
    
    return returnImage;
}



4.截圖生成UIImage

+ (UIImage *)screenshot
{
    CGSize imageSize = [[UIScreen mainScreen] bounds].size;

    UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);

    CGContextRef context = UIGraphicsGetCurrentContext();
    
    for (UIWindow *window in [[UIApplication sharedApplication] windows]) {
        if (![window respondsToSelector:@selector(screen)] || [window screen] == [UIScreen mainScreen]) {
            CGContextSaveGState(context);

            CGContextTranslateCTM(context, [window center].x, [window center].y);

            CGContextConcatCTM(context, [window transform]);
            
            CGContextTranslateCTM(context,
                                  -[window bounds].size.width * [[window layer] anchorPoint].x,
                                  -[window bounds].size.height * [[window layer] anchorPoint].y);
            
            [[window layer] renderInContext:context];
            
            CGContextRestoreGState(context);
        }
    }
    
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    
    UIGraphicsEndImageContext();
    
    return image;
}


5.給UIImage替換原有的底色

- (UIImage *)jsq_imageMaskedWithColor:(UIColor *)maskColor
{
    NSParameterAssert(maskColor != nil);
    
    CGRect imageRect = CGRectMake(0.0f, 0.0f, self.size.width, self.size.height);
    UIImage *newImage = nil;
    
    UIGraphicsBeginImageContextWithOptions(imageRect.size, NO, self.scale);
    {
        CGContextRef context = UIGraphicsGetCurrentContext();
        
        CGContextScaleCTM(context, 1.0f, -1.0f);
        CGContextTranslateCTM(context, 0.0f, -(imageRect.size.height));
        
        CGContextClipToMask(context, imageRect, self.CGImage);
        CGContextSetFillColorWithColor(context, maskColor.CGColor);
        CGContextFillRect(context, imageRect);
        
        newImage = UIGraphicsGetImageFromCurrentImageContext();
    }
    UIGraphicsEndImageContext();
    
    return newImage;
}




6.合成兩張UIImage

// 圖片疊加
+ (UIImage *)addImage:(UIImage *)image1 withImage:(UIImage *)image2 {
    
    UIGraphicsBeginImageContext(image1.size);
    
    [image1 drawInRect:CGRectMake(0, 0, image1.size.width, image1.size.height)];
    
    [image2 drawInRect:CGRectMake((image1.size.width - image2.size.width)/2,(image1.size.height - image2.size.height)/2, image2.size.width, image2.size.height)];
    
    UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
    
    UIGraphicsEndImageContext();
    
    return resultingImage;
}



7.獲取視訊播放的某一幀圖片

// 獲取幀圖片
+ (UIImage*) thumbnailImageForVideo:(NSURL *)videoURL atTime:(NSTimeInterval)ts {

    AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:videoURL options:nil];
    AVAssetImageGenerator *gen = [[AVAssetImageGenerator alloc] initWithAsset:asset];
    
    gen.appliesPreferredTrackTransform = YES;
    CMTime time = CMTimeMakeWithSeconds(ts, 600);
    NSError *error = nil;
    CMTime actualTime;
    CGImageRef image = [gen copyCGImageAtTime:time actualTime:&actualTime error:&error];
    UIImage *img = [[UIImage alloc] initWithCGImage:image];
    CGImageRelease(image);
    return img;
    
}



8.流暢的tableView優化之非同步處理圓角

- (void)mkj_cornerImageWithSize:(CGSize)size fillColor:(UIColor *)color completion:(block)completionBlock
{
    /**
     非同步繪製圓角圖片
     */
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSTimeInterval time = CACurrentMediaTime();
        // 1.建立上下文  第二個引數就是不透明  第三個引數scale是0,檢視透明會增加GPU的計算
        UIGraphicsBeginImageContextWithOptions(size, YES, 0);
        
        
        CGRect rec = CGRectMake(0, 0, size.width, size.height);
        
        // 2.讓裁減掉的多餘填充部分為白色,不然是黑色的
        [color setFill];
        UIRectFill(rec);
        // 3.BezierPath建立裁減路徑
        UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rec];
        [path addClip];
        
        // 4.繪製
        [self drawInRect:rec];
        
        
        // 5.從上下文取圖片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        
        // 6.關閉上下文
        UIGraphicsEndImageContext();
        
        NSLog(@"%lf",CACurrentMediaTime()-time);
        // 7.回撥到主執行緒
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completionBlock) {
                completionBlock(image);
            }
            
        });
    });
}



注意:這裡有個模擬器上的檢視優化知識點


1.螢幕上的每個畫素點的顏色是由當前畫素點上的多層layer(如果存在)共同決定的,GPU會進行計算出混合顏色的RGB值,最終顯示在螢幕上。而這需要讓GPU計算,所以我們要儘量避免設定alpha,這樣GPU會忽略下面所有的layer,節約計算量

如果你兩個檢視,相互遮蓋,上面的檢視如果是由alpha的,那麼開啟這個屬性你的View會是紅色的,而且用傳統的cornerRadius也會是紅色的,綠色是沒有效能問題,紅色有,那麼UILabel比較特殊,沒得玩,其他還是可以改改的


2.蘋果的GPU只解析32bit的顏色格式,記住是32bit


3.不要出現image sizeimageView size不同的情況,這樣會觸發反鋸齒計算,增加效能損耗,本地圖片好處理,可以直接看到大小,但是網路圖片載入時可以根據容器的大小傳參獲取,或者下載下來進行壓縮,不然會觸發反鋸齒計算


4.離屏渲染,這個完全不知道是啥,反正就是離屏渲染會導致CPU在後臺儲存一份bitmap,所以會導致CPU多餘運算

當drawInRect或者MaskToBounds會觸發,儘量避免,應該是cell還沒有滑進螢幕的時候進行提前繪製


1.常規做法

imageView.layer.cornerRadius = imageView.bounds.size.width/2;
    imageView.layer.masksToBounds = YES;
    imageView.image = image;


紅色警告,需要優化



2.非同步做圓角 程式碼在上面



紅色沒了,完美搞定,tableview滑動的時候就不會一直重繪了,絲滑絲滑的



8.微博,糗事百科這一類釋出的長圖片如何壓縮只顯示頂部區域,點開展示大圖

// 開啟圖形上下文  給定一個尺寸框框,無論下面那句話畫出來的新的image是多大,都根據這個尺寸框進行獲取,一般獲取超級長圖頭部圖片的方法
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(topic.picFrame.size.width, topic.picFrame.size.height), YES, 0.0);
        
        // 根據大小繪製  這裡給一個圖片壓縮後的尺寸,這個時候是等比例長度縮放的,而且是全尺寸長度的
        CGFloat width = topic.picFrame.size.width;
        CGFloat height = topic.picFrame.size.width * image.size.height / image.size.width;
        [image drawInRect:CGRectMake(0, 0, width, height)];
        
        
        // 從圖形上下文獲取圖片
        self.backImageView.image = UIGraphicsGetImageFromCurrentImageContext();
        
        
        // 結束圖形上下文
        UIGraphicsEndImageContext();


這裡不封裝方法了,直接寫出來看看,首先開啟的圖形上下文就是所要展示的區域大小,然後繪製的時候其實是等比例縮放,寬度是一樣的,但是長度是很長的,最後取出來的時候是根據圖形上下文的rect來取的,所以展示在最外層的就是頂部小小部分


看下實際需求點:

當我們在頭部展示的時候是顯示頂部圖片,然後點開就是全部圖片的展示,不然直接設定圖片的contentMode,大圖的時候顯示位置會有問題

   




持續更新。。。。。。。。

相關文章