【IOS開發高級系列】異步繪製專題

1 圖片處理

1.1 編輯圖片的幾個方法

第一種

    先用UIImage對象加載一張圖片

    然後轉化成CGImageRef放到CGContext中去編輯


第二種

    用CGImageCreate函數創建CGImageRef

    然後把CGImageRef放到CGContext中去編輯


第三種

    用CGImageCreateCopy 或者CGImageCreateCopyWithColorSpace函數拷貝

CGImageRefCGImageCreate (

    size_t width, //圖片的寬度

    size_t height, //圖片的高度

    size_t bitsPerComponent,  //圖片每個顏色的bits,比如rgb顏色空間,有可能是5 或者8 ==

    size_t bitsPerPixel,  //每一個像素佔用的buts,15 位24位 32位等等

    size_t bytesPerRow, //每一行佔用多少bytes 注意是bytes不是bits  1byte =8bit

    CGColorSpaceRef colorspace,  //顏色空間,比如rgb

    CGBitmapInfo bitmapInfo,  //layout,像素中bit的佈局, 是rgba還是 argb,==

    CGDataProviderRef provider,  //數據源提供者,url或者內存==

    const CGFloat decode[],  //一個解碼數組

    bool shouldInterpolate,  //抗鋸齒參數

    CGColorRenderingIntent intent

    //圖片渲染相關參數

);

1.2 示例代碼

CGImageRef CGImageCreate(size_t width, size_theight, size_tbitsPerComponent, size_t bitsPerPixel, size_tbytesPerRow, CGColorSpaceRef space, CGBitmapInfo bitmapInfo, CGDataProviderRefprovider, const CGFloat decode[], boolshouldInterpolate, CGColorRenderingIntent intent);

    通過這個方法,我們可以創建出一個CGImageRef類型的對象,下面分別對參數進行解釋:

    sizt_t是定義的一個可移植性的單位,在64位機器中爲8字節,32位位4字節。

    width:圖片寬度像素

    height:圖片高度像素

    bitsPerComponent:每個顏色的比特數,例如在rgba-32模式下爲8

    bitsPerPixel:每個像素的總比特數

    bytesPerRow:每一行佔用的字節數,注意這裏的單位是字節

    space:顏色空間模式,例如const CFStringRef kCGColorSpaceGenericRGB 這個函數可以返回一個顏色空間對象。

    bitmapInfo:位圖像素佈局,枚舉如下:

typedef CF_OPTIONS(uint32_t, CGBitmapInfo) {

  kCGBitmapAlphaInfoMask = 0x1F,

  kCGBitmapFloatComponents = (1 << 8),

  kCGBitmapByteOrderMask = 0x7000,

  kCGBitmapByteOrderDefault = (0 << 12),

  kCGBitmapByteOrder16Little = (1 << 12),

  kCGBitmapByteOrder32Little = (2 << 12),

  kCGBitmapByteOrder16Big = (3 << 12),

  kCGBitmapByteOrder32Big = (4 << 12)

}

    provider:數據源提供者

    decode[]:解碼渲染數組

    shouldInterpolate:是否抗鋸齒

    intent:圖片相關參數


CGImageRef CGImageMaskCreate(size_t width, size_theight, size_t bitsPerComponent, size_t bitsPerPixel, size_tbytesPerRow, CGDataProviderRef provider, const CGFloat decode[], boolshouldInterpolate)

    這個方法用於創建mask圖片圖層,可以設置其顯示部分與不顯示部分達到特殊的效果,參數意義同上。

CGImageRef CGImageCreateCopy(CGImageRefimage)

    這個方法可以複製一個CGImageRef對象

CGImageRef CGImageCreateWithJPEGDataProvider(CGDataProviderRef source, const CGFloat decode[], boolshouldInterpolate, CGColorRenderingIntent intent)

    通過JPEG數據源獲取圖像

CGImageRef CGImageCreateWithPNGDataProvider(CGDataProviderRefsource, const CGFloat decode[], boolshouldInterpolate, CGColorRenderingIntent intent)

    通過PNG數據源獲取圖像

CGImageRef CGImageCreateWithImageInRect(CGImageRefimage, CGRectrect)

    截取圖像的一個區域重繪圖像

CGImageRef CGImageCreateWithMask(CGImageRef image, CGImageRefmask)

    截取mask圖像的某一區域重繪

CGImageRef CGImageCreateWithMaskingColors(CGImageRefimage, const CGFloatcomponents[])

    通過顏色分量數組創建位圖

CGImageRef CGImageCreateCopyWithColorSpace(CGImageRefimage, CGColorSpaceRef space)

    通過顏色空間模式複製位圖

CGImageRef CGImageRetain(CGImageRefimage)

    引用+1

void CGImageRelease(CGImageRefimage)

    引用-1

bool CGImageIsMask(CGImageRefimage)

    返回是否爲Mask圖層

size_t CGImageGetWidth(CGImageRefimage)

    獲取寬度像素

