圖片解壓縮

tips:若是直接使用下面的方式加載圖片,圖片將會在視圖渲染在手機屏幕上時去解壓縮,即在主線程去解壓縮圖片,對性能消耗較大,所以產生了在子線程解壓縮的需求。html

UIImage *image = [UIImage imageWithContentsOfFile:@""];
    UIImageView *imgView = [[UIImageView alloc] initWithImage:image];
    [self.view addSubview:imgView];

對於大多數 iOS 應用來講,圖片每每是最佔用手機內存的資源之一,同時也是不可或缺的組成部分。將一張圖片從磁盤中加載出來,並最終顯示到屏幕上,中間其實通過了一系列複雜的處理過程,其中就包括了對圖片的解壓縮。git

圖片加載的工做流github

歸納來講,從磁盤中加載一張圖片,並將它顯示到屏幕上,中間的主要工做流以下:算法

1.假設咱們使用 +imageWithContentsOfFile: 方法從磁盤中加載一張圖片,這個時候的圖片並無解壓縮;數組

2.而後將生成的 UIImage 賦值給 UIImageView ;app

3.接着一個隱式的 CATransaction 捕獲到了 UIImageView 圖層樹的變化;ide

4.在主線程的下一個 run loop 到來時,Core Animation 提交了這個隱式的 transaction ,這個過程可能會對圖片進行 copy 操做,而受圖片是否字節對齊等因素的影響,這個 copy 操做可能會涉及如下部分或所有步驟:函數

  • 分配內存緩衝區用於管理文件 IO 和解壓縮操做;oop

  • 將文件數據從磁盤讀到內存中;佈局

  • 將壓縮的圖片數據解碼成未壓縮的位圖形式,這是一個很是耗時的 CPU 操做;

  • 最後 Core Animation 使用未壓縮的位圖數據渲染 UIImageView 的圖層。

在上面的步驟中,咱們提到了圖片的解壓縮是一個很是耗時的 CPU 操做,而且它默認是在主線程中執行的。那麼當須要加載的圖片比較多時,就會對咱們應用的響應性形成嚴重的影響,尤爲是在快速滑動的列表上,這個問題會表現得更加突出。

爲何須要解壓縮

既然圖片的解壓縮須要消耗大量的 CPU 時間,那麼咱們爲何還要對圖片進行解壓縮呢?是否能夠不通過解壓縮,而直接將圖片顯示到屏幕上呢?答案是否認的。要想弄明白這個問題,咱們首先須要知道什麼是位圖:

「A bitmap image (or sampled image) is an array of pixels (or samples). Each pixel represents a single point in the image. JPEG, TIFF, and PNG graphics files are examples of bitmap images.」

其實,位圖就是一個像素數組,數組中的每一個像素就表明着圖片中的一個點。咱們在應用中常常用到的 JPEG 和 PNG 圖片就是位圖。下面,咱們來看一個具體的例子,這是一張 PNG 圖片,像素爲 30?×?30 ,文件大小爲 843B :

check_green.png

咱們使用下面的代碼

1

2

UIImage *image = [UIImage imageNamed:@"check_green"];

CFDataRef rawData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));

就能夠獲取到這個圖片的原始像素數據,大小爲 3600B :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

01020102 032c023c 0567048c 078d06bf 08a006d9 09b307f3 09b307f3 08a006d9 078d06bf

0567048c 032c023c 01020102 00000000 00000000 00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

00000000 01060108 05570476 09ab07e9 09bb07ff 09bb07ff 09bb07ff 09bb07ff 09bb07ff

09bb07ff 09bb07ff 09bb07ff 09bb07ff 09bb07ff 09ab07e9 05570476 01060108 00000000

00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

00000000 00000000 00000000 033d0353 08a607e2 09bb07ff 09bb07ff 09bb07ff 09bb07ff

...

09bb07ff 09bb07ff 09bb07ff 09bb07ff 08a607e2 033d0353 00000000 00000000 00000000

00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

00000000 01060108 05570476 09ab07e9 09bb07ff 09bb07ff 09bb07ff 09bb07ff 09bb07ff

09bb07ff 09bb07ff 09bb07ff 09bb07ff 09bb07ff 09ab07e9 05570476 01060108 00000000

