UIImage解码优化

UIImage在分配到UIImageView或者UIButton中的时候,系统会自动进行解码,因为缓冲区将图片渲染到屏幕前就需要进行解码,也就是转成位图数据然后显示,例如:
1,imageView.image = xxx;
2,[button setImage:[UIImage imageNamed:@”xxx”] forState:0];
系统进行解码是在主线程进行操作的,解码操作比较耗时,而且需要手动进行解码压缩成位图。

//解码且压缩
- (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image {
    if (image == nil) {
        return image;
    }
    // autorelease 防止内存某一时刻过高,以及内存警告时释放内存
    @autoreleasepool{
        CGImageRef imageRef = image.CGImage;
        //颜色空间
        CGColorSpaceRef colorspaceRef = CGColorSpaceCreateDeviceRGB();
        size_t width = CGImageGetWidth(imageRef);
        size_t height = CGImageGetHeight(imageRef);
        //计算出每行行的像素数
        size_t bytesPerRow = 4 * width;
        // kCGImageAlphaNone is not supported in CGBitmapContextCreate.这里创建的contexts是没有透明因素的(位图图形上下文),为了防止渲染控件的时候直接忽略掉其下方的图层
        CGContextRef context = CGBitmapContextCreate(NULL,width,height,8,bytesPerRow,colorspaceRef,kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast);
        if (context == NULL) {
            CGColorSpaceRelease(colorspaceRef);
            return image;
        }
        // Draw the image into the context and retrieve the new bitmap image without alpha
        CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
        CGImageRef imageRefWithoutAlpha = CGBitmapContextCreateImage(context);
         //image.imageOrientation由于使用CoreGraphics绘制图片的Y坐标和UIKit的Y坐标相反,这时要要把图片正古来
        UIImage *imageWithoutAlpha = [UIImage imageWithCGImage:imageRefWithoutAlpha scale:image.scale orientation:image.imageOrientation];
        CGColorSpaceRelease(colorspaceRef);
        CGContextRelease(context);
        CGImageRelease(imageRefWithoutAlpha);
        return imageWithoutAlpha;
    }
}
//分配到异步线程去执行
- (void)settingImageWithSourceImg:(UIImage *)img complate:(void(^)(UIImage *pImg))complate
{
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        UIImage *cImg = [self decodedImageWithImage:img];
        dispatch_async(dispatch_get_main_queue(), ^{
            if(complate) complate(cImg);
        });
    });
}
//demo
    UIImage *sourceImg = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"IMG_0017" ofType:@"JPG"]];
    UIImageView *imgv = [[UIImageView alloc]initWithFrame:CGRectMake(50, 100, 80, 80)];
    imgv.backgroundColor = [UIColor redColor];
    [self.view addSubview:imgv];
    [self settingImageWithSourceImg:sourceImg complate:^(UIImage *pImg) {
        imgv.image = pImg;
    }];

如果直接给ImageView分配image,那么它的内存为:

如果手动压缩:

测试了一下, 效率快了百分之20左右,可以封装成一个类别,扩展到image里边,遇到本地大图可以进行调用,但是像素比较小的就没必要了,因为内存的大小为:

width*scale*height*scale/tkBytesPerPixel
scale为图像缩放比例,tkBytesPerPixel为RGBA的值,如果不修改图片的色值和透明度,那么RGBA为4。

扩展:

//
//  UIImage+Decode.h
// 
//
//  Created by xxxxx on 2020/11/13.
//

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger , IMAGE_GET_TYPE)
{
    NamedGetType= 1,
    ResourceGetType,
};


@interface UIImage (Decode)
+ (void)decodeImageWithNamed:(NSString *)named complated:(void(^)(UIImage * outImage))complated;
+ (void)decodeImageWithResource:(NSString *)resource ofType:(NSString *)type  complated:(void(^)(UIImage * outImage))complated;
@end

@interface NSImageName : NSObject
@property (nonatomic,assign)IMAGE_GET_TYPE getType;
@property (nonatomic,copy)NSString *named;
@property (nonatomic,copy)NSString *suffix;
@end

NS_ASSUME_NONNULL_END
//
//  UIImage+Decode.m
// 
//
//  Created by xxxxx on 2020/11/13.
//

#import "UIImage+Decode.h"


@implementation UIImage (Decode)

+ (void)decodeImageWithNamed:(NSString *)named complated:(void(^)(UIImage * outImage))complated
{
    NSImageName *nameObj = [NSImageName new];
    nameObj.named = named;
    nameObj.getType = NamedGetType;
    [self asyncDrawImageWithGetNamedObj:nameObj drawComplated:complated];
}

+ (void)decodeImageWithResource:(NSString *)resource ofType:(NSString *)type  complated:(void(^)(UIImage * outImage))complated
{
    NSImageName *nameObj = [NSImageName new];
    nameObj.named = resource;
    nameObj.suffix = type;
    nameObj.getType = ResourceGetType;
    [self asyncDrawImageWithGetNamedObj:nameObj drawComplated:complated];
}

+ (void)asyncDrawImageWithGetNamedObj:(NSImageName *)nameObj drawComplated:(void(^)(UIImage *cImage))drawComplated
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        UIImage *sourceImage = [UIImage imageNamed:nameObj.named];
        switch (nameObj.getType) {
            case ResourceGetType:
                sourceImage = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:nameObj.named ofType:nameObj.suffix]];
                break;
            //....other
            default:
                break;
        }
        UIImage *cImg = [self decodedImageWithImage:sourceImage];
        dispatch_async(dispatch_get_main_queue(), ^{
            if(drawComplated) drawComplated(cImg);
        });
    });
}

+ (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image {
    if (image == nil) {
        return image;
    }
    // autorelease 防止内存某一时刻过高,以及内存警告时释放内存
    @autoreleasepool{
        CGImageRef imageRef = image.CGImage;
        //颜色空间
        CGColorSpaceRef colorspaceRef = CGColorSpaceCreateDeviceRGB();
        size_t width = CGImageGetWidth(imageRef);
        size_t height = CGImageGetHeight(imageRef);
        //计算出每行行的像素数
        size_t bytesPerRow = 4 * width;
        // kCGImageAlphaNone is not supported in CGBitmapContextCreate.这里创建的contexts是没有透明因素的(位图图形上下文),为了防止渲染控件的时候直接忽略掉其下方的图层
        CGContextRef context = CGBitmapContextCreate(NULL,width,height,8,bytesPerRow,colorspaceRef,kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast);
        if (context == NULL) {
            CGColorSpaceRelease(colorspaceRef);
            return image;
        }
        // Draw the image into the context and retrieve the new bitmap image without alpha
        CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
        CGImageRef imageRefWithoutAlpha = CGBitmapContextCreateImage(context);
         //image.imageOrientation由于使用CoreGraphics绘制图片的Y坐标和UIKit的Y坐标相反,这时要要把图片正古来
        UIImage *imageWithoutAlpha = [UIImage imageWithCGImage:imageRefWithoutAlpha scale:image.scale orientation:image.imageOrientation];
        CGColorSpaceRelease(colorspaceRef);
        CGContextRelease(context);
        CGImageRelease(imageRefWithoutAlpha);
        return imageWithoutAlpha;
    }
}


@end

@implementation NSImageName

@end

One Reply to “UIImage解码优化”

Leave a Reply to temed Cancel reply

Your email address will not be published. Required fields are marked *