size_t CGImageGetHeight(CGImageRefimage)

獲取高度像素


下面這些方法分別獲取相應屬性

size_t CGImageGetBitsPerComponent(CGImageRefimage)

size_t CGImageGetBitsPerPixel(CGImageRefimage)

size_t CGImageGetBytesPerRow(CGImageRefimage)

CGColorSpaceRef CGImageGetColorSpace(CGImageRef image)CG_EXTERN CGImageAlphaInfo CGImageGetAlphaInfo(CGImageRefimage)

CGDataProviderRef CGImageGetDataProvider(CGImageRefimage)

const CGFloat *CGImageGetDecode(CGImageRefimage)

bool CGImageGetShouldInterpolate(CGImageRefimage)

CGColorRenderingIntent CGImageGetRenderingIntent(CGImageRefimage)

CGBitmapInfo CGImageGetBitmapInfo(CGImageRefimage)


1.3 PNG與JPEG優劣比較

    存儲速度:JPG更快

    壓縮比:JPG更大;

    圖片質量:JPG更好

    JPG不支持透明效果;    

    UIImageJPEGRepresentation方法在耗時上比較少 而UIImagePNGRepresentation耗時操作時間比較長;

    UIImageJPEGRepresentation函數需要兩個參數:圖片的引用和壓縮係數.而UIImagePNGRepresentation只需要圖片引用作爲參數.通過在實際使用過程中,比較發現: UIImagePNGRepresentation(UIImage* image) 要比UIImageJPEGRepresentation(UIImage* image, 1.0) 返回的圖片數據量大很多.譬如,同樣是讀取攝像頭拍攝的同樣景色的照片, UIImagePNGRepresentation()返回的數據量大小爲199K ,而 UIImageJPEGRepresentation(UIImage* image, 1.0)返回的數據量大小隻爲140KB,比前者少了50多KB.如果對圖片的清晰度要求不高,還可以通過設置 UIImageJPEGRepresentation函數的第二個參數,大幅度降低圖片數據量.譬如,剛纔拍攝的圖片, 通過調用UIImageJPEGRepresentation(UIImage* image, 1.0)讀取數據時,返回的數據大小爲140KB,但更改壓縮係數後,通過調用UIImageJPEGRepresentation(UIImage* image, 0.5)讀取數據時,返回的數據大小隻有11KB多,大大壓縮了圖片的數據量 ,而且從視角角度看,圖片的質量並沒有明顯的降低.因此,在讀取圖片數據內容時,建議優先使用UIImageJPEGRepresentation,並可根據自己的實際使用場景,設置壓縮係數,進一步降低圖片數據量大小.


1.4 圖片縮放

圖片縮放的三個函數

http://www.cnblogs.com/pengyingh/articles/2355052.html

        程序中一個界面用到了好多張大圖,內存報警告了,所以做了一下圖片縮放,在網上找了別人寫的代碼

//把圖片做等比縮放,生成一個新圖片

- (UIImage *)imageByScalingProportionallyToSize:(CGSize)targetSize sourceImage:(UIImage*)sourceImage {

//    UIGraphicsBeginImageContext(targetSize);

//    [sourceImage drawInRect:CGRectMake(0,0, targetSize.width, targetSize.height)];

//    UIImage* scaledImage =UIGraphicsGetImageFromCurrentImageContext();

//    UIGraphicsEndImageContext();

//    return scaledImage;

    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);


   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 thecontext to get back to the default

       UIGraphicsEndImageContext();

       return newImage;

}

//把圖片按照新大小進行裁剪,生成一個新圖片

- (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize image:(UIImage *)sourceImage

{

    //    UIImage*sourceImage = self;

    UIImage*newImage = nil;

   CGSizeimageSize = sourceImage.size;

   CGFloat width= imageSize.width;

   CGFloat height= imageSize.height;

   CGFloattargetWidth = targetSize.width;

   CGFloattargetHeight = 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;


       // centerthe 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 thecontext to get back to the default

   UIGraphicsEndImageContext();

   return newImage;

}


- (UIImage*)generatePhotoThumbnail:(UIImage *)image

{

    // Create a thumbnail version of the image for the eventobject.

    CGSize size =image.size;

    CGSize croppedSize;

    CGFloat ratio=64.0;//這個是設置轉換後圖片的尺寸大小

     CGFloat offsetX =0.0;

     CGFloat offsetY =0.0;


    // check the size of the image, we want to make it

    // a square with sides the size of the smallest dimension

   if(size.width > size.height) {

       offsetX = (size.height - size.width) /2;

       croppedSize = CGSizeMake(size.height, size.height);

   }else{

       offsetY = (size.width - size.height) /2;

       croppedSize = CGSizeMake(size.width, size.width);

   }


   // Crop the image before resize

   CGRect clippedRect = CGRectMake(offsetX *-1, offsetY * -1, croppedSize.width, croppedSize.height);

    //裁剪圖片

    CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], clippedRect);

   // Donecropping


    // Resize the image

    CGRect rect = CGRectMake(0.0,0.0,ratio, ratio);

    UIGraphicsBeginImageContext(rect.size);

    [[UIImage imageWithCGImage: imageRef] drawInRect: rect];

    UIImage *thumbnail =UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    // DoneResizing


    return thumbnail;

}


