說到圖像解碼庫,最容易想起的就是 libpng
和 libjpeg
這兩個老牌圖像解碼庫了。ios
libpng
和 libjpeg
分別各自對應 png
和 jpeg
兩種圖像格式。這兩種格式的區別以下:git
png
支持透明度,無損壓縮的圖片格式,能在保證不失真的狀況下儘量壓縮圖像文件的大小,所以圖像質量高,在一些貼紙應用中也大部分用的是 png 圖片。github
jpg
不支持透明度,有損壓縮的圖片格式,有損壓縮會使得原始圖片數據質量下載,也所以它佔用的內存小,在網頁應用中加速速度快。微信
要想在工程中同時解碼 png
和 jpeg
格式圖片,就必須同時引用這兩種庫,並且還得通過一系列編譯步驟才行。ide
在這裏,介紹一個簡單易用的圖像庫:stb_image
。Github 地址爲:https://github.com/nothings/stb ,目前已經有了 9600+ Star 。它的使用很是簡單,看看 README 可能你就會了。函數
<!--more-->性能
看看它的源碼,你會發現全是 .h
頭文件。這就是它的強大之處了,僅需在工程中加入頭文件就能夠解析圖像了(其實是函數實現等內容都放在頭文件了而已)。spa
重點關注以下三個頭文件:指針
stb_image.hcode
stb_image_write.h
stb_image_resize.h
下面就開始實踐吧,先給出一個完整的例子:
#include <iostream> #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" #define STB_IMAGE_RESIZE_IMPLEMENTATION #include "stb_image_resize.h" #include <string> #include <stdio.h> #include <stdlib.h> #include <vector> using namespace std; int main() { std::cout << "Hello, STB_Image" << std::endl; string inputPath = "/Users/glumes/Pictures/input.png"; int iw, ih, n; // 加載圖片獲取寬、高、顏色通道信息 unsigned char *idata = stbi_load(inputPath.c_str(), &iw, &ih, &n, 0); int ow = iw / 2; int oh = ih / 2; auto *odata = (unsigned char *) malloc(ow * oh * n); // 改變圖片尺寸 stbir_resize(idata, iw, ih, 0, odata, ow, oh, 0, STBIR_TYPE_UINT8, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_SRGB, nullptr ); string outputPath = "/Users/glumes/Pictures/output.png"; // 寫入圖片 stbi_write_png(outputPath.c_str(), ow, oh, n, odata, 0); stbi_image_free(idata); stbi_image_free(odata); return 0; }
這個例子很簡單也很全面,主要就是加載了一張圖片,並將它的寬高都縮小一倍,並保存縮小後圖片。
首先是調用 stbi_load
方法去加載圖像數據,並獲取相關信息。傳入的參數除了圖片文件地址,還有寬、高、顏色通道信息的引用。
變量 n
就表明圖片的顏色通道值,一般有以下的狀況:
返回的結果就是圖片像素數據的指針了。
stbi_load
不只僅支持 png
格式,把上面例子中的圖片改爲 jpg
格式後綴的依舊可行。
它支持的全部格式以下:
格式雖多,不過通常用到 png 和 jpg 就行了。
除了從文件加載圖片,stb_image 還支持從內存中加載圖片,經過該方法 stbi_load_from_memory
,在後續文章中會用到它的。
加載完圖片以後,stb_image 還提供了相應的釋放方法 stbi_image_free
,實際上就是把 free
封裝了一下而已。
加載完圖片像素數據以後,就能夠經過 stbir_resize
方法改變圖片的尺寸。
stbir_resize
方法參數有不少:
STBIRDEF int stbir_resize(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, stbir_datatype datatype, int num_channels, int alpha_channel, int flags, // stb 中提供了多種模式,可是修改後並無見很明顯的效果 stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context)
stbir_edge
和 stbir_filter
類型的參數,stb_image_resize 提供了多種類型:
typedef enum { STBIR_EDGE_CLAMP = 1, STBIR_EDGE_REFLECT = 2, STBIR_EDGE_WRAP = 3, STBIR_EDGE_ZERO = 4, } stbir_edge; typedef enum { STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 } stbir_filter;
但實際上調整不一樣類型組合獲得的圖片並無太多的變化 ┑( ̄Д  ̄)┍。
真正有用的可能仍是前面那幾個參數,指定了要將圖片縮放後的寬高數據。
最後就是調用 stbi_write_png
方法將像素數據寫入文件中,除此以外,stb_image_write 還提供了 stbi_write_jpg
方法來保存 jpg 格式圖片。
根據二者格式的不一樣,方法調用的參數也是不同的。
int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)
以上就是關於 stb_image 圖像解碼庫的小介紹。
總的來講,它仍是挺簡單易用的,在日常作一些 Demo 以及須要快速實現、驗證功能的狀況下均可以考慮考慮。
可是在一些大型的項目中,仍是要深思熟慮一些,從性能方面考慮,它確定不如老牌的圖像解碼庫了,像 libjpeg-turbo
解碼用到了 NEON
這樣 SIMD (單指令流多數據流)的操做,纔是大型項目的首選了。
關於 stb_image 在 Android 中的使用實踐,能夠參考個人項目:
https://github.com/glumes/InstantGLSL
歡迎關注微信公衆號:【紙上淺談】,得到最新文章推送~~~