00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000000 00000000 00000000 01020102 032c023c 0567048c

078d06bf 08a006d9 09b307f3 09b307f3 08a006d9 078d06bf 0567048c 032c023c 01020102

00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

也就是說,這張文件大小爲 843B 的 PNG 圖片解壓縮後的大小是 3600B ,是原始文件大小的 4.27 倍。那麼這個 3600B 是怎麼得來的呢?與圖片的文件大小或者像素有什麼必然的聯繫嗎?事實上,解壓縮後的圖片大小與原始文件大小之間沒有任何關係,而只與圖片的像素有關:

1

解壓縮後的圖片大小 = 圖片的像素寬 30 * 圖片的像素高 30 * 每一個像素所佔的字節數 4

至於這個公式是怎麼得來的,咱們後面會有詳細的說明,如今只須要知道便可。

至此,咱們已經知道了什麼是位圖,而且直觀地看到了它的原始像素數據,那麼它與咱們常常提到的圖片的二進制數據有什麼聯繫嗎?是同一個東西嗎?事實上,這兩者是徹底獨立的兩個東西,它們之間沒有必然的聯繫。爲了加深理解,我把這個圖片拖進 Sublime Text 2 中,獲得了這個圖片的二進制數據,大小與原始文件大小一致,爲 843B :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

8950 4e47 0d0a 1a0a 0000 000d 4948 4452 0000 001e 0000 001e 0806 0000 003b 30ae a200

0000 0173 5247 4200 aece 1ce9 0000 0305 4944 4154 480d c557 4d68 1341 149e 3709 da4d

09c6 8a56 2385 9e14 f458 4fa2 d092 f4a6 28d8 2222 de04 3d09 a1d0 7a50 0954 8bad 2d05

4fde 3c89 482b 2ad6 8334 d183 e049 ef9e 4a41 48b0 42eb a549 6893 1ddf 9bcd b4d9 d9d9

4dd8 a43a b0d9 9d79 3fdf bc79 3ff3 02ac 8591 1559 3e97 9b3e 5b05 fb32 6330 c098 48a2

183d 340a b886 8ff8 1e15 fced 587a e26b 16b2 b643 f2ff 057f 1263 fd9f fbbb 7ed7 7edd

1142 8c09 268e 04f1 2a1a 3058 0380 b9c3 91de a7ab 43ab 15b5 aebf 7d81 ad65 eb0a 5a31

8f4f 9f2e d4da 1c7e e249 64ca c3e5 d726 7eae 2fa2 7510 cb75 3d62 cc5e 0c0f 4a5a 69c3

...

36ac b11e 7006 f71b 5386 a2b7 1e48 ad82 a26a 2880 95db 3f8b f525 b880 e0ed 7221 75f1

fa02 2cd4 1af7 1d0e 546a 98e5 d4ae 342a 337e 6b96 134f 1ba0 0c0b c83b a0f2 3593 7b5c

6ca9 b541 cb4f 254e df58 d958 8955 a0fc 2638 658c 2660 f986 b5f1 f4dd 63f2 5aec ce59

e3b6 b0a7 cdac ee55 145c c7dc 8f60 f53f e0a6 b436 e3c0 27b0 8ecf 5054 336a ccd0 e1d8

2335 1f78 323d 6141 09c3 c1aa 5f8b 4e37 0899 e6b0 ed72 4046 759e d262 5247 9d01 1689

a976 55fb c993 6ed5 7d10 8ff4 b162 fe6f cd1e ee4a d4bb c18e 594e 96ea 1da6 c762 6539

bdff 7943 afc0 c91f bdd1 a327 28fc 29f7 d47a b337 f192 0cc9 36fa 5497 73f9 5827 aa39

1599 4eff 69fb 0b0d 1f7a 96cd 3eb0 7800 0000 0049 454e 44ae 4260 82

事實上,不論是 JPEG 仍是 PNG 圖片,都是一種壓縮的位圖圖形格式。只不過 PNG 圖片是無損壓縮,而且支持 alpha 通道,而 JPEG 圖片則是有損壓縮,能夠指定 0-100% 的壓縮比。值得一提的是,在蘋果的 SDK 中專門提供了兩個函數用來生成 PNG 和 JPEG 圖片:

1

2

3

4

5

// return image as PNG. May return nil if image has no CGImageRef or invalid bitmap format

UIKIT_EXTERN NSData * __nullable UIImagePNGRepresentation(UIImage * __nonnull image);

 

// return image as JPEG. May return nil if image has no CGImageRef or invalid bitmap format. compression is 0(most)..1(least)            

UIKIT_EXTERN NSData * __nullable UIImageJPEGRepresentation(UIImage * __nonnull image, CGFloat compressionQuality);

所以,在將磁盤中的圖片渲染到屏幕以前,必須先要獲得圖片的原始像素數據,才能執行後續的繪製操做,這就是爲何須要對圖片解壓縮的緣由。

強制解壓縮的原理

既然圖片的解壓縮不可避免,而咱們也不想讓它在主線程執行,影響咱們應用的響應性,那麼是否有比較好的解決方案呢?答案是確定的。

咱們前面已經提到了,當未解壓縮的圖片將要渲染到屏幕時,系統會在主線程對圖片進行解壓縮,而若是圖片已經解壓縮了,系統就不會再對圖片進行解壓縮。所以,也就有了業內的解決方案,在子線程提早對圖片進行強制解壓縮。

而強制解壓縮的原理就是對圖片進行從新繪製,獲得一張新的解壓縮後的位圖。其中,用到的最核心的函數是 CGBitmapContextCreate :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

/* Create a bitmap context. The context draws into a bitmap which is `width'

   pixels wide and `height' pixels high. The number of components for each

   pixel is specified by `space', which may also specify a destination color

   profile. The number of bits for each component of a pixel is specified by

   `bitsPerComponent'. The number of bytes per pixel is equal to

   `(bitsPerComponent * number of components + 7)/8'. Each row of the bitmap

   consists of `bytesPerRow' bytes, which must be at least `width * bytes

   per pixel' bytes; in addition, `bytesPerRow' must be an integer multiple

   of the number of bytes per pixel. `data', if non-NULL, points to a block

   of memory at least `bytesPerRow * height' bytes. If `data' is NULL, the

   data for context is allocated automatically and freed when the context is

   deallocated. `bitmapInfo' specifies whether the bitmap should contain an

   alpha channel and how it's to be generated, along with whether the

   components are floating-point or integer. */

CG_EXTERN CGContextRef __nullable CGBitmapContextCreate(void * __nullable data,

    size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow,

    CGColorSpaceRef cg_nullable space, uint32_t bitmapInfo)

    CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

顧名思義,這個函數用於建立一個位圖上下文,用來繪製一張寬 width 像素,高 height 像素的位圖。這個函數的註釋比較長,參數也比較難理解,可是先彆着急,咱們先來了解下相關的知識,而後再回過頭來理解這些參數,就會比較簡單了。

Pixel Format

咱們前面已經提到了,位圖其實就是一個像素數組,而像素格式則是用來描述每一個像素的組成格式,它包括如下信息:

  • Bits per component :一個像素中每一個獨立的顏色份量使用的 bit 數;

  • Bits per pixel :一個像素使用的總 bit 數;

  • Bytes per row :位圖中的每一行使用的字節數。

有一點須要注意的是,對於位圖來講,像素格式並非隨意組合的,目前只支持如下有限的 17 種特定組合

Supported Pixel Formats.png

從上圖可知,對於 iOS 來講,只支持 8 種像素格式。其中顏色空間爲 Null 的 1 種,Gray 的 2 種,RGB 的 5 種,CMYK 的 0 種。換句話說,iOS 並不支持 CMYK 的顏色空間。另外,在表格的第 2 列中,除了像素格式外,還指定了 bitmap information constant ,咱們在後面會詳細介紹。

Color and Color Spaces

在上面咱們提到了顏色空間,那麼什麼是顏色空間呢?它跟顏色有什麼關係呢?在 Quartz 中,一個顏色是由一組值來表示的,好比 0, 0, 1 。而顏色空間則是用來講明如何解析這些值的,離開了顏色空間,它們將變得毫無心義。好比,下面的值都表示藍色:

3.png