實際應用簡化

-(UIImage *)generatePhotoThumbnail:(UIImage *)image

{

    CGRect rect=CGRectMake(0,0,60,78);

    //裁剪圖片

    CGImageRefimageRef=CGImageCreateWithImageInRect([image CGImage], CGRectMake(0,0,140,182));


    UIGraphicsBeginImageContext(rect.size);

    [[UIImage imageWithCGImage:imageRef]drawInRect:rect];

    //如果不裁剪圖片可以直接畫

    //[image drawInRect:CGRectMake(0, 0,theSize.width, theSize.height)];

    UIImage*thumbnail=UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    returnthumbnail;

}


附:

UIImage類並沒有提供縮放圖片需要用到的API,是不是覺得很吃驚?沒關係,我們自己來添加一個。

定義縮放圖片的Category

//  UIImage+Scale.h

@interfaceUIImage (scale)

-(UIImage*)scaleToSize:(CGSize)size;

@end


實現這個Category的定義

// UIImage+Scale.m 


#import"UIImage+Scale.h"

@implementation UIImage (scale)

-(UIImage*)scaleToSize:(CGSize)size

{

    // 創建一個bitmap的context

    // 並把它設置成爲當前正在使用的context

    UIGraphicsBeginImageContext(size);

    // 繪製改變大小的圖片

    [self drawInRect: CGRectMake(0,0, size.width, size.height)];


     // 從當前context中創建一個改變大小後的圖片

     UIImage* scaledImage =UIGraphicsGetImageFromCurrentImageContext();


      // 使當前的context出堆棧

      UIGraphicsEndImageContext();

      // 返回新的改變大小後的圖片

    return scaledImage;

}

@end


如何使用

// 創建圖片

UIImage*image =[UIImage imageNamed:@"myImage.png"];

// 更改圖片大小

UIImage *scaledImage=[image scaleToSize:CGSizeMake(25.0f,35.0f)]


1.5 參考鏈接

IOS-圖片操作集合

http://blog.csdn.net/ch_soft/article/details/7685753

UIImagePNGRepresentation存在緩慢問題

http://blog.sina.com.cn/s/blog_95a3991f010162ws.html

UIImage變爲NSData並進行壓縮

http://www.cnblogs.com/robinkey/archive/2013/01/21/2869930.html

UIImageJPEGRepresentation和UIImagePNGRepresentation

http://blog.csdn.net/mideveloper/article/details/11473627

png有透明通道,JPEG無

http://blog.163.com/chentong1115@126/blog/static/45314732200972323921819/

透明PNG圖片有黑邊的解決方法

http://www.minroad.com/?p=9

用UIImage和UIButton畫出的按鈕,使用透明的png圖片,爲什麼會出現白邊

http://segmentfault.com/q/1010000000095447

JPG、PNG和GIF圖片的基本原理及優化方法

http://www.mahaixiang.cn/Photoshop/400.html

JPEG原理詳細

http://blog.chinaunix.net/uid-27002868-id-3220554.html

IOS開發中圖片資源使用png還是jpg格式

http://www.cnblogs.com/wengzilin/p/3485298.html

(good)ios開發圖片格式的選擇:png和jpg

http://m.blog.csdn.net/blog/awaylin113/22712317

IOS開發之保存圖片到Documents目錄及PNG,JPEG格式相互轉換

http://blog.csdn.net/sanpintian/article/details/7418755

iOS過濾png圖片透明部分點擊事件

http://www.cocoachina.com/industry/20121127/5192.html

JPEG壓縮原理

http://blog.csdn.net/xfortius/article/details/8904012

png壓縮原理

http://blog.csdn.net/zykun/article/details/1825086

iOS開發,圖片使用png好還是jpg好?

http://www.cocoachina.com/bbs/read.php?tid=110115


2 繪製文本

2.1 NSMutableAttributedString繪製

CGRect textViewRect = CGRectMake(ICON_SPACE, _imageHeight + ICON_SPACE, _postContentTextView.frame.size.width, _labelSize);

NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString: vm.contentText];

[str addAttribute:NSForegroundColorAttributeName value: kContentTextColor range:NSMakeRange(0,[vm.contentText length])];

[str addAttribute: NSFontAttributeName value: kContentTextFont range:NSMakeRange(0,[vm.contentText length])];

[str addAttribute: NSBackgroundColorDocumentAttribute value: [UIColor whiteColor] range: NSMakeRange(0, [vm.contentText length])];

[str drawInRect: textViewRect];


2.2 參考資料

IOS開發(78)之繪製文本

http://www.2cto.com/kf/201305/212045.html

iOS界面上繪製不同字體 顏色 大小的字符串

