普通方法有可能爆掉內(nèi)存雌团,這里改一下:
方法一:
/*
*定義一張圖片可以占用的最大空間
*/
//每個像素占用的字節(jié)數(shù)
static const size_t kBytesPerPixel = 4;
//色彩空間占用的字節(jié)數(shù)
static const size_t kBitsPerComponent = 8;
static const CGFloat kDestImageSizeMB = 60.0f;
static const CGFloat kSourceImageTileSizeMB = 20.0f;
static const CGFloat kBytesPerMB = 1024.0f * 1024.0f;
//1MB可以存儲多少像素
static const CGFloat kPixelsPerMB = kBytesPerMB / kBytesPerPixel;
//如果像素小于這個值,則不解壓縮
static const CGFloat kDestTotalPixels = kDestImageSizeMB * kPixelsPerMB;
static const CGFloat kTileTotalPixels = kSourceImageTileSizeMB * kPixelsPerMB;
static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to overlap the seems where tiles meet.
- (UIImage *)downsize:(nullable UIImage *)sourceImage{
// 創(chuàng)建NSAutoreleasePool
@autoreleasepool {
// 獲取圖片扯俱,這個時候是不會繪制
if( sourceImage == nil ) NSLog(@"input image not found!");
// 拿到當前圖片的寬高
CGSize sourceResolution = CGSizeZero;
sourceResolution.width = CGImageGetWidth(sourceImage.CGImage);
sourceResolution.height = CGImageGetHeight(sourceImage.CGImage);
// 當前圖片的像素
float sourceTotalPixels = sourceResolution.width * sourceResolution.height;
// 當前圖片渲染到界面上的大小
float sourceTotalMB = sourceTotalPixels / kPixelsPerMB;
// 獲取當前最合適的圖片渲染大小超陆,計算圖片的縮放比例
float imageScale = kDestTotalPixels / sourceTotalPixels;
// 拿到縮放后的寬高
CGSize destResolution = CGSizeZero;
destResolution.width = (int)( sourceResolution.width * imageScale );
destResolution.height = (int)( sourceResolution.height * imageScale );
// 生成一個rgb的顏色空間
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// 縮放情況下的每一行的字節(jié)數(shù)
int bytesPerRow = kBytesPerPixel * destResolution.width;
// 計算縮放情況下的位圖大小牺弹,申請一塊內(nèi)存
void* destBitmapData = malloc( bytesPerRow * destResolution.height );
if( destBitmapData == NULL ) NSLog(@"failed to allocate space for the output image!");
// 根據(jù)計算的參數(shù)生成一個合適尺寸的位圖
CGContextRef destContext;
destContext = CGBitmapContextCreate( destBitmapData, destResolution.width, destResolution.height, 8, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast );
// 如果生成失敗了釋放掉之前申請的內(nèi)存
if( destContext == NULL ) {
free( destBitmapData );
NSLog(@"failed to create the output bitmap context!");
}
// 釋放掉顏色空間
CGColorSpaceRelease( colorSpace );
// 坐標系轉(zhuǎn)換
CGContextTranslateCTM( destContext, 0.0f, destResolution.height );
CGContextScaleCTM( destContext, 1.0f, -1.0f );
// 分塊繪制的寬度(原始寬度)
CGRect sourceTile = CGRectZero;
sourceTile.size.width = sourceResolution.width;
// 分塊繪制的高度
sourceTile.size.height = (int)( kTileTotalPixels / sourceTile.size.width );
NSLog(@"source tile size: %f x %f",sourceTile.size.width, sourceTile.size.height);
sourceTile.origin.x = 0.0f;
// 繪制到位圖上的寬高
CGRect destTile = CGRectZero;
destTile.size.width = destResolution.width;
destTile.size.height = sourceTile.size.height * imageScale;
destTile.origin.x = 0.0f;
NSLog(@"dest tile size: %f x %f",destTile.size.width, destTile.size.height);
// 重合的像素
float sourceSeemOverlap = (int)((kDestSeemOverlap/destResolution.height)*sourceResolution.height);
//NSLog(@"dest seem overlap: %f, source seem overlap: %f",destSeemOverlap, sourceSeemOverlap);
CGImageRef sourceTileImageRef;
// 分塊繪制需要多少次才能繪制完成
int iterations = (int)( sourceResolution.height / sourceTile.size.height );
int remainder = (int)sourceResolution.height % (int)sourceTile.size.height;
if( remainder ) iterations++;
// 添加重合線條
float sourceTileHeightMinusOverlap = sourceTile.size.height;
sourceTile.size.height += sourceSeemOverlap;
destTile.size.height += kDestSeemOverlap;
// 分塊繪制
for( int y = 0; y < iterations; ++y ) {
// create an autorelease pool to catch calls to -autorelease made within the downsize loop.
@autoreleasepool {
NSLog(@"iteration %d of %d",y+1,iterations);
sourceTile.origin.y = y * sourceTileHeightMinusOverlap + sourceSeemOverlap;
destTile.origin.y = ( destResolution.height ) - ( ( y + 1 ) * sourceTileHeightMinusOverlap * imageScale + kDestSeemOverlap );
// 分塊拿到圖片數(shù)據(jù)
sourceTileImageRef = CGImageCreateWithImageInRect( sourceImage.CGImage, sourceTile );
// 計算繪制的位置
if( y == iterations - 1 && remainder ) {
float dify = destTile.size.height;
destTile.size.height = CGImageGetHeight( sourceTileImageRef ) * imageScale;
dify -= destTile.size.height;
destTile.origin.y += dify;
}
// 繪制到位圖上
CGContextDrawImage( destContext, destTile, sourceTileImageRef );
// 釋放內(nèi)存
CGImageRelease( sourceTileImageRef );
}
}
CGImageRef destImageRef = CGBitmapContextCreateImage(destContext);
CGContextRelease(destContext);
if (destImageRef == NULL) {
return sourceImage;
}
//生成處理結(jié)束以后的圖片
UIImage *destImage = [UIImage imageWithCGImage:destImageRef scale:sourceImage.scale orientation:sourceImage.imageOrientation];
CGImageRelease(destImageRef);
if (destImage == nil) {
return sourceImage;
}
return destImage;
}
}
方法二:
static size_t getAssetBytesCallback(void *info, void *buffer, off_t position, size_t count) {
NSData *rep = (__bridge id)info;
NSError *error = nil;
BDALOG_INFO(@"%@ %@ ", @(position), @(count));
[rep getBytes:(void *)buffer range:NSMakeRange(position, count)];
return count;
}
static void releaseAssetCallback(void *info) { CFRelease(info); }
- (UIImage *)thumbnailForAsset:(NSData *)assetData maxPixelSize:(NSUInteger)size {
NSParameterAssert(self.asset != nil);
NSParameterAssert(size > 0);
CGDataProviderDirectCallbacks callbacks = {
.version = 0,
.getBytePointer = NULL,
.releaseBytePointer = NULL,
.getBytesAtPosition = getAssetBytesCallback,
.releaseInfo = releaseAssetCallback,
};
CGDataProviderRef provider = CGDataProviderCreateDirect((void *)CFBridgingRetain(assetData), [assetData length],
&callbacks); // CGDataProviderCreateWithURL(url);
CGImageSourceRef source = CGImageSourceCreateWithDataProvider(provider, NULL);
CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(source, 0, (__bridge CFDictionaryRef) @{
(NSString *)kCGImageSourceCreateThumbnailFromImageAlways : @YES,
(NSString *)kCGImageSourceThumbnailMaxPixelSize : @(size),
(NSString *)kCGImageSourceCreateThumbnailWithTransform : @YES,
});
CFRelease(source);
CFRelease(provider);
if (!imageRef) {
return nil;
}
UIImage *toReturn = [UIImage imageWithCGImage:imageRef];
CFRelease(imageRef);
return toReturn;
}