若是不知道顏色空間,那麼咱們根本沒法知道這些值所表明的顏色。好比 0, 0, 1 在 RGB 下表明藍色,而在 BGR 下則表明的是紅色。在 RGB 和 BGR 兩種顏色空間下,綠色是相同的,而紅色和藍色則相互對調了。所以,對於同一張圖片,使用 RGB 和 BGR 兩種顏色空間可能會獲得兩種不同的效果:

color_profiles.png

是否是感受很是有意思呢?

Color Spaces and Bitmap Layout

咱們前面已經知道了,像素格式是用來描述每一個像素的組成格式的,好比每一個像素使用的總 bit 數。而要想確保 Quartz 可以正確地解析這些 bit 所表明的含義,咱們還須要提供位圖的佈局信息 CGBitmapInfo :

1

2

3

4

5

6

7

8

9

10

11

12

13

typedef CF_OPTIONS(uint32_t, CGBitmapInfo) {

    kCGBitmapAlphaInfoMask = 0x1F,

 

    kCGBitmapFloatInfoMask = 0xF00,

    kCGBitmapFloatComponents = (1 << 8),

 

    kCGBitmapByteOrderMask     = kCGImageByteOrderMask,

    kCGBitmapByteOrderDefault  = (0 << 12),

    kCGBitmapByteOrder16Little = kCGImageByteOrder16Little,

    kCGBitmapByteOrder32Little = kCGImageByteOrder32Little,

    kCGBitmapByteOrder16Big    = kCGImageByteOrder16Big,

    kCGBitmapByteOrder32Big    = kCGImageByteOrder32Big

} CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

它主要提供了三個方面的佈局信息:

  • alpha 的信息;

  • 顏色份量是否爲浮點數;

  • 像素格式的字節順序。

其中,alpha 的信息由枚舉值 CGImageAlphaInfo 來表示:

1

2

3

4

5

6

7

8

9

10

typedef CF_ENUM(uint32_t, CGImageAlphaInfo) {

    kCGImageAlphaNone,               /* For example, RGB. */

    kCGImageAlphaPremultipliedLast,  /* For example, premultiplied RGBA */

    kCGImageAlphaPremultipliedFirst, /* For example, premultiplied ARGB */

    kCGImageAlphaLast,               /* For example, non-premultiplied RGBA */

    kCGImageAlphaFirst,              /* For example, non-premultiplied ARGB */

    kCGImageAlphaNoneSkipLast,       /* For example, RBGX. */

    kCGImageAlphaNoneSkipFirst,      /* For example, XRGB. */

    kCGImageAlphaOnly                /* No color data, alpha data only */

};

上面的註釋其實已經比較清楚了,它一樣也提供了三個方面的 alpha 信息:

  • 是否包含 alpha ;

  • 若是包含 alpha ,那麼 alpha 信息所處的位置,在像素的最低有效位,好比 RGBA ,仍是最高有效位,好比 ARGB ;

  • 若是包含 alpha ,那麼每一個顏色份量是否已經乘以 alpha 的值,這種作法能夠加速圖片的渲染時間,由於它避免了渲染時的額外乘法運算。好比,對於 RGB 顏色空間,用已經乘以 alpha 的數據來渲染圖片,每一個像素均可以免 3 次乘法運算,紅色乘以 alpha ,綠色乘以 alpha 和藍色乘以 alpha 。

那麼咱們在解壓縮圖片的時候應該使用哪一個值呢?根據 Which CGImageAlphaInfo should we use 和官方文檔中對 UIGraphicsBeginImageContextWithOptions 函數的討論:

「You use this function to configure the drawing environment for rendering into a bitmap. The format for the bitmap is a ARGB 32-bit integer pixel format using host-byte order. If the opaque parameter is YES, the alpha channel is ignored and the bitmap is treated as fully opaque (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host). Otherwise, each pixel uses a premultipled ARGB format (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host).」

咱們能夠知道,當圖片不包含 alpha 的時候使用 kCGImageAlphaNoneSkipFirst ,不然使用 kCGImageAlphaPremultipliedFirst 。另外,這裏也提到了字節順序應該使用 32 位的主機字節順序 kCGBitmapByteOrder32Host ,而這個值具體是什麼,咱們後面再討論。