http://blog.csdn.net/wsk_123_123/article/details/23277457

初探NSAttributedString和NSMutableAttributedString的使用-LiuWJ

http://www.tuicool.com/articles/Fvqia2

iOS字符屬性NSAttributedString描述

http://my.oschina.net/lanrenbar/blog/395909

NSAttributedString詳解

http://www.cnblogs.com/zhw511006/archive/2012/09/21/2696700.html


3 異步繪製

3.1 異步繪製示例

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

       CGRect drawRect = _bgImageView.frame;

       UIGraphicsBeginImageContextWithOptions(drawRect.size, YES, 0);

       CGContextRef context = UIGraphicsGetCurrentContext();


       if(!context) {

           return;

       }


       [[UIColor whiteColor] set];

       CGContextFillRect(context, drawRect);


       CGRect imgRect = CGRectZero;

       if ([vm.contentImgPath length] > 0) {

           imgRect =CGRectMake(0, 0, BODY_HEIGTH, _imageHeight);

           [vm.contentImage drawInRect:  contentImageView.frame blendMode: kCGBlendModeNormal alpha:1];

       }


       CGRect textViewRect = CGRectZero;

        if ([vm.contentText length] > 0) {

            NSMutableAttributedString*str;

            if (!isContentDisplayCompletly) {

                if (vm.digestText) {

                    str = [[NSMutableAttributedString alloc] initWithString: vm.digestText attributes: contentTextView.typingAttributes];

                }else

                    str = [[NSMutableAttributedString alloc] initWithString: vm.contentText attributes: contentTextView.typingAttributes];

            }else

                str = [[NSMutableAttributedString alloc] initWithString: vm.contentText attributes: contentTextView.typingAttributes];

            [strdrawInRect: contentTextView.frame];

        }


        if (_subjectTitleHeight> 0) {

            CGRect subjectIconFrame = CGRectMake(_subjectButton.frame.origin.x, _subjectButton.frame.origin.y, 16, 16);

            UIImage*iconImg = [UIImage imageNamed:@"FlagIcon"];

            subjectIconFrame.size = iconImg.size;

            [iconImg drawInRect: subjectIconFrame blendMode: kCGBlendModeNormal alpha:1];

            CGRect subjectTitleFrame = CGRectMake(subjectIconFrame.origin.x + subjectIconFrame.size.width, subjectIconFrame.origin.y, 100, _subjectTitleHeight);

            [_subjectButton.titleLabel.attributedText drawInRect: subjectTitleFrame];

        }


        UIImage*temp = UIGraphicsGetImageFromCurrentImageContext();

        UIGraphicsEndImageContext();


        dispatch_async(dispatch_get_main_queue(),^{

            _bgImageView.image = nil;

            _bgImageView.image=temp;

            [self setHidden:NO];

        });

    });


3.2 DrawRect之後注意用hitTest:withEvent:方法處理事件接收

//用戶觸摸時第一時間加載內容

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event{

    UIView*result = [super hitTest: point withEvent: event];

    CGPoint buttonPoint = [_subjectButton convertPoint: point fromView: self];

    if ([_subjectButton pointInside:buttonPointwithEvent:event]){

        return _subjectButton;

    }

    returnresult;

}


3.3 參考鏈接

[iOS Animation]-CALayer繪圖效率-異步繪製

http://my.oschina.net/u/2438875/blog/507545?fromerr=R4LnEaJ5

CGDataProviderCreateWithData對內存數據的釋放

http://www.taofengping.com/2012/11/04/cgdataprovidercreatewithdata_memory_release/#.VnJQ6jaitZF

IOS中使用像素位圖(CGImageRef)對圖片進行處理

http://my.oschina.net/u/2340880/blog/406437?p={{currentPage-1}}


4 Asyncdisplaykit

4.1 參考鏈接

Asyncdisplaykit指南(一)

http://www.th7.cn/Program/IOS/201410/302413.shtml

AsyncDisplayKit教程:達到60FPS的滾動幀率

http://www.cocoachina.com/swift/20141124/10298.html

http://asyncdisplaykit.org/guide/

AsyncDisplayKit入門指南

http://www.cocoachina.com/ios/20141020/9975.html


5 開發技巧

5.1 常見問題

5.1.1 CGBitmapContextCreateImage繪製後內存泄露導致內存告警

        CGBitmapContextCreateImage繪製的圖片會造成內存無法釋放,應該換用CGDataProviderCreateWithCFData。

5.1.1.1 方案一:修改源代碼,入緩存前壓縮

http://my.oschina.net/u/1244672/blog/510379

SDWebImage有一個SDWebImageDownloaderOperation類來執行下載操作的。裏面有個下載完成的方法:

