libpng 源码的使用 第四节:写 (接口)
上一篇
libpng 源碼的使用 第四節:寫 (設置)
主要是一些初始化及輔助信息設置,及整體概念介紹,本篇主要是寫操作的接口即用戶接口介紹
高級寫接口
在這一點上,有兩種方法可以進行: 通過高級寫接口或一系列低級寫操作。如果信息結構中存在圖像數據,則可以使用高級接口。 允許所有定義的輸出轉換,并通過以下掩碼啟用。
PNG_TRANSFORM_IDENTITY No transformationPNG_TRANSFORM_PACKING Pack 1, 2 and 4-bit samplesPNG_TRANSFORM_PACKSWAP Change order of packedpixels to LSB firstPNG_TRANSFORM_INVERT_MONO Invert monochrome imagesPNG_TRANSFORM_SHIFT Normalize pixels to thesBIT depthPNG_TRANSFORM_BGR Flip RGB to BGR, RGBAto BGRAPNG_TRANSFORM_SWAP_ALPHA Flip RGBA to ARGB or GAto AGPNG_TRANSFORM_INVERT_ALPHA Change alpha from opacityto transparencyPNG_TRANSFORM_SWAP_ENDIAN Byte-swap 16-bit samplesPNG_TRANSFORM_STRIP_FILLER Strip out fillerbytes (deprecated).PNG_TRANSFORM_STRIP_FILLER_BEFORE Strip out leadingfiller bytesPNG_TRANSFORM_STRIP_FILLER_AFTER Strip out trailingfiller bytes如果信息結構中有有效的圖像數據(可以使用png_set_rows()將圖像數據放入信息結構中),則只需執行以下操作:
png_write_png(png_ptr, info_ptr, png_transforms, NULL)其中png_transforms是一個整數,其中包含一些轉換標志集的按位或。 此調用等效于png_write_info(),然后遵循由轉換掩碼指示的一組轉換,然后是png_write_image(),最后是png_write_end()。
(此調用的最終參數尚未使用??傆幸惶?#xff0c;它可能指向某些將來的輸出轉換所需的轉換參數。)
使用png_write_png()時,必須使用png_transforms且不能調用任何png_set_transform()函數。
底層寫接口
如果要改用低級路由,則現在可以將所有文件信息寫入實際的圖像數據。 您可以通過調用png_write_info()來實現。
png_write_info(png_ptr, info_ptr);請注意,在png_write_info()之前,您可能需要執行一次轉換。 在PNG文件中,圖像中的Alpha通道為不透明度級別。 如果以透明級別提供數據,則可以在寫入之前反轉alpha通道,以使0完全透明,而255(在8位或調色板圖像中)或65535(在16位圖像中)完全不透明。 使用
png_set_invert_alpha(png_ptr);這必須出現在png_write_info()之前,而不是其他轉換之后,因為對于調色板圖像,tRNS塊數據必須在tRNS之前反轉
? 塊被寫入。 如果您的圖像不是調色板圖像,則無需更改tRNS數據(在這種情況下,該顏色表示要呈現為透明的單一顏色),并且可以在調用png_write_info()之后安全地進行此轉換。
如果需要在存在PLTE的情況下編寫要顯示在PLTE塊之前的私有塊,則可以分兩步編寫PNG信息,并在其中插入代碼以編寫自己的塊:
寫入文件信息后,可以設置該庫以處理圖像數據的任何特殊轉換。 將按照應該發生的順序描述轉換數據的各種方式。 這很重要,因為其中一些會更改數據的顏色類型和/或位深,而其他一些僅適用于某些顏色類型和位深。 即使每個轉換都檢查它是否具有可以執行某些操作的數據,也應確保僅在對數據有效的情況下才啟用轉換。 例如,不要在灰度數據上交換紅色和藍色。
PNG文件存儲RGB像素,打包成3或6個字節。 此代碼告訴庫將每個像素具有4或8字節的輸入數據剝離到3或6字節(或將2或4字節的灰度+填充數據剝離到每個像素1或2字節)。
其中未使用0,其位置是PNG_FILLER_BEFORE或PNG_FILLER_AFTER,具體取決于像素中的填充字節是存儲的XRGB還是RGBX。
PNG文件將位深度1、2和4的像素打包到盡可能小的字節中,例如1位文件每字節8個像素。如果以每字節1像素提供數據,請使用此代碼 ,它將正確地將像素打包為一個字節:
PNG文件將可能的位深度減小為1、2、4、8和16。如果您的數據具有其他位深度,則可以將sBIT塊寫入文件中,以便解碼器可以根據需要恢復原始數據。
/* Set the true bit depth of the image data */if (color_type & PNG_COLOR_MASK_COLOR){sig_bit.red = true_bit_depth;sig_bit.green = true_bit_depth;sig_bit.blue = true_bit_depth;}else{sig_bit.gray = true_bit_depth;}if (color_type & PNG_COLOR_MASK_ALPHA){sig_bit.alpha = true_bit_depth;}png_set_sBIT(png_ptr, info_ptr, &sig_bit);如果數據以除PNG支持的位深度以外的位深度存儲在行緩沖區中(例如,對于4位PNG,3位數據在0-7范圍內),這將縮放這些值以顯示為正確的位深度 PNG要求的理由。
png_set_shift(png_ptr, &sig_bit);PNG文件以網絡字節順序(big-endian,即,最高有效位在前)存儲16位像素。 如果以其他方式(即,最低有效位在前,即PC的存儲方式)提供它們,則將使用此代碼:
if (bit_depth > 8)png_set_swap(png_ptr);如果使用的是像素壓縮圖像(1、2或4位/像素),并且需要更改像素壓縮為字節的順序,則可以使用:
if (bit_depth < 8)png_set_packswap(png_ptr);PNG文件以紅色,綠色,藍色的順序存儲3個彩色像素。 如果以藍色,綠色,紅色提供它們,則將使用以下代碼:
png_set_bgr(png_ptr);PNG文件將單色描述為黑色為零,白色為1。 如果提供的像素具有相反的顏色(黑色為一,白色為零),則將使用以下代碼:
png_set_invert_mono(png_ptr);最后,如果現有的轉換函數都不滿足您的需求,則可以編寫自己的轉換函數。 這是通過使用以下命令設置回調來完成的
png_set_write_user_transform_fn(png_ptr,write_transform_fn);您必須提供功能
void write_transform_fn(png_structp png_ptr, png_row_infoprow_info, png_bytep data)有關工作示例,請參見pngtest.c。 在處理任何其他轉換之前,將調用您的函數。 如果supportedlibpng也提供了一個信息例程,該例程可以從您的回調中調用:
png_get_current_row_number(png_ptr);png_get_current_pass_number(png_ptr);這將返回傳遞給轉換的當前行。 對于隔行圖像,返回的值是輸入子圖像圖像中的行。 使用PNG_ROW_FROM_PASS_ROW(行,pass)和PNG_COL_FROM_PASS_COL(行,pass)來查找給定隔行掃描子圖像像素(行,行,pass)的輸出像素(x,y)。
上面有關隔行處理的討論包含有關如何使用這些值的更多信息。
您還可以設置一個指向用戶結構的指針,以供您的回調函數使用。
寫入時將忽略此函數的user_channels和user_depth參數; 您可以將它們設置為零,如圖所示。
您可以通過函數png_get_user_transform_ptr()檢索指針。 例如:
可以在寫入一定數量的行之后,使libpng手動或自動刷新任何未決的輸出。 要一次刷新輸出流,請執行以下操作:
png_write_flush(png_ptr);并在寫入一定數量的掃描線后讓libpng定期刷新輸出流,請調用:
png_set_flush(png_ptr, nrows);請注意,行之間的距離是從上次調用png_write_flush()以來的距離,或者從圖像的第一行開始(如果從未調用過)。 因此,如果先寫入50行,然后寫入png_set_flush 25,它將刷新下一條掃描線的輸出,此后每25行刷新一次,除非在寫入25行之前調用png_write_flush()。 如果行數太小(對于640像素寬的RGB圖像,少于大約10條線),則圖像壓縮可能會顯著降低(盡管這對于實時應用程序是可以接受的)。 與不使用刷新的圖像相比,不頻繁的刷新只會使壓縮性能降低百分之幾。
意思就是在使用?png_write_rows 或者?png_write_row 手動寫入圖像數據的時候,最好調用?png_write_flush 對圖像數據進行刷新。如果不手動調用,可以設置 png_set_flush (n)自動調用 n為 調用間隔,調用間隔越大,壓縮率越高。 這樣理解的,還沒有詳細看?png_write_flush 的實現。
寫入圖像數據
轉換就是這樣。 現在您可以寫入圖像數據了。 最簡單的方法是在一個函數調用中。 如果整個圖像都在內存中,則只需調用png_write_image(),libpng就會寫入圖像。 您將需要將指針數組傳遞給每一行。 該函數會自動處理隔行掃描,因此您無需調用png_set_interlace_handling()或多次調用此函數,也無需調用png_write_rows()所需的其他任何東西。
png_write_image(png_ptr, row_pointers);其中row_pointers是:
png_byte *row_pointers[height];您可以指向void或char或任何用于像素的對象。
如果您不想一次寫入整個圖像,則可以改用png_write_rows()。 如果文件不是隔行掃描的,這很簡單:
row_pointers與png_write_image()調用中的相同。
如果您一次只寫一行,則可以使用一個row_pointer而不是row_pointers數組來做到這一點:
當文件隔行掃描時,事情可能會變得更加復雜。 當前(自1999年7月發布的PNG規范版本1.2)定義的唯一PNG隔行掃描方案是“ Adam7”隔行掃描方案,它將一個圖像分解為七個大小不同的較小圖像。 libpng將為您構建這些圖像,或者您可以自己完成。 如果要自己構建它們,請參閱PNG規范以了解何時寫入的像素的詳細信息。
如果您不希望libpng處理隔行掃描細節,只需使用png_set_interlace_handling()并以正確的次數調用png_write_rows()來寫入所有子圖像(png_set_interlace_handling()返回子圖像的數目。)
如果要使用libpng構建子圖像,請在開始編寫任何行之前調用此方法:
這將返回所需的通過次數。 當前為7,但是如果添加其他隔行掃描類型,則可能會更改。
然后寫完整的圖像number_of_passes次。
寫入隔行掃描圖像之前,請仔細考慮。通常,讀取此類圖像的代碼會在執行任何處理之前將所有圖像數據未經壓縮地讀入內存。只有可以即時顯示圖像的代碼才能利用隔行掃描,即使這樣,圖像也必須恰好是輸出設備的正確大小,因為縮放圖像需要相鄰的像素,并且只有在所有遍數都經過后才能使用這些像素。被閱讀。
如果您確實寫了隔行掃描圖像,則幾乎不需要自己處理隔行掃描。調用png_set_interlace_handling()并使用上述方法。
唯一可以想象的是,您真正需要編寫隔行掃描圖像的過程是,當您逐遍讀取一次并對其進行了逐像素轉換時,如上面的讀取代碼中所述。在這種情況下,請使用PNG_PASS_ROWS和PNG_PASS_COLS宏依次確定每個子圖像的大小,并簡單地寫入從讀取代碼中獲得的行。
完成順序寫入
完成寫入圖像后,應該完成寫入文件。 如果您有興趣編寫評論或時間,則應傳遞適當填充的png_info指針。 如果您不感興趣,則可以傳遞NULL。
png_write_end(png_ptr, info_ptr);完成后,可以釋放libpng使用的所有內存,如下所示:
png_destroy_write_struct(&png_ptr, &info_ptr);也可以使用以下函數分別釋放指向libpng分配的存儲的info_ptr成員:
png_free_data(png_ptr, info_ptr, mask, seq)mask - identifies data to be freed, a maskcontaining the bitwise OR of one ormore ofPNG_FREE_PLTE, PNG_FREE_TRNS,PNG_FREE_HIST, PNG_FREE_ICCP,PNG_FREE_PCAL, PNG_FREE_ROWS,PNG_FREE_SCAL, PNG_FREE_SPLT,PNG_FREE_TEXT, PNG_FREE_UNKN,or simply PNG_FREE_ALLseq - sequence number of item to be freed(-1 for all items)當相關存儲已被釋放,尚未分配,或者由用戶而非libpng分配時,可以安全地調用此函數,在這些情況下將不執行任何操作。 如果僅允許所選數據類型的一項(例如PLTE),則忽略“ seq”參數。 如果“ seq”不為-1,并且掩碼中標識的數據類型允許多個項目(例如文本或sPLT),則僅釋放結構中的第n個項目,其中n為“ seq”。
如果分配的數據(例如使用png_set_ *傳遞給libpng的調色板),則必須在調用png_destroy_write_struct()之前將其釋放。
默認行為是僅釋放由libpng內部分配的數據。 可以更改它,以便libpng不會釋放數據,或者它將釋放用戶使用png_malloc()或png_calloc()分配并通過png_set _ *()函數傳入的數據,
例如,要將某些數據的責任從讀取結構轉移到寫入結構,可以使用
png_data_freer(read_ptr, read_info_ptr,PNG_USER_WILL_FREE_DATA,PNG_FREE_PLTE|PNG_FREE_tRNS|PNG_FREE_hIST)png_data_freer(write_ptr, write_info_ptr,PNG_DESTROY_WILL_FREE_DATA,PNG_FREE_PLTE|PNG_FREE_tRNS|PNG_FREE_hIST)從而短暫地將釋放用戶的責任重新分配給用戶,但之后立即又將其再次分配給write_destroy函數。完成此操作后,銷毀讀取結構并繼續在寫入結構中使用PLTE,tRNS和hIST數據將是安全的。
此功能僅影響已經分配的數據。您可以先調用此函數,然后再調用png_set _ *()函數以控制用戶還是png_destroy _ *()應該釋放數據。當用戶對libpng分配的數據承擔責任時,應用程序必須使用png_free()釋放它,并且當用戶將對用戶分配的數據的責任轉移給libpng時,用戶必須已使用png_malloc()或png_calloc()分配它。
如果您分別分配了text_ptr.text,text_ptr.lang和text_ptr.translated_keyword,則不要將釋放text_ptr的責任轉移給libpng,因為當libpng填充png_text結構時,它將這些成員與鍵成員結合在一起,而png_free_data()僅會釋放text_ptr.key。同樣,如果您將釋放text_ptr的責任從libpng轉移到您的應用程序,則您的應用程序不得單獨釋放這些成員。有關編寫PNG圖像的更緊湊的示例,請參見文件example.c。
寫入機器翻譯完,下一篇是簡單用戶接口,對使用也比較重要。
?
?
總結
以上是生活随笔為你收集整理的libpng 源码的使用 第四节:写 (接口)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux怎么生成sct文件,sct文件
- 下一篇: 云种植app