至於顏色份量是否爲浮點數,這個就比較簡單了,直接邏輯或 kCGBitmapFloatComponents 就能夠了。更詳細的內容就不展開了,由於咱們通常用不上這個值。

接下來,咱們來簡單地瞭解下像素格式的字節順序,它是由枚舉值 CGImageByteOrderInfo 來表示的:

1

2

3

4

5

6

7

typedef CF_ENUM(uint32_t, CGImageByteOrderInfo) {

    kCGImageByteOrderMask     = 0x7000,

    kCGImageByteOrder16Little = (1 << 12),

    kCGImageByteOrder32Little = (2 << 12),

    kCGImageByteOrder16Big    = (3 << 12),

    kCGImageByteOrder32Big    = (4 << 12)

} CG_AVAILABLE_STARTING(__MAC_10_12, __IPHONE_10_0);

它主要提供了兩個方面的字節順序信息:

對於 iPhone 來講,採用的是小端模式,可是爲了保證應用的向後兼容性,咱們可使用系統提供的宏,來避免 Hardcoding :

1

2

3

4

5

6

7

#ifdef __BIG_ENDIAN__

    #define kCGBitmapByteOrder16Host kCGBitmapByteOrder16Big

    #define kCGBitmapByteOrder32Host kCGBitmapByteOrder32Big

#else /* Little endian. */

    #define kCGBitmapByteOrder16Host kCGBitmapByteOrder16Little

    #define kCGBitmapByteOrder32Host kCGBitmapByteOrder32Little

#endif

根據前面的討論,咱們知道字節順序的值應該使用的是 32 位的主機字節順序 kCGBitmapByteOrder32Host ,這樣的話無論當前設備採用的是小端模式仍是大端模式,字節順序始終與其保持一致。

下面,咱們來看一張圖,它很是形象地展現了在使用 16 或 32 位像素格式的 CMYK 和 RGB 顏色空間下,一個像素是如何被表示的:

pixel formats.png

咱們從圖中能夠看出,在 32 位像素格式下,每一個顏色份量使用 8 位;而在 16 位像素格式下,每一個顏色份量則使用 5 位。

好了,瞭解完這些相關知識後,咱們再回過頭來看看 CGBitmapContextCreate 函數中每一個參數所表明的具體含義:

  • data :若是不爲 NULL ,那麼它應該指向一塊大小至少爲 bytesPerRow * height 字節的內存;若是 爲 NULL ,那麼系統就會爲咱們自動分配和釋放所需的內存,因此通常指定 NULL 便可;

  • width 和 height :位圖的寬度和高度,分別賦值爲圖片的像素寬度和像素高度便可;

  • bitsPerComponent :像素的每一個顏色份量使用的 bit 數,在 RGB 顏色空間下指定 8 便可;

  • bytesPerRow :位圖的每一行使用的字節數,大小至少爲 width * bytes per pixel 字節。有意思的是,當咱們指定 0 時,系統不只會爲咱們自動計算,並且還會進行 cache line alignment 的優化,更多信息能夠查看 what is byte alignment (cache line alignment) for Core Animation? Why it matters? 和 Why is my image’s Bytes per Row more than its Bytes per Pixel times its Width? ,親測可用;

  • space :就是咱們前面提到的顏色空間,通常使用 RGB 便可;

  • bitmapInfo :就是咱們前面提到的位圖的佈局信息。

到這裏,你已經掌握了強制解壓縮圖片須要用到的最核心的函數,點個贊。

開源庫的實現

接下來,咱們來看看在三個比較流行的開源庫 YYKit 、SDWebImage 和 FLAnimatedImage 中,對圖片的強制解壓縮是如何實現的。

首先,咱們來看看 YYKit 中的相關代碼,用於解壓縮圖片的函數 YYCGImageCreateDecodedCopy 存在於 YYImageCoder 類中,核心代碼以下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