- (void)connectionDidFinishLoading:(NSURLConnection*)aConnection {

    SDWebImageDownloaderCompletedBlock completionBlock = self.completedBlock;

    @synchronized(self) {

        CFRunLoopStop(CFRunLoopGetCurrent());

        self.thread = nil;

        self.connection= nil;

        [[NSNotificationCenter defaultCenter] postNotificationName: SDWebImageDownloadStopNotificati onobject: nil];

    }


    if (![[NSURLCache sharedURLCache] cachedResponseForRequest:_request]) {

        responseFromCached= NO;

    }


    if(completionBlock)

    {

    if(self.options & SDWebImageDownloaderIgnoreCachedResponse &&responseFromCached) {

        completionBlock(nil, nil, nil, YES);

    }

    else {

        UIImage *image= [UIImage sd_imageWithData: self.imageData];

        NSString *key= [[SDWebImageManager sharedManager] cacheKeyForURL: self.request.URL];

        image = [self scaledImageForKey: key image: image];


        // Do notforce decoding animated GIFs

        if (!image.images) {

            image = [UIImage decodedImageWithImage: image];

        }

        if (CGSizeEqualToSize(image.size, CGSizeZero)) {

            completionBlock(nil, nil, [NSError errorWithDomain:@"SDWebImageErrorDomain" code:0userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0pixels"}], YES);

        }

        else {

            completionBlock(image, self.imageData, nil, YES);

        }

    }

}

self.completionBlock= nil;

[self done];

}


其中,UIImage*image = [UIImage sd_imageWithData:self.imageData];就是將data轉換成image。

再看看sd_imageWithData:這個方法:

+ (UIImage*)sd_imageWithData:(NSData *)data {

    UIImage*image;

    NSString*imageContentType = [NSData sd_contentTypeForImageData: data];


    if ([imageContentType isEqualToString:@"image/gif"]) {

        image =[UIImage sd_animatedGIFWithData: data];

    }

#ifdefSD_WEBP

    else if([imageContentType isEqualToString:@"image/webp"])

    {

        image =[UIImage sd_imageWithWebPData: data];

    }

#endif

    else {

        image =[[UIImage alloc] initWithData: data];

        UIImageOrientation orientation = [self sd_imageOrientationFromImageData: data];

        if(orientation != UIImageOrientationUp) {

            image =[UIImage imageWithCGImage: image.CGImage scale: image.scale orientation: orientation];

        }

    }

    return image;

}

        這個方法在UIImage+MultiFormat裏面,是UIImage的一個類別處理。這句話很重要image =[[UIImage alloc] initWithData: data]; SDWebImage把下載下來的data直接轉成image,然後沒做等比縮放直接存起來使用。所以,我們只需要在這邊做處理即可:

UIImage+MultiFormat添加一個方法:

+(UIImage*)compressImageWith:(UIImage *)image

{

    float imageWidth = image.size.width;

    float imageHeight = image.size.height;

    float width =640;

    float height =image.size.height/(image.size.width/width);

    float widthScale = imageWidth /width;

    float heightScale = imageHeight /height;


    // 創建一個bitmap的context

    // 並把它設置成爲當前正在使用的context

    UIGraphicsBeginImageContext(CGSizeMake(width, height));


    if (widthScale> heightScale) {

        [image drawInRect: CGRectMake(0, 0, imageWidth /heightScale , height)];

    }

    else {

        [image drawInRect: CGRectMake(0, 0, width , imageHeight /widthScale)];

    }


    // 從當前context中創建一個改變大小後的圖片

    UIImage*newImage = UIGraphicsGetImageFromCurrentImageContext();

    // 使當前的context出堆棧

    UIGraphicsEndImageContext();


    return newImage;

}


    然後在:image =[[UIImage alloc] initWithData: data];下面調用以下:

if (data.length/1024 > 1024) {

    image = [self compressImageWith: image];

}


    當data大於1M的時候做壓縮處理。革命尚未成功,還需要一步處理。在SDWebImageDownloaderOperation的connectionDidFinishLoading方法裏面的:

UIImage *image= [UIImage sd_imageWithData: self.imageData];

//將等比壓縮過的image在賦在轉成data賦給self.imageData

NSData *data =UIImageJPEGRepresentation(image, 1);

self.imageData = [NSMutableData dataWithData: data];


5.1.1.2 方案二:設置全局緩存大小

http://www.myexception.cn/swift/2033029.html

1、首先在appdelegate方法didFinishLaunchingWithOptions

SDImageCache.sharedImageCache().maxCacheSize=1024*1024*8設置一下最大的緩存大小。

2、在appdelegate applicationDidReceiveMemoryWarning里加入

SDImageCache.sharedImageCache().clearMemory()

SDWebImageManager.sharedManager().cancelAll()


5.1.1.3 方案三:定時清理內存緩存

http://www.bubuko.com/infodetail-956863.html

    經過嘗試,發現了一個最簡單的完美解決該問題的方法


    在使用SDWebImage加載較多圖片造成內存警告時,定期調用

 [[SDImageCache sharedImageCache] setValue:nilforKey:@"memCache"];


5.1.1.4 方案四(不推薦):修復SD庫代碼,不做解壓,直接返回壓縮的原圖


5.1.1.5 方案五(推薦):使用CGDataProviderRef進行圖形解壓重繪

iOS開發中界面展示大圖片時UIImage的性能有關問題

http://www.myexception.cn/operating-system/578931.html

#import "SDWebImageDecoder.h"


@implementationUIImage (ForceDecode)


+ (UIImage*)decodedImageWithImage:(UIImage*)image {

    if (image.images) {

       // Do not decode animated images

       return image;

    }


    UIImage *decompressedImage;

    @autoreleasepool{

        //核心代碼,可以解決內存未釋放問題

        NSData *data = UIImageJPEGRepresentation(image, 1);

        CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);

        CGImageRef imageRef = CGImageCreateWithPNGDataProvider(dataProvider, NULL, NO, kCGRenderingIntentDefault);


    //    CGImageRef imageRef = image.CGImage;

        CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef));

        CGRect imageRect = (CGRect){.origin = CGPointZero, .size=imageSize};

        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

        CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);


        int infoMask = (bitmapInfo & kCGBitmapAlphaInfoMask);

        BOOL anyNonAlpha = (infoMask == kCGImageAlphaNone || infoMask == kCGImageAlphaNoneSkipFirst || infoMask ==kCGImageAlphaNoneSkipLast);


        // CGBitmapContextCreate doesn't support kCGImageAlphaNone with RGB.

        //https://developer.apple.com/library/mac/#qa/qa1037/_index.html

        if (infoMask == kCGImageAlphaNone&& CGColorSpaceGetNumberOfComponents(colorSpace)

> 1) {

            // Unset the old alpha info.

            bitmapInfo &= ~kCGBitmapAlphaInfoMask;

            // Set noneSkipFirst.

            bitmapInfo |= kCGImageAlphaNoneSkipFirst;

        }

            // Some PNGs tell us they have alpha but only 3 components. Odd.

        else if (!anyNonAlpha && CGColorSpaceGetNumberOfComponents(colorSpace)

== 3) {

                // Unset the old alpha info.

                bitmapInfo &= ~kCGBitmapAlphaInfoMask;

                bitmapInfo |=kCGImageAlphaPremultipliedFirst;

            }


    // It calculates the bytes-per-row based on the bitsPerComponent and width arguments.

            CGContextRef context = CGBitmapContextCreate(NULL,

            imageSize.width,

            imageSize.height,

            CGImageGetBitsPerComponent(imageRef), 0, colorSpace, bitmapInfo);

            CGColorSpaceRelease(colorSpace);


            // If failed, return undecompressed image

            if (!context) return image;


            CGContextDrawImage(context, imageRect, imageRef);

            CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context);

            CGContextRelease(context);


            decompressedImage = [UIImage imageWithCGImage: decompressedImageRef scale: image.scale orientation: image.imageOrientation];

            CGImageRelease(decompressedImageRef);

       }


        //    CVPixelBufferRef pixelBuffer;   

        //   CreateCGImageFromCVPixelBuffer(pixelBuffer,&decompressedImageRef);

        //    CGImage *cgImage =CGBitmapContextCreateImage(context);

        //    CFDataRef dataRef =CGDataProviderCopyData(CGImageGetDataProvider(cgImage));

        //    CGImageRelease(cgImage);

        //    image->imageRef = dataRef;

        //    image->image = CFDataGetBytePtr(dataRef);

    return decompressedImage;

}


