一般情況下恭应,項目初期我們對上傳圖片的尺寸是沒有嚴格要求的,因為開始階段功能>優(yōu)化耘眨。但是當用戶數(shù)量逐漸增多昼榛,我們肯定要考慮到用戶體驗,所以對于圖片的上傳剔难,一定對作出硬性的要求胆屿,要求客戶端對用戶選擇的圖片進行壓縮,減少上傳消耗的流量偶宫。
大多數(shù)情況下非迹,都是使用UIImageJPEGRepresentation這個方法對圖片進行壓縮,但是這個壓縮有自己的極限纯趋,測試中一般10M以上的圖片憎兽,壓縮到300K左右就已經(jīng)到頭了。它的優(yōu)點就是壓縮對圖片質(zhì)量的損耗很小吵冒,保證了圖片的質(zhì)量纯命。但是它不能滿足我們對圖片壓縮的硬性要求,比如我們的需求是圖片不能超過50K:
- (void)jkr_tryCompressToDataLength:(NSInteger)length withBlock:(void (^)(NSData *))block {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
CGFloat scale = 0.9;
NSData *scaleData = UIImageJPEGRepresentation(self, scale);
while (scaleData.length > length) {
scale -= 0.1;
if (scale < 0) {
break;
}
scaleData = UIImageJPEGRepresentation(self, scale);
}
dispatch_async(dispatch_get_main_queue(), ^{
block(scaleData);
});
});
}
可以看到利用這種方式壓縮到307K已經(jīng)是極限桦锄,無法滿足我們圖片不能超過50K的要求扎附。
另一種方式就是利用離屏渲染重新繪制圖片,這個方式可以將圖片壓縮到任意大小结耀,比如我們要求的50K留夜,但是它的缺點就是對圖片的質(zhì)量損耗很嚴重:
- (UIImage *)jkr_compressWithWidth:(CGFloat)width {
if (width <= 0 || [self isKindOfClass:[NSNull class]] || self == nil) return nil;
CGSize newSize = CGSizeMake(width, width * (self.size.height / self.size.width));
UIGraphicsBeginImageContext(newSize);
[self drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
- (void)jkr_fastCompressToDataLength:(NSInteger)length withBlock:(void (^)(NSData *))block {
CGFloat scale = 1.0;
UIImage *newImage = [self copy];
NSInteger newImageLength = UIImageJPEGRepresentation(newImage, 1.0).length;
while (newImageLength > length) {
NSLog(@"Do compress");
// 如果限定的大小比當前的尺寸大0.9的平方倍匙铡,就用開方求縮放倍數(shù),減少縮放次數(shù)
if ((double)length / (double)newImageLength < 0.81) {
scale = sqrtf((double)length / (double)newImageLength);
} else {
scale = 0.9;
}
CGFloat width = newImage.size.width * scale;
newImage = [newImage jkr_compressWithWidth:width];
newImageLength = UIImageJPEGRepresentation(newImage, 1.0).length;
}
dispatch_async(dispatch_get_main_queue(), ^{
block(UIImageJPEGRepresentation(newImage, 1.0));
});
}
可以看到,圖片成功的壓縮到了50K以下碍粥,但是圖片的質(zhì)量損耗非常嚴重鳖眼,因為重新繪制并不是對圖片的壓縮處理,它在繪制的同時嚼摩,就因為尺寸的限定钦讳,造成了嚴重的失真效果。
下面是我的思路枕面,為了首先保證圖片的壓縮要求愿卒,重新渲染圖片是必須的,但是盡量減少對圖片的損耗潮秘,所以盡可能縮小渲染的尺寸和原圖尺寸的差琼开,最大可能的使用JEPG壓縮。所以先對圖片進行小幅度的縮放枕荞,再判斷縮放后的圖片壓縮后在極限壓縮的條件是否滿足壓縮條件柜候,如果滿足則通過壓縮獲取圖片。
下面是將一張同樣17.88M的圖片壓縮到50K的效果:
通過這樣的方式躏精,在壓縮尺寸和圖片質(zhì)量中盡量做到平衡渣刷,方法就是先通過UIImageJPEGRepresentation壓縮到最小,如果不能滿足尺寸要求矗烛,則先將圖片重新繪制為稍小尺寸的圖片辅柴,然后再用UIImageJPEGRepresentation壓縮,直到滿足大小要求位置高诺。實際測試這樣可以在將圖片壓縮到非常小的尺寸的同時碌识,讓可以有很高的清晰度碾篡。
代碼如下:
#pragma mark - 圖片根據(jù)寬度重繪
- (UIImage *)jkr_compressWithWidth:(CGFloat)width {
if (width <= 0 || [self isKindOfClass:[NSNull class]] || self == nil) return nil;
CGSize newSize = CGSizeMake(width, width * (self.size.height / self.size.width));
UIGraphicsBeginImageContext(newSize);
[self drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
#pragma mark - 壓縮圖片高質(zhì)量
- (void)jkr_compressToDataLength:(NSInteger)length withBlock :(void (^)(NSData *))block {
if (length <= 0 || [self isKindOfClass:[NSNull class]] || self == nil) {
block(nil);
return;
}
dispatch_async(dispatch_get_global_queue(0, 0), ^{
UIImage *newImage = [self copy];
{
CGFloat clipScale = 0.9;
NSData *pngData = UIImagePNGRepresentation(self);
NSLog(@"Original pnglength %zd", pngData.length);
NSData *jpgData = UIImageJPEGRepresentation(self, 1.0);
NSLog(@"Original jpglength %zd", jpgData.length);
while (jpgData.length > length) {
NSData *newImageData = UIImageJPEGRepresentation(newImage, 0.0);
if (newImageData.length < length) {
CGFloat scale = 1.0;
newImageData = UIImageJPEGRepresentation(newImage, scale);
while (newImageData.length > length) {
scale -= 0.1;
newImageData = UIImageJPEGRepresentation(newImage, scale);
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Result jpglength %zd", newImageData.length);
block(newImageData);
});
return;
} else {
newImage = [newImage jkr_compressWithWidth:newImage.size.width * clipScale];
jpgData = UIImageJPEGRepresentation(newImage, 1.0);
}
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Result jpglength %zd", jpgData.length);
block(jpgData);
});
}
});
}