對於大多數 IOS 應用來說,圖片往往是最占用手機內存的資源之一,同時也是不可或缺的組成部分。將一張圖片從磁盤中加載出來,並最終顯示到屏幕上,中間其實經過了一系列復雜的處理過程,其中就包括了對圖片的解壓縮。
圖片加載的工作流概括來說,從磁盤中加載一張圖片,並將它顯示到屏幕上,中間的主要工作流如下:
假設我們使用+imageWithContentsOfFile:
方法從磁盤中加載一張圖片,這個時候的圖片並沒有解壓縮;
然後將生成的UIImage
賦值給UIImageView
;
接著一個隱式的CATransaction
捕獲到了UIImageView
圖層樹的變化;
在主線程的下一個 run loop 到來時,Core Animation 提交了這個隱式的 transaction ,這個過程可能會對圖片進行 copy 操作,而受圖片是否字節對齊等因素的影響,這個
copy 操作可能會涉及以下部分或全部步驟:
分配內存緩沖區用於管理文件 IO 和解壓縮操作;
將文件數據從磁盤讀到內存中;
將壓縮的圖片數據解碼成未壓縮的位圖形式,這是一個非常耗時的 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 :
我們使用下面的代碼:
1 2UIImage *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 1700000000 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 178950 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);
顧名思義,這個函數用於創建一個位圖上下文,用來繪制一張寬
Pixel Formatwidth
像素,高height
像素的位圖。這個函數的注釋比較長,參數也比較難理解,但是先別著急,我們先來了解下相關的知識,然後再回過頭來理解這些參數,就會比較簡單了。我們前面已經提到了,位圖其實就是一個像素數組,而像素格式則是用來描述每個像素的組成格式,它包括以下信息:
Bits per component :一個像素中每個獨立的顏色分量使用的 bit 數; Bits per pixel :一個像素使用的總 bit 數; Bytes per row :位圖中的每一行使用的字節數。有一點需要注意的是,對於位圖來說,像素格式並不是隨意組合的,目前只支持以下有限的17 種特定組合:
從上圖可知,對於 iOS 來說,只支持 8 種像素格式。其中顏色空間為 Null 的 1 種,Gray 的 2 種,RGB 的 5 種,CMYK 的 0 種。換句話說,iOS 並不支持 CMYK 的顏色空間。另外,在表格的第 2 列中,除了像素格式外,還指定了 bitmap information constant ,我們在後面會詳細介紹。
Color and Color Spaces在上面我們提到了顏色空間,那麼什麼是顏色空間呢?它跟顏色有什麼關系呢?在 Quartz 中,一個顏色是由一組值來表示的,比如 0, 0, 1 。而顏色空間則是用來說明如何解析這些值的,離開了顏色空間,它們將變得毫無意義。比如,下面的值都表示藍色:
如果不知道顏色空間,那麼我們根本無法知道這些值所代表的顏色。比如 0, 0, 1 在 RGB 下代表藍色,而在 BGR 下則代表的是紅色。在 RGB 和 BGR 兩種顏色空間下,綠色是相同的,而紅色和藍色則相互對調了。因此,對於同一張圖片,使用 RGB 和 BGR 兩種顏色空間可能會得到兩種不一樣的效果:
是不是感覺非常有意思呢?
Color Spaces and Bitmap Layout我們前面已經知道了,像素格式是用來描述每個像素的組成格式的,比如每個像素使用的總 bit 數。而要想確保 Quartz 能夠正確地解析這些 bit 所代表的含義,我們還需要提供位圖的布局信息
CGBitmapInfo
:1 2 3 4 5 6 7 8 9 10 11 12 13typedef 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 10typedef 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 7typedef 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);
它主要提供了兩個方面的字節順序信息:
小端模式還是大端模式; 數據以 16 位還是 32 位為單位。對於 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 顏色空間下,一個像素是如何被表示的:
我們從圖中可以看出,在 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 31CGImageRef 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
函數的部分參數存在細微的差別,如下表所示:
在上表中,用淺綠色背景標記的參數即為我們在前面的分析中所推薦的參數,用這些參數解壓縮後的圖片渲染的速度會更快。因此,從理論上說 YYKit 中的解壓縮算法是三者之中最優的。
性能對比口說無憑,因此我編寫了一個小的測試程序,來簡單地對比一下這三個開源庫的解壓縮性能,源碼可以在GitHub上找到。
采用的測試樣例分別為 5 張 PNG 圖片和 5 張 JPEG 圖片,像素依次為 128x96 、256x192 、512x384 、1024x768 和 2048x1536 ,它們其實都長一個樣:
首先,我們來了解下測試的原理,我們可以將從磁盤加載一張圖片到最終渲染到屏幕上的過程劃分為三個階段:
初始化階段:從磁盤初始化圖片,生成一個未解壓縮的UIImage
對象; 解壓縮階段:分別使用 YYKit 、SDWebImage 和 FLAnimatedImage 對第 1 步中得到的UIImage
對象進行解壓縮,得到一個新的解壓縮後的UIImage
對象; 繪制階段:將第 2 步中得到的UIImage
對象繪制到屏幕上。這裡我們以繪制階段的耗時為依據來評測解壓縮的性能,解壓縮的算法越優秀,那麼得到的圖片就越符合系統渲染時的需求,繪制的時間也就越短。為了讓測試的結果更准確,我們對每張圖片都解壓縮 10 次,然後取平均值。說明,本次使用的測試設備是 iPhone 5s 。
首先,我們來看看解壓縮 PNG 圖片的測試結果:
相應的柱狀圖如下:
從上圖可以看出,就我們采用的測試樣例來說,解壓縮 PNG 圖片的性能 SDWebImage 最好,FLAnimatedImage 次之,YYKit 最差。這與我們前面的理論結果有一定的差距,可能是測試樣例太少,也可能這就是真實結果。另外,需要說明的是,我們這裡使用的 PNG 圖片都是不帶 alpha 值,因為 SDWebImage 不支持解壓縮帶 alpha 值的 PNG 圖片。
接著,我們再來看看解壓縮 JPEG 圖片的測試結果:
相應的柱狀圖如下:
這次 YYKit 終於翻盤了,解壓縮 JPEG 圖片的性能最好,SDWebImage 和 FLAnimatedImage 並列第二。
總結其實,要理解 iOS 中圖片的解壓縮並不難,重點是要理解位圖的概念。而圖片解壓縮的過程其實就是將圖片的二進制數據轉換成像素數據的過程。了解這些知識,將有助於我們更好地處理圖片,管理好它們所占用的內存。
參考鏈接https://www.coco.netics.com/2011/10/avoiding-image-decompression-sickness/
Posted by雷純鋒Feb20th,201710:47 am
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
原文鏈接:http://blog.leichunfeng.com/blog/2017/02/20/talking-about-the-decompression-of-the-image-in-ios/
以上就是談談 iOS 中圖片的解壓縮的全文介紹,希望對您學習和使用ios應用開發有所幫助.【談談 iOS 中圖片的解壓縮】的相關資料介紹到這裡,希望對您有所幫助! 提示:不會對讀者因本文所帶來的任何損失負責。如果您支持就請把本站添加至收藏夾哦!