5.1.2 UIImage自定義繪製的四種方法

///方法中會自動做縮放處理

+(void) getBitmapImage: (UIImage *)image Size: (CGSize)imageSize WithCompletionBlock:(HJCallbackBlock)block

{

       dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

            UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0);

            CGContextRef context = UIGraphicsGetCurrentContext();


            if(!context) {

                dispatch_async(dispatch_get_main_queue(), ^{

                    block(image);

                });

            }


            CGRect rect = CGRectMake(0, 0, imageSize.width, imageSize.height);

            //座標系統已經自動考慮了縮放因素,不需要額外處理

            [image drawInRect: rect blendMode: kCGBlendModeNormal alpha:1];

            UIImage *temp = UIGraphicsGetImageFromCurrentImageContext();

            NSData *tempData = UIImageJPEGRepresentation(temp, 1);

            UIGraphicsEndImageContext();


            //設置SDWebImage庫的緩存

            NSString *device = [HJUtility getCurDeviceModel];

            if ([device rangeOfString:@"iPhone 4"].length > 0) {

                if (tempData.length > 500000) {

                    tempData =UIImageJPEGRepresentation(temp, 0.4);

                }


                temp = [UIImage imageWithData: tempData];

        }


        dispatch_async(dispatch_get_main_queue(), ^{

            if(block) {

                block(temp);

            }

        });

    });


    //方案二,內存有釋放,掛機

    //   UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);

    //

    //   CGContextRef context = UIGraphicsGetCurrentContext();

    //   CGRect rect = CGRectMake(0, 0, imageSize.width * [UIScreenmainScreen].scale, imageSize.height * [UIScreen mainScreen].scale);

    //   // draw alpha-mask

    ////   CGContextSetBlendMode(context, kCGBlendModeNormal);

    //   CGContextDrawImage(context, rect, image.CGImage);

    //   // draw tint color, preserving alpha values of original image

    ////   CGContextSetBlendMode(context, kCGBlendModeSourceIn);

    //

    //   CGContextFillRect(context, rect);

    //

    //   //Set the original greyscale template as the overlay of the new image

    //   UIImage *imgData = [self verticallyFlipImage:image];

    //   [imgData drawInRect:imageRect];

    //   UIImage *colouredImage = UIGraphicsGetImageFromCurrentImageContext();

    //   UIGraphicsEndImageContext();

    //   colouredImage = [self verticallyFlipImage:colouredImage];

    //    CGContextRelease(context);

    //   return colouredImage;


    //方案三,CGBitmapContextCreate方案,內存沒釋放

    //   CGFloat targetWidth = imageSize.width * [UIScreen mainScreen].scale;

    //   CGFloat targetHeight = imageSize.height * [UIScreen mainScreen].scale;

    //   CGImageRef imageRef = [image CGImage];

    //   CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);

    //   CGColorSpaceRef colorSpaceInfo = CGImageGetColorSpace(imageRef);

    //   CGContextRef bitmapContext;

    //   bitmapContext = CGBitmapContextCreate(NULL, targetWidth,targetHeight,CGImageGetBitsPerComponent(imageRef),CGImageGetBytesPerRow(imageRef),colorSpaceInfo, bitmapInfo);

    //   CGContextDrawImage(bitmapContext, CGRectMake(0, 0, targetWidth,targetHeight), imageRef);

    //

    //   CGImageRef imgref = CGBitmapContextCreateImage(bitmapContext);

    //   UIImage* newImage = [UIImage imageWithCGImage:imgref];

    //

    //   CGColorSpaceRelease(colorSpaceInfo);

    //   CGContextRelease(bitmapContext);

    //   CGImageRelease(imgref);

    //

    //   return newImage;


    //方案四,CGBitmapContextCreate方案,但是採用CGDataProviderCreateWithCFData方案解決內存佔用問題

    //   NSData *data = UIImageJPEGRepresentation(image, 1);

    //   CGDataProviderRef dataProvider =CGDataProviderCreateWithCFData((__bridge CFDataRef)data);

    //   CGImageRef imageRef = CGImageCreateWithJPEGDataProvider(dataProvider,

    //                                                          NULL, NO,

    //                                                           kCGRenderingIntentDefault);

    //

    //   CGFloat targetWidth = imageSize.width * [UIScreen mainScreen].scale;

    //   CGFloat targetHeight = imageSize.height * [UIScreen mainScreen].scale;

    //   //        CGImageRef imageRef = [image CGImage];

    //

    //   CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);

    //

    //   CGColorSpaceRef colorSpaceInfo = CGImageGetColorSpace(imageRef);

    //   CGContextRef bitmapContext;

    //   bitmapContext = CGBitmapContextCreate(NULL, targetWidth,targetHeight,CGImageGetBitsPerComponent(imageRef),0, colorSpaceInfo,bitmapInfo);

    //   CGContextDrawImage(bitmapContext, CGRectMake(0, 0, targetWidth,targetHeight), imageRef);

    //

    //   // If failed, return undecompressed image

    //   if (!bitmapContext) return image;

    //

    //   CGImageRef imgref = CGBitmapContextCreateImage(bitmapContext);

    //   UIImage* newImage = [UIImage imageWithCGImage:imgref];//[UIImageimageWithCGImage:decompressedImageRef scale:image.scaleorientation:image.imageOrientation];

    //   

    //   CGColorSpaceRelease(colorSpaceInfo);

    //   CGContextRelease(bitmapContext);

    //   CGImageRelease(imgref);

    //   

    //   return newImage;

}


