普通方法有可能爆掉内存,这里改一下:
方法一:
/*
*定义一张图片可以占用的最大空间
*/
//每个像素占用的字节数
static const size_t kBytesPerPixel = 4;
//色彩空间占用的字节数
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{
// 创建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();
// 缩放情况下的每一行的字节数
int bytesPerRow = kBytesPerPixel * destResolution.width;
// 计算缩放情况下的位图大小,申请一块内存
void* destBitmapData = malloc( bytesPerRow * destResolution.height );
if( destBitmapData == NULL ) NSLog(@"failed to allocate space for the output image!");
// 根据计算的参数生成一个合适尺寸的位图
CGContextRef destContext;
destContext = CGBitmapContextCreate( destBitmapData, destResolution.width, destResolution.height, 8, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast );
// 如果生成失败了释放掉之前申请的内存
if( destContext == NULL ) {
free( destBitmapData );
NSLog(@"failed to create the output bitmap context!");
}
// 释放掉颜色空间
CGColorSpaceRelease( colorSpace );
// 坐标系转换
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 );
// 分块拿到图片数据
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 );
// 释放内存
CGImageRelease( sourceTileImageRef );
}
}
CGImageRef destImageRef = CGBitmapContextCreateImage(destContext);
CGContextRelease(destContext);
if (destImageRef == NULL) {
return sourceImage;
}
//生成处理结束以后的图片
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;
}
网友评论