前面翻译了读png图像,接下来看写入png,即压缩
IV. 写入
其中大部分与 读 非常相似。 但是,在这里重复了所有重要的事情,因此您不必经常在阅读部分中进行回顾就可以理解写操作。
设置
在进入libpng之前,您将需要进行I / O初始化,因此,如果它不起作用,则没有任何要撤消的操作。 如果不使用标准I / O功能,则需要用自定义编写功能替换它们。 请参阅自定义libpng下的讨论。
FILE *fp = fopen(file_name, "wb");
if (!fp)
return ERROR;
接下来,需要分配并初始化png_struct和png_info,因为它们都可能相对较大,因此除非您有足够的可用堆栈空间,否则您可能不希望将它们存储在堆栈中。 当然,您将要检查它们是否返回NULL。 如果您还在阅读,则不想将您的读取结构和写入结构都命名为“ png_ptr”; 您可以将它们命名为任意名称,例如“ read_ptr”和“ write_ptr”。 例如,查看pngtest.c。
png_structp png_ptr = png_create_write_struct
(PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
user_error_fn, user_warning_fn);
if (!png_ptr)
return ERROR;
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
{
png_destroy_write_struct(&png_ptr,
(png_infopp)NULL);
return ERROR;
}
如果要使用自己的内存分配例程,请定义PNG_USER_MEM_SUPPORTED并使用png_create_write_struct_2()而不是png_create_write_struct():
png_structp png_ptr =
png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
(png_voidp)user_error_ptr,
user_error_fn, user_warning_fn, (png_voidp)
user_mem_ptr, user_malloc_fn, user_free_fn);
具有这些结构之后,将需要设置错误处理。 当libpng遇到错误时,它期望将tolongjmp()返回到您的例程。 因此,您将需要调用setjmp()并传递png_jmpbuf(png_ptr)。 如果从不同的例程编写文件,则每次输入新的调用png _ *()函数的例程时,都需要更新png_jmpbuf(png_ptr)。 有关setjmp / longjmp的更多信息,请参见编译器的setjmp / longjmp文档。 有关libpng错误处理的更多信息,请参见下面“自定义Libpng”部分中有关libpng错误处理的讨论。
if (setjmp(png_jmpbuf(png_ptr)))
{
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(fp);
return ERROR;
}
...
return;
如果您希望避免setjmp / longjmp问题的复杂性,则可以使用PNG_NO_SETJMP编译libpng,在这种情况下,错误将导致对PNG_ABORT()的调用,该调用默认为abort()。
只要您的函数不返回,就可以#define PNG_ABORT()一个比abort()有用的函数。
在libpng1.5.10中添加了检查写入时无效的调色板索引的功能。 如果像素包含无效(超出范围)的索引,则libpng会发出良性错误。 默认情况下启用此功能,因为根据PNG规范第11.3.2节,此条件是错误,但是可以在每个png_ptr中使用以下命令忽略该错误:
png_set_check_for_invalid_index(png_ptr, 0);
如果忽略该错误,或者如果png_benign_error()将其视为警告,则编码器将按原样写入任何无效像素,从而导致无效的PNG数据流作为输出。 在这种情况下,当应用程序写入的PLTE块的条目数少于比特深度所允许的数量时,该应用程序负责确保像素索引在范围内。
现在,您需要设置输出代码。 libpng的默认设置是使用C函数fwrite()。 如果使用此功能,则需要在函数png_init_io()中传递有效的FILE *。 确保以二进制模式打开文件。 同样,如果您希望以其他方式处理写入数据,请参见下面的CustomizingLibpng部分中有关libpng I / O处理的讨论。
png_init_io(png_ptr, fp);
如果您要将PNG嵌入到诸如MORNING之类的数据流中,并且不想libpng编写8字节签名,或者如果您已经在应用程序中编写了签名,请使用
png_set_sig_bytes(png_ptr, 8);
通知libpng它不应该写签名。
写回调函数
此时,您可以设置一个回调函数,该函数将在写入每一行之后被调用,该函数可用于控制进度表等。 已在pngtest.c中进行了演示。您必须提供一个函数
void write_row_callback(png_structp png_ptr, png_uint_32 row,int pass);
{
/* put your code here */
}
(您可以给它取一个自己喜欢的名字,而不是“ write_row_callback”)
要通知libpng有关您的功能,请使用
png_set_write_status_fn(png_ptr, write_row_callback);
调用此函数时,该行已被完全处理,并且也已被写出。 “行”和“pass”是指要处理的下一行。对于非隔行的情况,刚处理的行仅比传入的行号少一个,并且pass始终为0。对于隔行的情况,除非行值为0,否则适用相同的情况,在这种情况下,刚刚处理的行为前一个通行证中的最后一个。因为隔行扫描可能会跳过一个传递,所以如果您确实需要知道回调中记录的最后一个传递(行,传递)是什么,并且每次都使用最后一个记录的值,则不能确保前一个传递只是“pass-1”。
与用户转换一样,您可以使用PNG_ROW_FROM_PASS_ROW宏找到输出行。
现在,您可以选择修改压缩库的运行方式。以下功能主要用于测试,但在某些情况下可能有用,例如,如果您需要极快地编写PNG文件并愿意放弃某些压缩,或者如果您希望以最大的压缩代价获得最大的压缩,则可以使用这些功能。如果您在这方面没有特殊需要,请通过完全不调用此函数来让库执行所需的操作,因为它已经过调整以提供良好的速度/压缩比。 png_set_filter()的第二个参数是filter方法,其唯一有效值为0(从1999年7月的PNG规范,版本1.2开始)或64(如果要编写要嵌入在MNG数据流中的PNG数据流)。第三个参数是一个标志,指示要为每条扫描线测试的过滤器类型。有关特定过滤器类型的详细信息,请参见PNG规范。
/* turn on or off filtering, and/or choose
specific filters. You can use either a single
PNG_FILTER_VALUE_NAME or the bitwise OR of one
or more PNG_FILTER_NAME masks.
*/
png_set_filter(png_ptr, 0,
PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE |
PNG_FILTER_SUB | PNG_FILTER_VALUE_SUB |
PNG_FILTER_UP | PNG_FILTER_VALUE_UP |
PNG_FILTER_AVG | PNG_FILTER_VALUE_AVG |
PNG_FILTER_PAETH | PNG_FILTER_VALUE_PAETH|
PNG_ALL_FILTERS | PNG_FAST_FILTERS);
如果应用程序要在压缩过程中启动和停止使用特定的滤镜,则应从所有滤镜开始(以确保在以后需要时存储前一行像素),然后在启动后添加和删除它们 压缩。
如果您正在编写要嵌入在MNGdatastream中的PNG数据流,则第二个参数可以是0或64。
png_set_compression _ *()函数连接到zlib压缩库,除非您真的知道自己在做什么,否则应该将其忽略。 唯一通用的调用是png_set_compression_level(),它更改zlib尝试压缩图像数据所花费的时间。 有关压缩级别的详细信息,请参见压缩库(zlib.h和algorithm.txt,与zlib一起分发)。
#include zlib.h
/* Set the zlib compression level */
png_set_compression_level(png_ptr,
Z_BEST_COMPRESSION);
/* Set other zlib parameters for compressing IDAT */
png_set_compression_mem_level(png_ptr, 8);
png_set_compression_strategy(png_ptr,
Z_DEFAULT_STRATEGY);
png_set_compression_window_bits(png_ptr, 15);
png_set_compression_method(png_ptr, 8);
png_set_compression_buffer_size(png_ptr, 8192)
/* Set zlib parameters for text compression
* If you don't call these, the parameters
* fall back on those defined for IDAT chunks
*/
png_set_text_compression_mem_level(png_ptr, 8);
png_set_text_compression_strategy(png_ptr,
Z_DEFAULT_STRATEGY);
png_set_text_compression_window_bits(png_ptr, 15);
png_set_text_compression_method(png_ptr, 8);
设置信息内容以进行输出
现在,您需要在实际图像之前用希望写入的所有数据填充png_info结构。 请注意,在图像之后唯一可以写的是文本块和时间块(无论如何,从PNG规范1.2开始)。 有关更多信息,请参见png_write_end()和最新的PNG规范。 如果您想在图像之前写入它们,请立即填写它们,并将该数据标记为有效。 如果要等到数据之后再使用,请不要填充它们,直到png_write_end()。 有关png_info及其数据类型中的所有字段,请参见png.h。 有关字段包含内容的说明,请参见PNG规范。
png_info的一些更重要的部分是:
png_set_IHDR(png_ptr, info_ptr, width, height,
bit_depth, color_type, interlace_type,
compression_type, filter_method)
width - holds the width of the image
in pixels (up to 2^31).
height - holds the height of the image
in pixels (up to 2^31).
bit_depth - holds the bit depth of one of the
image channels.
(valid values are 1, 2, 4, 8, 16
and depend also on the
color_type. See also significant
bits (sBIT) below).
color_type - describes which color/alpha
channels are present.
PNG_COLOR_TYPE_GRAY
(bit depths 1, 2, 4, 8, 16)
PNG_COLOR_TYPE_GRAY_ALPHA
(bit depths 8, 16)
PNG_COLOR_TYPE_PALETTE
(bit depths 1, 2, 4, 8)
PNG_COLOR_TYPE_RGB
(bit_depths 8, 16)
PNG_COLOR_TYPE_RGB_ALPHA
(bit_depths 8, 16)
PNG_COLOR_MASK_PALETTE
PNG_COLOR_MASK_COLOR
PNG_COLOR_MASK_ALPHA
interlace_type - PNG_INTERLACE_NONE or
PNG_INTERLACE_ADAM7
compression_type - (must be
PNG_COMPRESSION_TYPE_DEFAULT)
filter_method - (must be PNG_FILTER_TYPE_DEFAULT
or, if you are writing a PNG to
be embedded in a MNG datastream,
can also be
PNG_INTRAPIXEL_DIFFERENCING)
如果调用png_set_IHDR(),则该调用必须出现在任何其他png_set _ *()函数之前,因为它们可能需要访问某些IHDR设置。 其余png_set _ *()函数可以按任何顺序调用。
如果愿意,可以稍后再次调用png_set_IHDR()来重置compression_type,interlace_type或filter_method。 如果这样做,则每个调用中的width,height,bit_depth和color_type必须相同。
png_set_PLTE(png_ptr, info_ptr, palette,
num_palette);
palette - the palette for the file
(array of png_color)
num_palette - number of entries in the palette
png_set_gAMA(png_ptr, info_ptr, file_gamma);
png_set_gAMA_fixed(png_ptr, info_ptr, int_file_gamma);
file_gamma - the gamma at which the image was
created (PNG_INFO_gAMA)
int_file_gamma - 100,000 times the gamma at which
the image was created
png_set_cHRM(png_ptr, info_ptr, white_x, white_y, red_x, red_y,
green_x, green_y, blue_x, blue_y)
png_set_cHRM_XYZ(png_ptr, info_ptr, red_X, red_Y, red_Z, green_X,
green_Y, green_Z, blue_X, blue_Y, blue_Z)
png_set_cHRM_fixed(png_ptr, info_ptr, int_white_x, int_white_y,
int_red_x, int_red_y, int_green_x, int_green_y,
int_blue_x, int_blue_y)
png_set_cHRM_XYZ_fixed(png_ptr, info_ptr, int_red_X, int_red_Y,
int_red_Z, int_green_X, int_green_Y, int_green_Z,
int_blue_X, int_blue_Y, int_blue_Z)
{white,red,green,blue}_{x,y}
A color space encoding specified using the chromaticities
of the end points and the white point.
{red,green,blue}_{X,Y,Z}
A color space encoding specified using the encoding end
points - the CIE tristimulus specification of the intended
color of the red, green and blue channels in the PNG RGB
data. The white point is simply the sum of the three end
points.
png_set_sRGB(png_ptr, info_ptr, srgb_intent);
srgb_intent - the rendering intent
(PNG_INFO_sRGB) The presence of
the sRGB chunk means that the pixel
data is in the sRGB color space.
This chunk also implies specific
values of gAMA and cHRM. Rendering
intent is the CSS-1 property that
has been defined by the International
Color Consortium
(http://www.color.org).
It can be one of
PNG_sRGB_INTENT_SATURATION,
PNG_sRGB_INTENT_PERCEPTUAL,
PNG_sRGB_INTENT_ABSOLUTE, or
PNG_sRGB_INTENT_RELATIVE.
png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr,
srgb_intent);
srgb_intent - the rendering intent
(PNG_INFO_sRGB) The presence of the
sRGB chunk means that the pixel
data is in the sRGB color space.
This function also causes gAMA and
cHRM chunks with the specific values
that are consistent with sRGB to be
written.
png_set_iCCP(png_ptr, info_ptr, name, compression_type,
profile, proflen);
name - The profile name.
compression_type - The compression type; always
PNG_COMPRESSION_TYPE_BASE for PNG 1.0.
You may give NULL to this argument to
ignore it.
profile - International Color Consortium color
profile data. May contain NULs.
proflen - length of profile data in bytes.
png_set_sBIT(png_ptr, info_ptr, sig_bit);
sig_bit - the number of significant bits for
(PNG_INFO_sBIT) each of the gray, red,
green, and blue channels, whichever are
appropriate for the given color type
(png_color_16)
png_set_tRNS(png_ptr, info_ptr, trans_alpha,
num_trans, trans_color);
trans_alpha - array of alpha (transparency)
entries for palette (PNG_INFO_tRNS)
num_trans - number of transparent entries
(PNG_INFO_tRNS)
trans_color - graylevel or color sample values
(in order red, green, blue) of the
single transparent color for
non-paletted images (PNG_INFO_tRNS)
png_set_eXIf_1(png_ptr, info_ptr, num_exif, exif);
exif - Exif profile (array of
png_byte) (PNG_INFO_eXIf)
png_set_hIST(png_ptr, info_ptr, hist);
hist - histogram of palette (array of
png_uint_16) (PNG_INFO_hIST)
png_set_tIME(png_ptr, info_ptr, mod_time);
mod_time - time image was last modified
(PNG_VALID_tIME)
png_set_bKGD(png_ptr, info_ptr, background);
background - background color (of type
png_color_16p) (PNG_VALID_bKGD)
png_set_text(png_ptr, info_ptr, text_ptr, num_text);
text_ptr - array of png_text holding image
comments
text_ptr[i].compression - type of compression used
on "text" PNG_TEXT_COMPRESSION_NONE
PNG_TEXT_COMPRESSION_zTXt
PNG_ITXT_COMPRESSION_NONE
PNG_ITXT_COMPRESSION_zTXt
text_ptr[i].key - keyword for comment. Must contain
1-79 characters.
text_ptr[i].text - text comments for current
keyword. Can be NULL or empty.
text_ptr[i].text_length - length of text string,
after decompression, 0 for iTXt
text_ptr[i].itxt_length - length of itxt string,
after decompression, 0 for tEXt/zTXt
text_ptr[i].lang - language of comment (NULL or
empty for unknown).
text_ptr[i].translated_keyword - keyword in UTF-8 (NULL
or empty for unknown).
Note that the itxt_length, lang, and lang_key
members of the text_ptr structure only exist when the
library is built with iTXt chunk support. Prior to
libpng-1.4.0 the library was built by default without
iTXt support. Also note that when iTXt is supported,
they contain NULL pointers when the "compression"
field contains PNG_TEXT_COMPRESSION_NONE or
PNG_TEXT_COMPRESSION_zTXt.
num_text - number of comments
png_set_sPLT(png_ptr, info_ptr, &palette_ptr,
num_spalettes);
palette_ptr - array of png_sPLT_struct structures
to be added to the list of palettes
in the info structure.
num_spalettes - number of palette structures to be
added.
png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y,
unit_type);
offset_x - positive offset from the left
edge of the screen
offset_y - positive offset from the top
edge of the screen
unit_type - PNG_OFFSET_PIXEL, PNG_OFFSET_MICROMETER
png_set_pHYs(png_ptr, info_ptr, res_x, res_y,
unit_type);
res_x - pixels/unit physical resolution
in x direction
res_y - pixels/unit physical resolution
in y direction
unit_type - PNG_RESOLUTION_UNKNOWN,
PNG_RESOLUTION_METER
png_set_sCAL(png_ptr, info_ptr, unit, width, height)
unit - physical scale units (an integer)
width - width of a pixel in physical scale units
height - height of a pixel in physical scale units
(width and height are doubles)
png_set_sCAL_s(png_ptr, info_ptr, unit, width, height)
unit - physical scale units (an integer)
width - width of a pixel in physical scale units
expressed as a string
height - height of a pixel in physical scale units
(width and height are strings like "2.54")
png_set_unknown_chunks(png_ptr, info_ptr, &unknowns,
num_unknowns)
unknowns - array of png_unknown_chunk
structures holding unknown chunks
unknowns[i].name - name of unknown chunk
unknowns[i].data - data of unknown chunk
unknowns[i].size - size of unknown chunk's data
unknowns[i].location - position to write chunk in file
0: do not write chunk
PNG_HAVE_IHDR: before PLTE
PNG_HAVE_PLTE: before IDAT
PNG_AFTER_IDAT: after IDAT
根据已写入输出文件的什么部分,将自动设置“位置”成员。您可以在调用pngtest.c中演示的png_set_unknown_chunks()之后更改其值。在每个“位置”内,根据块在结构中的位置(即“ i”的值,即从输入文件中读取块或使用png_set_unknown_chunks定义块的顺序)对块进行排序。
关于文字和num_text的快速介绍。 text是png_textstructures的数组。 num_text是数组中有效结构的数量。每个png_text结构均包含语言代码,关键字,文本值和压缩类型。
压缩类型具有与图像数据的压缩类型相同的有效数字。当前,唯一的有效数字为零。但是,您可以存储压缩的或未压缩的文本,与必须始终压缩的图像不同。因此,如果您不想压缩文本,请将压缩类型设置为PNG_TEXT_COMPRESSION_NONE。由于tEXt和zTXt块没有语言字段,因此如果您指定PNG_TEXT_COMPRESSION_NONE或PNG_TEXT_COMPRESSION_zTXtany语言代码或翻译后的关键字将不会被写出。
在文本达到几百个字节之前,不值得对其进行压缩。将文本写出到文件后,将压缩类型设置为PNG_TEXT_COMPRESSION_NONE_WR或PNG_TEXT_COMPRESSION_zTXt_WR,这样就不会在末尾再次写出该文本了。如果您使用相同的结构调用png_write_end())。
PNG规范中给出的关键字是:
Title Short (one line) title or
caption for image
Author Name of image's creator
Description Description of image (possibly long)
Copyright Copyright notice
Creation Time Time of original image creation
(usually RFC 1123 format, see below)
Software Software used to create the image
Disclaimer Legal disclaimer
Warning Warning of nature of content
Source Device used to create the image
Comment Miscellaneous comment; conversion
from other image format
关键字-文本对的工作原理如下。关键字应该是评论内容的简短描述。在PNG规范中可以找到一些典型的关键字,以及对关键字的一些建议。您可以在文件中重复关键字。您甚至可以在图像之前和之后写入一些文本。例如,您可能希望在图像之前放置图像说明,但将免责声明保留到此之后,因此,通过调制解调器连接工作的查看者不必等到免责声明通过调制解调器之后,才可以开始查看图像。最后,关键字应该是全字词,而不是缩写词。关键字和文本在ISO 8859-1(Latin-1)字符集中(常规ASCII的超集),并且不能包含NUL字符,并且不应包含控制字符或其他不可打印的字符。为了使注释易于阅读,请坚持使用基本ASCII,并避免使用机器特定的字符集扩展名,例如IBM-PC字符集。关键字必须存在,但是您可以在非压缩对中省略文本字符串。压缩对必须具有文本字符串,因为无论如何只有文本字符串会被压缩,因此压缩将毫无意义。
PNG通过png_time结构支持修改时间。提供了两个转换例程,对于time_t提供了png_convert_from_time_t(),对于struct tm提供了png_convert_from_struct_tm()。 time_t例程使用gmtime()。您不必使用任何一种,但是如果您希望直接填写png_time结构,则应尽可能以世界标准时间(GMT)代替当地时间。请注意,年号是整年(例如1998,而不是98-PNG符合2000年!),并且月份从1开始。
如果要存储原始图像创建的时间,则应使用带有“ Creation Time”关键字的普通tEXt块。这是必要的,因为PNG图像的“创建时间”有些模糊,具体取决于您是否指的是PNG文件,以非PNG格式创建图像的时间,从中扫描图像的静态照片或对象本身。为了简化日期,尽管不是必须的,但是建议“创建时间” tEXt块使用RFC 1123格式的日期(例如“ 1997年5月22日18:07:10 GMT”)。与tIME块不同,预计“创建时间” tEXt块不会被软件自动更改。为了便于使用RFC 1123日期,提供了一个函数png_convert_to_rfc1123_buffer(buffer,png_timep),以便从PNG时间转换为RFC 1123格式字符串。调用者必须提供至少29个字节的可写缓冲区。
写未知的块
您可以使用png_set_unknown_chunks函数将私有块排队等待写入。 您给它一个块名称,位置,原始数据和一个大小。 您还必须使用png_set_keep_unknown_chunks()来确保libpng会处理它们。 这里的所有都是它的。 根据指定的位置,随后将在png_write_info_before_PLTE,png_write_info或png_write_endfunction之后写入这些块。 先前读取到信息结构的未知块列表中的任何块也将按照满足PNG规范的排序规则的顺序写出。
这是编写两个私有块prVt和miNE的示例:
#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
/* Set unknown chunk data */
png_unknown_chunk unk_chunk[2];
strcpy((char *) unk_chunk[0].name, "prVt";
unk_chunk[0].data = (unsigned char *) "PRIVATE DATA";
unk_chunk[0].size = strlen(unk_chunk[0].data)+1;
unk_chunk[0].location = PNG_HAVE_IHDR;
strcpy((char *) unk_chunk[1].name, "miNE";
unk_chunk[1].data = (unsigned char *) "MY CHUNK DATA";
unk_chunk[1].size = strlen(unk_chunk[0].data)+1;
unk_chunk[1].location = PNG_AFTER_IDAT;
png_set_unknown_chunks(write_ptr, write_info_ptr,
unk_chunk, 2);
/* Needed because miNE is not safe-to-copy */
png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS,
(png_bytep) "miNE", 1);
# if PNG_LIBPNG_VER < 10600
/* Deal with unknown chunk location bug in 1.5.x and earlier */
png_set_unknown_chunk_location(png, info, 0, PNG_HAVE_IHDR);
png_set_unknown_chunk_location(png, info, 1, PNG_AFTER_IDAT);
# endif
# if PNG_LIBPNG_VER < 10500
/* PNG_AFTER_IDAT writes two copies of the chunk prior to libpng-1.5.0,
* one before IDAT and another after IDAT, so don't use it; only use
* PNG_HAVE_IHDR location. This call resets the location previously
* set by assignment and png_set_unknown_chunk_location() for chunk 1.
*/
png_set_unknown_chunk_location(png, info, 1, PNG_HAVE_IHDR);
# endif
#endif
下一篇翻译 写操作的 高级接口