5.1.3 繪製時單元格底部出現高度不定的細微黑線

問題原因:

       將Text做寬高計算時,高度值容易得出小數數值,而頁面繪製均是基於整數像素點繪製,對於小數點部分,系統會做捨去處理(即便有縮放),固留下高度不定的未繪製區域(爲黑色)。


解決方案:

       將計算出來的高度值做向下取整處理即可。

CGRect labelFrame = [content boundingRectWithSize: size options: NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:_postContentTextView.typingAttributes context: nil];

labelSize = labelFrame.size;

labelSize.height = ceilf(labelSize.height);


6 參考鏈接

(GOOD)iOS開發中界面展示大圖片時UIImage的性能有關問題

http://www.myexception.cn/operating-system/578931.html

(Good)iPhone - UIImage Leak, CGBitmapContextCreateImage Leak

http://stackoverflow.com/questions/1427478/iphone-uiimage-leak-cgbitmapcontextcreateimage-leak

Another iPhone - CGBitmapContextCreateImage Leak

http://stackoverflow.com/questions/1434714/another-iphone-cgbitmapcontextcreateimage-leak

UIGraphicsBeginImageContext vs CGBitmapContextCreate

http://stackoverflow.com/questions/4683448/uigraphicsbeginimagecontext-vs-cgbitmapcontextcreate

