轉載-談談 iOS 中圖片的解壓縮

談談 iOS 中圖片的解壓縮

FEB 20TH, 2017 10:47 AM

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

圖片加載的工作流

概括來說,從磁盤中加載一張圖片,并將它顯示到屏幕上,中間的主要工作流如下:

  1. 假設我們使用 +imageWithContentsOfFile: 方法從磁盤中加載一張圖片,這個時候的圖片并沒有解壓縮;

  2. 然后將生成的 UIImage 賦值給 UIImageView

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

  4. 在主線程的下一個 run loop 到來時,Core Animation 提交了這個隱式的 transaction ,這個過程可能會對圖片進行 copy 操作,而受圖片是否

    字節對齊

    等因素的影響,這個 copy 操作可能會涉及以下部分或全部步驟:

    1. 分配內存緩沖區用于管理文件 IO 和解壓縮操作;
    2. 將文件數據從磁盤讀到內存中;
    3. 將壓縮的圖片數據解碼成未壓縮的位圖形式,這是一個非常耗時的 CPU 操作;
    4. 最后 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 :

位圖

我們使用下面的代碼

`UIImage *image = [UIImage imageNamed:@"check_green"]; CFDataRef rawData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage)); `

就可以獲取到這個圖片的原始像素數據,大小為 3600B :

`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 是怎么得來的呢?與圖片的文件大小或者像素有什么必然的聯系嗎?事實上,解壓縮后的圖片大小與原始文件大小之間沒有任何關系,而只與圖片的像素有關:

`解壓縮后的圖片大小 = 圖片的像素寬 30 * 圖片的像素高 30 * 每個像素所占的字節數 4 `

至于這個公式是怎么得來的,我們后面會有詳細的說明,現在只需要知道即可。

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

`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 圖片:

`// 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

`/* 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 種特定組合

img

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

Color and Color Spaces

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

img

如果不知道顏色空間,那么我們根本無法知道這些值所代表的顏色。比如 0, 0, 1 在 RGB 下代表藍色,而在 BGR 下則代表的是紅色。在 RGB 和 BGR 兩種顏色空間下,綠色是相同的,而紅色和藍色則相互對調了。因此,對于同一張圖片,使用 RGB 和 BGR 兩種顏色空間可能會得到兩種不一樣的效果:

color_profiles

是不是感覺非常有意思呢?

Color Spaces and Bitmap Layout

我們前面已經知道了,像素格式是用來描述每個像素的組成格式的,比如每個像素使用的總 bit 數。而要想確保 Quartz 能夠正確地解析這些 bit 所代表的含義,我們還需要提供位圖的布局信息 CGBitmapInfo

`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 來表示:

`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來表示的:

`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

`#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

我們從圖中可以看出,在 32 位像素格式下,每個顏色分量使用 8 位;而在 16 位像素格式下,每個顏色分量則使用 5 位。

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

  • data :如果不為 NULL ,那么它應該指向一塊大小至少為 bytesPerRow * height 字節的內存;如果 為 NULL ,那么系統就會為我們自動分配和釋放所需的內存,所以一般指定 NULL 即可;
  • widthheight :位圖的寬度和高度,分別賦值為圖片的像素寬度和像素高度即可;
  • 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 :就是我們前面提到的位圖的布局信息。

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

開源庫的實現

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

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

`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 函數的部分參數存在細微的差別,如下表所示:

img

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

性能對比

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

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

128x96

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

  • 初始化階段:從磁盤初始化圖片,生成一個未解壓縮的 UIImage 對象;
  • 解壓縮階段:分別使用 YYKit 、SDWebImage 和 FLAnimatedImage 對第 1 步中得到的 UIImage 對象進行解壓縮,得到一個新的解壓縮后的 UIImage 對象;
  • 繪制階段:將第 2 步中得到的 UIImage 對象繪制到屏幕上。

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

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

img

相應的柱狀圖如下:

img

從上圖可以看出,就我們采用的測試樣例來說,解壓縮 PNG 圖片的性能 SDWebImage 最好,FLAnimatedImage 次之,YYKit 最差。這與我們前面的理論結果有一定的差距,可能是測試樣例太少,也可能這就是真實結果。另外,需要說明的是,我們這里使用的 PNG 圖片都是不帶 alpha 值,因為 SDWebImage 不支持解壓縮帶 alpha 值的 PNG 圖片。

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

img

相應的柱狀圖如下:

img

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

總結

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

參考鏈接

https://www.cocoanetics.com/2011/10/avoiding-image-decompression-sickness/
https://developer.apple.com/library/content/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/Introduction/Introduction.html
https://github.com/path/FastImageCache
http://stackoverflow.com/questions/23790837/what-is-byte-alignment-cache-line-alignment-for-core-animation-why-it-matters

Posted by 雷純鋒 Feb 20th, 2017 10:47 am
原文鏈接:http://blog.leichunfeng.com/blog/2017/02/20/talking-about-the-decompression-of-the-image-in-ios/

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,565評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,115評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,577評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,514評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,234評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,621評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,641評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,822評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,380評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,128評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,319評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,879評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,548評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,970評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,229評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,048評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,285評論 2 376

推薦閱讀更多精彩內容