CGImageRef YYCGImageCreateDecodedCopy(CGImageRef imageRef, BOOL decodeForDisplay) {

    ...

 

    if (decodeForDisplay) { // decode with redraw (may lose some precision)

        CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef) & kCGBitmapAlphaInfoMask;

 

        BOOL hasAlpha = NO;

        if (alphaInfo == kCGImageAlphaPremultipliedLast ||

            alphaInfo == kCGImageAlphaPremultipliedFirst ||

            alphaInfo == kCGImageAlphaLast ||

            alphaInfo == kCGImageAlphaFirst) {

            hasAlpha = YES;

        }

 

        // BGRA8888 (premultiplied) or BGRX8888

        // same as UIGraphicsBeginImageContext() and -[UIView drawRect:]

        CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;

        bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;

 

        CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, YYCGColorSpaceGetDeviceRGB(), bitmapInfo);

        if (!context) return NULL;

 

        CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); // decode

        CGImageRef newImage = CGBitmapContextCreateImage(context);

        CFRelease(context);

 

        return newImage;

    else {

        ...

    }

}

它接受一個原始的位圖參數 imageRef ,最終返回一個新的解壓縮後的位圖 newImage ,中間主要通過了如下三個步驟:

  • 使用 CGBitmapContextCreate 函數建立一個位圖上下文;

  • 使用 CGContextDrawImage 函數將原始位圖繪製到上下文中;

  • 使用 CGBitmapContextCreateImage 函數建立一張新的解壓縮後的位圖。

事實上,SDWebImage 和 FLAnimatedImage 中對圖片的解壓縮過程與上述徹底一致,只是傳遞給 CGBitmapContextCreate 函數的部分參數存在細微的差異,以下表所示:

CGBitmapContextCreate.png

在上表中,用淺綠色背景標記的參數即爲咱們在前面的分析中所推薦的參數,用這些參數解壓縮後的圖片渲染的速度會更快。所以,從理論上說 YYKit 中的解壓縮算法是三者之中最優的。

性能對比

口說無憑,所以我編寫了一個小的測試程序,來簡單地對比一下這三個開源庫的解壓縮性能,源碼能夠在 GitHub 上找到。

採用的測試樣例分別爲 5 張 PNG 圖片和 5 張 JPEG 圖片,像素依次爲 128x96 、256x192 、512x384 、1024x768 和 2048x1536 ,它們其實都長一個樣:

128x96.png

首先,咱們來了解下測試的原理,咱們能夠將從磁盤加載一張圖片到最終渲染到屏幕上的過程劃分爲三個階段:

  • 初始化階段:從磁盤初始化圖片,生成一個未解壓縮的 UIImage 對象;

  • 解壓縮階段:分別使用 YYKit 、SDWebImage 和 FLAnimatedImage 對第 1 步中獲得的 UIImage 對象進行解壓縮,獲得一個新的解壓縮後的 UIImage 對象;

  • 繪製階段:將第 2 步中獲得的 UIImage 對象繪製到屏幕上。

這裏咱們以繪製階段的耗時爲依據來評測解壓縮的性能,解壓縮的算法越優秀,那麼獲得的圖片就越符合系統渲染時的需求,繪製的時間也就越短。爲了讓測試的結果更準確,咱們對每張圖片都解壓縮 10 次,而後取平均值。說明,本次使用的測試設備是 iPhone 5s 。

首先,咱們來看看解壓縮 PNG 圖片的測試結果:

012.png

相應的柱狀圖以下:

011.png

從上圖能夠看出,就咱們採用的測試樣例來講,解壓縮 PNG 圖片的性能 SDWebImage 最好,FLAnimatedImage 次之,YYKit 最差。這與咱們前面的理論結果有必定的差距,多是測試樣例太少,也可能這就是真實結果。另外,須要說明的是,咱們這裏使用的 PNG 圖片都是不帶 alpha 值,由於 SDWebImage 不支持解壓縮帶 alpha 值的 PNG 圖片。

 

接着,咱們再來看看解壓縮 JPEG 圖片的測試結果:

decompress_jpeg_compare.png

相應的柱狀圖以下:

decompress_jpeg.png

此次 YYKit 終於翻盤了,解壓縮 JPEG 圖片的性能最好,SDWebImage 和 FLAnimatedImage 並列第二。

總結

其實,要理解 iOS 中圖片的解壓縮並不難,重點是要理解位圖的概念。而圖片解壓縮的過程其實就是將圖片的二進制數據轉換成像素數據的過程。瞭解這些知識,將有助於咱們更好地處理圖片,管理好它們所佔用的內存。

相關文章
相關標籤/搜索