iPhone - CGBitmapContextCreateImage Leak, Anyone else withthis problem?

http://stackoverflow.com/questions/1431566/iphone-cgbitmapcontextcreateimage-leak-anyone-else-with-this-problem

Build and Analyze false positive on leak detection?

http://stackoverflow.com/questions/8438249/build-and-analyze-false-positive-on-leak-detection

iPhone - Multiple CGBitmapContextCreateImage Calls -ObjectAlloc climbing

http://stackoverflow.com/questions/1436465/iphone-multiple-cgbitmapcontextcreateimage-calls-objectalloc-climbing

(Good)ios開發圖片處理,內存泄露

http://www.oschina.net/question/736524_69802

主題:CGBitmapContextCreateImage(bitmap)內存泄露問題處理

http://www.cocoachina.com/bbs/read.php?tid=31835

iOS異步圖片加載優化與常用開源庫分析

http://luoyibu.com/2015/05/12/iOS異步圖片加載優化與常用開源庫分析/

主題:圖片處理開源函數ImageProcessing  CGDataProviderCreateWithData Bug修復

http://www.cocoachina.com/bbs/read.php?tid=116149

CGDataProviderCreateWithData對內存數據的釋放

http://www.taofengping.com/2012/11/04/cgdataprovidercreatewithdata_memory_release/#.VmpqgoSitZE

IOS7.x下UIGraphicsGetImageFromCurrentImageContext引發內存暴漲,導致應用被結束掉

http://blog.163.com/l1_jun/blog/static/1438638820155593641529/

在iOS中與CGContextRef的內存泄漏

http://www.itstrike.cn/Question/55b86ce7-dfba-4548-a103-22dc5317420a.html

使用AFNetworking, SDWebimage和OHHTTPStubs

http://blog.shiqichan.com/using-afnetworking-sdwebimage-and-ohhttpstubs/

SDWebImage緩存圖片的機制(轉)

http://blog.csdn.net/zhun36/article/details/8900327

近來一個swift項目用uicollectionview 用sdwebimage 加載圖片,發生內存猛增,直接閃退的情況,簡單說一下解決方案

http://www.myexception.cn/swift/2033029.html

關於SDWebImage加載高清圖片導致app崩潰的問題

http://www.bubuko.com/infodetail-956863.html

SDWebImage加載大圖導致的內存警告問題

http://blog.csdn.net/richer1997/article/details/43481959

解決MWPhotoBrowser中的SDWebImage加載大圖導致的內存警告問題

http://my.oschina.net/u/1244672/blog/510379

使用SDWebImage加載大量圖片後造成內存泄露的解決辦法

http://www.bubuko.com/infodetail-985746.html

UIGraphicsBeginImageContext系列知識

http://blog.sina.com.cn/s/blog_5fb39f9101017n1v.html

iOS繪圖教程

http://blog.csdn.net/nogodoss/article/details/18660153

CGBitmapContextCreate函數

http://blog.csdn.net/thanklife/article/details/25790433

UIGraphicsBeginImageContext創建的映像停留在內存中永恆

http://codego.net/589714/

多次在cell中加載網絡圖片後,內存增長,以前資源未釋放

http://bbs.csdn.net/topics/390891681

請問下面的代碼有潛在的內存泄漏?

http://codego.net/459077/

[ios]UIGraphicsGetImageFromCurrentImageContext()-內存泄漏

http://www.itstrike.cn/Question/88ada9bd-911c-44a7-874b-e04c1a1c2bca.html

[轉載]ios開發之View屬性hidden, opaque, alpha的區別

http://blog.sina.com.cn/s/blog_7da2c9030101ev8n.html

利用預渲染加速iOS設備的圖像顯示

http://www.keakon.net/2011/07/26/利用預渲染加速iOS設備的圖像顯示

iOS使用CGContextRef繪製各種圖形

http://www.devstore.cn/essay/essayInfo/116.html

iOS CGContextRef畫圖小結

http://blog.sina.com.cn/s/blog_9693f61a0101deko.html

IOS用CGContextRef畫各種圖形(文字、圓、直線、弧線、矩形、扇形、橢圓、三角形、圓角矩形、貝塞爾曲線、圖片)

http://blog.csdn.net/rhljiayou/article/details/9919713

iOS畫圖 以及清空

http://blog.csdn.net/woshidaniu/article/details/46683409


7 Quartz 2D

7.1 參考鏈接

iOS通過Quartz畫矩形、文字、線

http://blog.csdn.net/onlyou930/article/details/7726399

Quartz 2D參考-文本

http://blog.csdn.net/kmyhy/article/details/7258338

Quartz 2D (ProgrammingWithQuartz) note

http://renxiangzyq.iteye.com/blog/1188025


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章