😁博客主頁😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客內容🤑:🍭嵌入式開發、Linux、C語言、C++、數據結構、音視頻🍭
🤣本文內容🤣:🍭介紹 🍭
😎金句分享😎:🍭你不能選擇最好的,但最好的會來選擇你——泰戈爾🍭
?發布時間?:
本文未經允許,不得轉發!!!
目錄
- 🎄一、概述
- 🎄二、libpng 讀取 png 圖片文件步驟
- ?2.1、打開文件并檢查是否png文件
- ?2.2、初始化libpng的數據結構 :png_ptr, info_ptr
- ?2.3、設置錯誤返回點
- ?2.4、初始化 io
- ?2.5、讀取png文件信息
- ?2.6、讀取實際的rgb數據
- ?2.7、釋放一些相關的資源和內存
- 🎄三、libpng 寫入 png 圖片文件步驟
- ?3.1、1、打開要寫入的png文件
- ?2.2、初始化libpng的數據結構 :png_ptr, info_ptr
- ?2.3、設置錯誤返回點
- ?2.4、初始化 io
- ?2.5、設置圖片屬性
- ?2.6、寫文件頭
- ?2.7、寫入實際的rgb數據
- ?2.8、寫入文件尾
- ?2.9、釋放一些相關的資源和內存
- 🎄四、總結
🎄一、概述
前面的文章介紹過怎么將 libpng 庫進行編譯、交叉編譯。需要了解的可以參考:zlib-1.2.11庫、libpng-1.6.36庫編譯及交叉編譯。
這篇文章主要介紹怎么使用 libpng 庫讀寫png圖片文件。如果需要了解更多的關于 libpng 庫的操作,可以看看 libpng 庫源碼中的 example.c
文件。
🎄二、libpng 讀取 png 圖片文件步驟
?2.1、打開文件并檢查是否png文件
libpng 讀取 png 圖片文件的第一個步驟就是打開 png 圖片,因為png圖片文件最前面有8個字節的png標識,所以先使用png_sig_cmp
函數檢測一下當前文件是否是 png 文件。
// 1、打開文件并檢查是否png文件
/* Open the prospective PNG file. */
if ((fp = fopen(file_name, "rb")) == NULL)goto read_end;/* Read in some of the signature bytes. */
if (fread(buf, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK)goto read_end;/* Compare the first PNG_BYTES_TO_CHECK bytes of the signature.* Return nonzero (true) if they match.*/
if (0 != png_sig_cmp((png_const_bytep)buf, 0, PNG_BYTES_TO_CHECK))goto read_end;
?2.2、初始化libpng的數據結構 :png_ptr, info_ptr
libpng庫讀取文件時需要用到兩個指針png_structp, png_infop
,分別標識 內部表述結構體 和 png圖片信息結構體,第二步需要初始化這兩結構體。
// 2、初始化libpng的數據結構 :png_ptr, info_ptr
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL)
{goto read_end;
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL)
{goto read_end;
}
?2.3、設置錯誤返回點
// 3、設置錯誤返回點
if (setjmp(png_jmpbuf(png_ptr)))
{/* Free all of the memory associated with the png_ptr and info_ptr. */png_destroy_read_struct(&png_ptr, &info_ptr, NULL);if (NULL != fp){fclose(fp);fp = NULL;}/* If we get here, we had a problem reading the file. */return -1;}
?2.4、初始化 io
初始化 io, 把png結構體和文件流io進行綁定 。
// 4、初始化 io, 把png結構體和文件流io進行綁定
png_init_io(png_ptr, fp);
?2.5、讀取png文件信息
讀取文件信息,包括顏色類型、位深度、寬、高、通道數量等。
位深度,指 ARGB 單個通道的顏色占用了多少個bit,如 RGB888 的位深度是8,RGB555的位深度是5。
通道數量,指多少個顏色通道,ARGB是4,RGB是3。
// 5.讀取文件信息
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);
int color_type = png_get_color_type(png_ptr, info_ptr); // 顏色類型
p_pic_data->bit_depth = png_get_bit_depth(png_ptr, info_ptr); // 位深度
p_pic_data->width = png_get_image_width(png_ptr, info_ptr); // 寬
p_pic_data->height = png_get_image_height(png_ptr, info_ptr); // 高
p_pic_data->channels = png_get_channels(png_ptr, info_ptr); // 通道數量
?2.6、讀取實際的rgb數據
libpng 庫讀取rgb數據的函數有如下幾個:
png_get_rowbytes
:獲取每一行的 rgb 數據;
png_get_rows
:獲取整個圖片的 rgb 數據;
png_read_row
:讀取一行的 rgb 數據;
png_read_rows
:讀取多行的 rgb 數據;
png_read_image
:讀取整個圖片的 rgb 數據
// 6.讀取實際的rgb數據
int i, j, k;
int size, pos = 0;
png_bytepp row_pointers; // 實際存儲rgb數據的buf
row_pointers = png_get_rows(png_ptr, info_ptr); // 也可以分別每一行獲取png_get_rowbytes();
size = p_pic_data->width * p_pic_data->height * p_pic_data->channels; // 申請內存先計算空間
if (p_pic_data->channels == 4 || color_type == PNG_COLOR_TYPE_RGB_ALPHA) // 顏色深度32位,帶有 Alpha 通道
{const int stride = 4; // 跨度,一個像素表示的字節數p_pic_data->rgba = (png_bytep)malloc(size);if (NULL == p_pic_data->rgba){printf("malloc rgba failed ...\n");goto read_end;}// 從row_pointers里讀出實際的rgba數據出來for (i = 0; i < p_pic_data->height; i++){for (j = 0; j < p_pic_data->width * stride; j += stride){for (k = 0; k < stride; k++){p_pic_data->rgba[pos++] = row_pointers[i][j + k];}}}
}
?2.7、釋放一些相關的資源和內存
讀取完成后,需要釋放相關的內存和資源。
read_end: // 釋放一些相關的資源和內存if (NULL != fp){fclose(fp);fp = NULL;}if (png_ptr != NULL){if (info_ptr == NULL)png_destroy_read_struct(&png_ptr, NULL, NULL);elsepng_destroy_read_struct(&png_ptr, &info_ptr, NULL);}return ret;
🎄三、libpng 寫入 png 圖片文件步驟
?3.1、1、打開要寫入的png文件
首先,打開要寫入的 png 文件,后面libpng庫也會用到這個文件指針。
// 1、打開要寫入的png文件
if ((fp = fopen(file_name, "wb+")) == NULL)goto write_end;
?2.2、初始化libpng的數據結構 :png_ptr, info_ptr
libpng庫寫文件時需要用到兩個指針png_structp, png_infop
,分別標識 內部表述結構體 和 png圖片信息結構體,第二步需要初始化這兩結構體。
注意,這里使用的是 png_create_write_struct
,與讀取時有區別。
// 2、初始化libpng的數據結構 :png_ptr, info_ptr
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL)
{goto write_end;
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL)
{goto write_end;
}
?2.3、設置錯誤返回點
// 3、設置錯誤返回點
if (setjmp(png_jmpbuf(png_ptr)))
{goto write_end;
}
?2.4、初始化 io
初始化 io, 把png結構體和文件流io進行綁定 。
// 4、初始化 io, 把png結構體和文件流io進行綁定
png_init_io(png_ptr, fp);
?2.5、設置圖片屬性
設置圖片屬性:顏色類型、寬、高、位深等。
// 5、設置圖片屬性
int color_type = PNG_COLOR_TYPE_RGB_ALPHA;
int interlace = 0;
png_set_IHDR(png_ptr, info_ptr, p_pic_data->width, p_pic_data->height, p_pic_data->bit_depth, color_type,(!interlace) ? PNG_INTERLACE_NONE : PNG_INTERLACE_ADAM7,PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
?2.6、寫文件頭
// 6、寫文件頭
png_write_info(png_ptr, info_ptr);
?2.7、寫入實際的rgb數據
libpng 庫寫入rgb數據的函數有如下幾個:
png_write_row
:寫入一行的 rgb 數據;
png_write_rows
:寫入多行的 rgb 數據;
png_write_image
:寫入整個圖片的 rgb 數據;
// 7、寫入圖片信息
png_bytep p_row_pointers;
int i = 0;
for (i = 0; i < p_pic_data->height; i++)
{p_row_pointers = (png_bytep)(p_pic_data->rgba + (i * p_pic_data->width * p_pic_data->channels));png_write_rows(png_ptr, &p_row_pointers, 1);
}
?2.8、寫入文件尾
// 8、寫入文件尾png_write_end(png_ptr, info_ptr);
?2.9、釋放一些相關的資源和內存
寫入完成后,需要釋放相關的內存和資源。
write_end: // 釋放一些相關的資源和內存if (NULL != fp){fclose(fp);fp = NULL;}if (png_ptr != NULL){if (info_ptr == NULL)png_destroy_read_struct(&png_ptr, NULL, NULL);elsepng_destroy_read_struct(&png_ptr, &info_ptr, NULL);}return ret;
🎄四、總結
本文先介紹了使用 libpng 庫讀取png文件的步驟,然后又介紹了使用 libpng 庫寫入png文件的步驟,最后給出了源碼地址。
根據上面的步驟,你應該可以自己寫一個讀寫png圖片的 .c 源文件了,如果還有問題可以下載下面源碼,本文源碼地址:https://download.csdn.net/download/wkd_007/91134800。
如果文章有幫助的話,點贊👍、收藏?,支持一波,謝謝 😁😁😁
參考:
libpng庫編碼圖片為png