/* pngwrite.c - general routines to write a PNG file * *Copyright(c)2018-2026CosminTruta *Copyright(c)1998-2002,2004,2006-2018GlennRanders-Pehrson *Copyright(c)1996-1997AndreasDilger *Copyright(c)1995-1996GuyEricSchalnat,Group42,Inc. * *Thiscodeisreleasedunderthelibpnglicense. *Forconditionsofdistributionanduse,seethedisclaimer *andlicenseinpng.h
*/
#include"pngpriv.h" #ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED # include <errno.h> #endif/* SIMPLIFIED_WRITE_STDIO */
#ifdef PNG_WRITE_SUPPORTED
#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED /* Write out all the unknown chunks for the current given location */ staticvoid
write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr, unsignedint where)
{ if (info_ptr->unknown_chunks_num != 0)
{
png_const_unknown_chunkp up;
png_debug(5, "writing extra chunks");
for (up = info_ptr->unknown_chunks;
up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
++up) if ((up->location & where) != 0)
{ /* If per-chunk unknown chunk handling is enabled use it, otherwise *justwritethechunkstheapplicationhasset.
*/ #ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED int keep = png_handle_as_unknown(png_ptr, up->name);
/* NOTE: this code is radically different from the read side in the *matterofhandlinganancillaryunknownchunk.Inthereadside *thedefaultbehavioristodiscardit,inthecodebelowthedefault *behavioristowriteit.Criticalchunksare,however,only *writtenifexplicitlylistedorifthedefaultissettowriteall *unknownchunks. * *Thedefaulthandlingisalsoslightlyweird-itisnotpossibleto *stopthewritingofallunsafe-to-copychunks! * *TODO:REVIEW:thiswouldseemtobeabug.
*/ if (keep != PNG_HANDLE_CHUNK_NEVER &&
((up->name[3] & 0x20) /* safe-to-copy overrides everything */ ||
keep == PNG_HANDLE_CHUNK_ALWAYS ||
(keep == PNG_HANDLE_CHUNK_AS_DEFAULT &&
png_ptr->unknown_default == PNG_HANDLE_CHUNK_ALWAYS))) #endif
{ /* TODO: review, what is wrong with a zero length unknown chunk? */ if (up->size == 0)
png_warning(png_ptr, "Writing zero-length unknown chunk");
/* Writes all the PNG information. This is the suggested way to use the *library.Ifyouhaveanewchunktoadd,makeafunctiontowriteit, *andputitinthecorrectlocationhere.Ifyouwantthechunkwritten *aftertheimagedata,putitinpng_write_end().Istronglyencourage *youtosupplyaPNG_INFO_<chunk>flag,andcheckinfo_ptr->validbefore *writingthechunk,asthatwillkeepthecodefrombreakingifyouwant *tojustwriteaplainPNGfile.Ifyouhavelongcomments,Isuggest *writingtheminpng_write_end(),andcompressingthem.
*/ void PNGAPI
png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr)
{
png_debug(1, "in png_write_info_before_PLTE");
#ifdef PNG_MNG_FEATURES_SUPPORTED if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0 && \
png_ptr->mng_features_permitted != 0)
{
png_warning(png_ptr, "MNG features are not allowed in a PNG datastream");
png_ptr->mng_features_permitted = 0;
} #endif
/* The rest of these check to see if the valid field has the appropriate *flagset,andifitdoes,writesthechunk. * *1.6.0:COLORSPACEsupportcontrolsthewritingofthesechunkstoo,and *thechunkswillbewritteniftheWRITEroutineisthereand *information*isavailableintheCOLORSPACE.(See *png_colorspace_sync_infoinpng.cforwherethevalidflagsgetset.) * *Undercertaincircumstancesthecolorspacecanbeinvalidatedwithout *syncingtheinfo_struct'valid'flags;thishappensiflibpngdetects *anerrorandcallspng_errorwhilethecolorspaceisbeingset,yet *theapplicationcontinueswritingthePNG.Socheckthe'invalid' *flagheretoo.
*/ #ifdef PNG_WRITE_APNG_SUPPORTED if ((info_ptr->valid & PNG_INFO_acTL) != 0)
png_write_acTL(png_ptr, info_ptr->num_frames, info_ptr->num_plays); #endif #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED /* Write unknown chunks first; PNG v3 establishes a precedence order *forcolourspacechunks.Itiscertainthereforethatnew *colourspacechunkswillhaveaprecedenceandverylikelyitwillbe *higherthanallknownsofar.Writingtheunknownchunkshereis *mostlikelytopresentthechunksinthemostconvenientorder. * *FUTURE:maybewritechunksintheordertheappcallspng_set_chnk *togivetheappcontrol.
*/
write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_IHDR); #endif
#ifdef PNG_WRITE_sBIT_SUPPORTED /* PNG v3: a streaming app will need to see this before cICP because *theinformationishelpfulinhandlingHLGencoding(whichis *natively10bitsbutgetsexpandedto16inPNG.) * *Theappshouldn'tcareabouttheorderideally,butitmighthave *nochoice.InPNGv3,appsareallowedtorejectPNGswherethe *APNGchunksareoutofordersoitbehooveslibpngtobenicehere.
*/ if ((info_ptr->valid & PNG_INFO_sBIT) != 0)
png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type); #endif
/* PNG v3: the July 2004 version of the TR introduced the concept of colour *spacepriority.Asaboveitthereforebehooveslibpngtowritethecolour *spacechunksinthepriorityordersothatastreamingappneednotbuffer *them. * *PNGv3:ChunksmDCVandcLLIprovideancillaryinformationforthe *interpretationofthecolourspacechunksbutdonotrequiresupportfor *thosechunkssoareoutsidethe"COLORSPACE"checkbutbeforethewriteof *thecolourspacechunksthemselves.
*/ #ifdef PNG_WRITE_cLLI_SUPPORTED if ((info_ptr->valid & PNG_INFO_cLLI) != 0)
{
png_write_cLLI_fixed(png_ptr, info_ptr->maxCLL, info_ptr->maxFALL);
} #endif #ifdef PNG_WRITE_mDCV_SUPPORTED if ((info_ptr->valid & PNG_INFO_mDCV) != 0)
{
png_write_mDCV_fixed(png_ptr,
info_ptr->mastering_red_x, info_ptr->mastering_red_y,
info_ptr->mastering_green_x, info_ptr->mastering_green_y,
info_ptr->mastering_blue_x, info_ptr->mastering_blue_y,
info_ptr->mastering_white_x, info_ptr->mastering_white_y,
info_ptr->mastering_maxDL, info_ptr->mastering_minDL);
} #endif
#ifdef PNG_WRITE_tIME_SUPPORTED if ((info_ptr->valid & PNG_INFO_tIME) != 0)
{
png_write_tIME(png_ptr, &(info_ptr->mod_time));
png_ptr->mode |= PNG_WROTE_tIME;
} #endif/* tIME */
#ifdef PNG_WRITE_sPLT_SUPPORTED if ((info_ptr->valid & PNG_INFO_sPLT) != 0) for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)
png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); #endif/* sPLT */
#ifdef PNG_WRITE_TEXT_SUPPORTED /* Check to see if we need to write text chunks */ for (i = 0; i < info_ptr->num_text; i++)
{
png_debug2(2, "Writing header text chunk %d, type %d", i,
info_ptr->text[i].compression); /* An internationalized chunk? */ if (info_ptr->text[i].compression > 0)
{ #ifdef PNG_WRITE_iTXt_SUPPORTED /* Write international chunk */
png_write_iTXt(png_ptr,
info_ptr->text[i].compression,
info_ptr->text[i].key,
info_ptr->text[i].lang,
info_ptr->text[i].lang_key,
info_ptr->text[i].text); /* Mark this chunk as written */ if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; else
info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; #else
png_warning(png_ptr, "Unable to write international text"); #endif
}
/* If we want a compressed text chunk */ elseif (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt)
{ #ifdef PNG_WRITE_zTXt_SUPPORTED /* Write compressed chunk */
png_write_zTXt(png_ptr, info_ptr->text[i].key,
info_ptr->text[i].text, info_ptr->text[i].compression); /* Mark this chunk as written */
info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; #else
png_warning(png_ptr, "Unable to write compressed text"); #endif
}
elseif (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
{ #ifdef PNG_WRITE_tEXt_SUPPORTED /* Write uncompressed chunk */
png_write_tEXt(png_ptr, info_ptr->text[i].key,
info_ptr->text[i].text, 0); /* Mark this chunk as written */
info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; #else /* Can't get here */
png_warning(png_ptr, "Unable to write uncompressed text"); #endif
}
} #endif/* tEXt */
/* Writes the end of the PNG file. If you don't want to write comments or *timeinformation,youcanpassNULLforinfo.Ifyoualreadywrotethese *inpng_write_info(),donotwritethemagainhere.Ifyouhavelong *comments,Isuggestwritingthemhere,andcompressingthem.
*/ void PNGAPI
png_write_end(png_structrp png_ptr, png_inforp info_ptr)
{
png_debug(1, "in png_write_end");
if (png_ptr == NULL) return;
if ((png_ptr->mode & PNG_HAVE_IDAT) == 0)
png_error(png_ptr, "No IDATs written into file");
#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
png_ptr->num_palette_max >= png_ptr->num_palette)
png_benign_error(png_ptr, "Wrote palette index exceeding num_palette"); #endif
/* See if user wants us to write information chunks */ if (info_ptr != NULL)
{ #ifdef PNG_WRITE_TEXT_SUPPORTED int i; /* local index variable */ #endif #ifdef PNG_WRITE_tIME_SUPPORTED /* Check to see if user has supplied a time chunk */ if ((info_ptr->valid & PNG_INFO_tIME) != 0 &&
(png_ptr->mode & PNG_WROTE_tIME) == 0)
png_write_tIME(png_ptr, &(info_ptr->mod_time));
#endif #ifdef PNG_WRITE_TEXT_SUPPORTED /* Loop through comment chunks */ for (i = 0; i < info_ptr->num_text; i++)
{
png_debug2(2, "Writing trailer text chunk %d, type %d", i,
info_ptr->text[i].compression); /* An internationalized chunk? */ if (info_ptr->text[i].compression > 0)
{ #ifdef PNG_WRITE_iTXt_SUPPORTED /* Write international chunk */
png_write_iTXt(png_ptr,
info_ptr->text[i].compression,
info_ptr->text[i].key,
info_ptr->text[i].lang,
info_ptr->text[i].lang_key,
info_ptr->text[i].text); /* Mark this chunk as written */ if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; else
info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; #else
png_warning(png_ptr, "Unable to write international text"); #endif
}
elseif (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
{ #ifdef PNG_WRITE_zTXt_SUPPORTED /* Write compressed chunk */
png_write_zTXt(png_ptr, info_ptr->text[i].key,
info_ptr->text[i].text, info_ptr->text[i].compression); /* Mark this chunk as written */
info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; #else
png_warning(png_ptr, "Unable to write compressed text"); #endif
}
elseif (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
{ #ifdef PNG_WRITE_tEXt_SUPPORTED /* Write uncompressed chunk */
png_write_tEXt(png_ptr, info_ptr->text[i].key,
info_ptr->text[i].text, 0); /* Mark this chunk as written */
info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; #else
png_warning(png_ptr, "Unable to write uncompressed text"); #endif
}
} #endif
tbuf = gmtime(&ttime); if (tbuf == NULL)
{ /* TODO: add a safe function which takes a png_ptr argument and raises *apng_errorifthettimeargumentisinvalidandthecalltogmtime *failsasaconsequence.
*/
memset(ptime, 0, sizeof(*ptime)); return;
}
/* This is a highly dubious configuration option; by default it is off, *butitmaybeappropriateforprivatebuildsthataretesting *extensionsnotconformanttothecurrentspecification,orof *applicationsthatmustnotfailtowriteatallcosts!
*/ #ifdef PNG_BENIGN_WRITE_ERRORS_SUPPORTED /* In stable builds only warn if an application error can be completely *handled.
*/
png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN; #endif
/* App warnings are warnings in release (or release candidate) builds but *areerrorsduringdevelopment.
*/ #if PNG_RELEASE_BUILD
png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN; #endif
/* TODO: delay this, it can be done in png_init_io() (if the app doesn't *doititself)avoidingsettingthedefaultfunctionifitisnot *required.
*/
png_set_write_fn(png_ptr, NULL, NULL, NULL);
}
return png_ptr;
}
/* Write a few rows of image data. If the image is interlaced, *eitheryouwillhavetowritethe7subimages,or,ifyou *havecalledpng_set_interlace_handling(),youwillhaveto *"write"theimageseventimes.
*/ void PNGAPI
png_write_rows(png_structrp png_ptr, png_bytepp row,
png_uint_32 num_rows)
{
png_uint_32 i; /* row counter */
png_bytepp rp; /* row pointer */
png_debug(1, "in png_write_rows");
if (png_ptr == NULL) return;
/* Loop through the rows */ for (i = 0, rp = row; i < num_rows; i++, rp++)
{
png_write_row(png_ptr, *rp);
}
}
/* Write the image. You only need to call this function once, even *ifyouarewritinganinterlacedimage.
*/ void PNGAPI
png_write_image(png_structrp png_ptr, png_bytepp image)
{
png_uint_32 i; /* row index */ int pass, num_pass; /* pass variables */
png_bytepp rp; /* points to current row */
if (png_ptr == NULL) return;
png_debug(1, "in png_write_image");
#ifdef PNG_WRITE_INTERLACING_SUPPORTED /* Initialize interlace handling. If image is not interlaced, *thiswillsetpassto1
*/
num_pass = png_set_interlace_handling(png_ptr); #else
num_pass = 1; #endif /* Loop through passes */ for (pass = 0; pass < num_pass; pass++)
{ /* Loop through image */ for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
{
png_write_row(png_ptr, *rp);
}
}
}
/* Called by user to write a row of image data */ void PNGAPI
png_write_row(png_structrp png_ptr, png_const_bytep row)
{ /* 1.5.6: moved from png_struct to be a local structure: */
png_row_info row_info;
png_debug2(1, "in png_write_row (row %u, pass %d)",
png_ptr->row_number, png_ptr->pass);
if (png_ptr == NULL) return;
/* Initialize transformations and other stuff if first time */ if (png_ptr->row_number == 0 && png_ptr->pass == 0)
{ /* Make sure we wrote the header info */ if ((png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE) == 0)
png_error(png_ptr, "png_write_info was never called before png_write_row");
/* Check for transforms that have been set but were defined out */ #if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED) if ((png_ptr->transformations & PNG_INVERT_MONO) != 0)
png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined"); #endif
#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED) if ((png_ptr->transformations & PNG_FILLER) != 0)
png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined"); #endif #if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ defined(PNG_READ_PACKSWAP_SUPPORTED) if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
png_warning(png_ptr, "PNG_WRITE_PACKSWAP_SUPPORTED is not defined"); #endif
#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED) if ((png_ptr->transformations & PNG_PACK) != 0)
png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined"); #endif
#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED) if ((png_ptr->transformations & PNG_SHIFT) != 0)
png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined"); #endif
#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED) if ((png_ptr->transformations & PNG_BGR) != 0)
png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined"); #endif
#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED) if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0)
png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined"); #endif
png_write_start_row(png_ptr);
}
#ifdef PNG_WRITE_INTERLACING_SUPPORTED /* If interlaced and not interested in row, return */ if (png_ptr->interlaced != 0 &&
(png_ptr->transformations & PNG_INTERLACE) != 0)
{ switch (png_ptr->pass)
{ case0: if ((png_ptr->row_number & 0x07) != 0)
{
png_write_finish_row(png_ptr); return;
} break;
/* Copy user's row into buffer, leaving room for filter byte. */
memcpy(png_ptr->row_buf + 1, row, row_info.rowbytes);
#ifdef PNG_WRITE_INTERLACING_SUPPORTED /* Handle interlacing */ if (png_ptr->interlaced && png_ptr->pass < 6 &&
(png_ptr->transformations & PNG_INTERLACE) != 0)
{
png_do_write_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass); /* This should always get caught above, but still ... */ if (row_info.width == 0)
{
png_write_finish_row(png_ptr); return;
}
} #endif
#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED /* Handle other transformations */ if (png_ptr->transformations != 0)
png_do_write_transformations(png_ptr, &row_info); #endif
/* At this point the row_info pixel depth must match the 'transformed' depth, *whichisalsotheoutputdepth.
*/ if (row_info.pixel_depth != png_ptr->pixel_depth ||
row_info.pixel_depth != png_ptr->transformed_pixel_depth)
png_error(png_ptr, "internal write transform logic error");
/* Added at libpng-1.5.10 */ #ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED /* Check for out-of-range palette index */ if (row_info.color_type == PNG_COLOR_TYPE_PALETTE &&
png_ptr->num_palette_max >= 0)
png_do_check_palette_indexes(png_ptr, &row_info); #endif
/* Find a filter if necessary, filter the row and write it out. */
png_write_find_filter(png_ptr, &row_info);
if (png_ptr->write_row_fn != NULL)
(*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
}
#ifdef PNG_WRITE_FLUSH_SUPPORTED /* Set the automatic flush interval or 0 to turn flushing off */ void PNGAPI
png_set_flush(png_structrp png_ptr, int nrows)
{
png_debug(1, "in png_set_flush");
/* Free any memory used in png_ptr struct without freeing the struct itself. */ staticvoid
png_write_destroy(png_structrp png_ptr)
{
png_debug(1, "in png_write_destroy");
/* Free any memory zlib uses */ if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0)
deflateEnd(&png_ptr->zstream);
#ifdefined(PNG_tRNS_SUPPORTED) /* Free the independent copy of trans_alpha owned by png_struct. */
png_free(png_ptr, png_ptr->trans_alpha);
png_ptr->trans_alpha = NULL; #endif
/* Free the independent copy of the palette owned by png_struct. */
png_free(png_ptr, png_ptr->palette);
png_ptr->palette = NULL;
/* The error handling and memory handling information is left intact at this *point:thejmp_bufmaystillhavetobefreed.Seepng_destroy_png_struct *forhowthishappens.
*/
}
/* Free all memory used by the write. *Inlibpng1.6.0thisAPIchangedquietlytonolongeracceptaNULLvaluefor **png_ptr_ptr.Priorto1.6.0itwouldacceptsuchavalueanditwouldfree *thepassedininfo_structsbutitwouldquietlyfailtofreeanyofthedata *insidethem.In1.6.0itquietlydoesnothing(ithastobequietbecauseit *hasnopng_ptr.)
*/ void PNGAPI
png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)
{
png_debug(1, "in png_destroy_write_struct");
if (png_ptr_ptr != NULL)
{
png_structrp png_ptr = *png_ptr_ptr;
if (png_ptr != NULL) /* added in libpng 1.6.0 */
{
png_destroy_info_struct(png_ptr, info_ptr_ptr);
/* Allow the application to select one or more row filters to use. */ void PNGAPI
png_set_filter(png_structrp png_ptr, int method, int filters)
{
png_debug(1, "in png_set_filter");
#ifdef PNG_WRITE_FILTER_SUPPORTED /* If we have allocated the row_buf, this means we have already started *withtheimageandweshouldhaveallocatedallofthefilterbuffers *thathavebeenselected.Ifprev_rowisn'talreadyallocated,then *itistoolatetostartusingthefiltersthatneedit,sincewe *willbemissingthedatainthepreviousrow.Ifanapplication *wantstostartandstopusingparticularfiltersduringcompression, *itshouldstartoutwithallofthefilters,andthenremovethem *oraddthembackafterthestartofcompression. * *NOTE:thisisanastyconstraintonthecode,becauseitmeansthatthe *prev_rowbuffermustbemaintainedeveniftherearecurrentlyno *'prev_row'requiringfiltersactive.
*/ if (png_ptr->row_buf != NULL)
{ int num_filters;
png_alloc_size_t buf_size;
/* Repeat the checks in png_write_start_row; 1 pixel high or wide *imagescannotbenefitfromcertainfilters.Ifthisisn'tdonehere *thecheckbelowwillfireon1pixelhighimages.
*/ if (png_ptr->height == 1)
filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);
if (png_ptr->width == 1)
filters &= ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH);
if ((filters & (PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH)) != 0
&& png_ptr->prev_row == NULL)
{ /* This is the error case, however it is benign - the previous row *isnotavailablesothefiltercan'tbeused.Justwarnhere.
*/
png_app_warning(png_ptr, "png_set_filter: UP/AVG/PAETH cannot be added after start");
filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);
}
num_filters = 0;
if (filters & PNG_FILTER_SUB)
num_filters++;
if (filters & PNG_FILTER_UP)
num_filters++;
if (filters & PNG_FILTER_AVG)
num_filters++;
if (filters & PNG_FILTER_PAETH)
num_filters++;
/* Allocate needed row buffers if they have not already been *allocated.
*/
buf_size = PNG_ROWBYTES(png_ptr->usr_channels * png_ptr->usr_bit_depth,
png_ptr->width) + 1;
if (png_ptr->try_row == NULL)
png_ptr->try_row = png_voidcast(png_bytep,
png_malloc(png_ptr, buf_size));
#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED void PNGAPI
png_set_compression_level(png_structrp png_ptr, int level)
{
png_debug(1, "in png_set_compression_level");
if (png_ptr == NULL) return;
png_ptr->zlib_level = level;
}
void PNGAPI
png_set_compression_mem_level(png_structrp png_ptr, int mem_level)
{
png_debug(1, "in png_set_compression_mem_level");
if (png_ptr == NULL) return;
png_ptr->zlib_mem_level = mem_level;
}
void PNGAPI
png_set_compression_strategy(png_structrp png_ptr, int strategy)
{
png_debug(1, "in png_set_compression_strategy");
if (png_ptr == NULL) return;
/* The flag setting here prevents the libpng dynamic selection of strategy.
*/
png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
png_ptr->zlib_strategy = strategy;
}
/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a *smallervalueofwindow_bitsifitcandososafely.
*/ void PNGAPI
png_set_compression_window_bits(png_structrp png_ptr, int window_bits)
{
png_debug(1, "in png_set_compression_window_bits");
if (png_ptr == NULL) return;
/* Prior to 1.6.0 this would warn but then set the window_bits value. This *meantthatnegativewindowbitsvaluescouldbeselectedthatwouldcause *libpngtowriteanon-standardPNGfilewithrawdeflateorgzip *compressedIDATorancillarychunks.Suchfilescanbereadandthereis *nowarningonread,sothisseemslikeaverybadidea.
*/ if (window_bits > 15)
{
png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
window_bits = 15;
}
elseif (window_bits < 8)
{
png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
window_bits = 8;
}
png_ptr->zlib_window_bits = window_bits;
}
void PNGAPI
png_set_compression_method(png_structrp png_ptr, int method)
{
png_debug(1, "in png_set_compression_method");
if (png_ptr == NULL) return;
/* This would produce an invalid PNG file if it worked, but it doesn't and *deflatewillfaultit,soitisharmlesstojustwarnhere.
*/ if (method != 8)
png_warning(png_ptr, "Only compression method 8 is supported by PNG");
/* The following were added to libpng-1.5.4 */ #ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED void PNGAPI
png_set_text_compression_level(png_structrp png_ptr, int level)
{
png_debug(1, "in png_set_text_compression_level");
if (png_ptr == NULL) return;
png_ptr->zlib_text_level = level;
}
void PNGAPI
png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level)
{
png_debug(1, "in png_set_text_compression_mem_level");
if (png_ptr == NULL) return;
png_ptr->zlib_text_mem_level = mem_level;
}
void PNGAPI
png_set_text_compression_strategy(png_structrp png_ptr, int strategy)
{
png_debug(1, "in png_set_text_compression_strategy");
if (png_ptr == NULL) return;
png_ptr->zlib_text_strategy = strategy;
}
/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a *smallervalueofwindow_bitsifitcandososafely.
*/ void PNGAPI
png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits)
{
png_debug(1, "in png_set_text_compression_window_bits");
if (png_ptr == NULL) return;
if (window_bits > 15)
{
png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
window_bits = 15;
}
elseif (window_bits < 8)
{
png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
window_bits = 8;
}
png_ptr->zlib_text_window_bits = window_bits;
}
void PNGAPI
png_set_text_compression_method(png_structrp png_ptr, int method)
{
png_debug(1, "in png_set_text_compression_method");
if (png_ptr == NULL) return;
if (method != 8)
png_warning(png_ptr, "Only compression method 8 is supported by PNG");
png_ptr->zlib_text_method = method;
} #endif/* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ /* end of API added to libpng-1.5.4 */
void PNGAPI
png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn)
{
png_debug(1, "in png_set_write_status_fn");
#ifdef PNG_INFO_IMAGE_SUPPORTED void PNGAPI
png_write_png(png_structrp png_ptr, png_inforp info_ptr, int transforms, png_voidp params)
{
png_debug(1, "in png_write_png");
if (png_ptr == NULL || info_ptr == NULL) return;
if ((info_ptr->valid & PNG_INFO_IDAT) == 0)
{
png_app_error(png_ptr, "no rows for png_write_image to write"); return;
}
/* Write the file header information. */
png_write_info(png_ptr, info_ptr);
/* ------ these transformations don't touch the info structure ------- */
/* Invert monochrome pixels */ if ((transforms & PNG_TRANSFORM_INVERT_MONO) != 0) #ifdef PNG_WRITE_INVERT_SUPPORTED
png_set_invert_mono(png_ptr); #else
png_app_error(png_ptr, "PNG_TRANSFORM_INVERT_MONO not supported"); #endif
/* Shift the pixels up to a legal bit depth and fill in *asappropriatetocorrectlyscaletheimage.
*/ if ((transforms & PNG_TRANSFORM_SHIFT) != 0) #ifdef PNG_WRITE_SHIFT_SUPPORTED if ((info_ptr->valid & PNG_INFO_sBIT) != 0)
png_set_shift(png_ptr, &info_ptr->sig_bit); #else
png_app_error(png_ptr, "PNG_TRANSFORM_SHIFT not supported"); #endif
/* Pack pixels into bytes */ if ((transforms & PNG_TRANSFORM_PACKING) != 0) #ifdef PNG_WRITE_PACK_SUPPORTED
png_set_packing(png_ptr); #else
png_app_error(png_ptr, "PNG_TRANSFORM_PACKING not supported"); #endif
/* Swap location of alpha bytes from ARGB to RGBA */ if ((transforms & PNG_TRANSFORM_SWAP_ALPHA) != 0) #ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
png_set_swap_alpha(png_ptr); #else
png_app_error(png_ptr, "PNG_TRANSFORM_SWAP_ALPHA not supported"); #endif
/* Remove a filler (X) from XRGB/RGBX/AG/GA into to convert it into *RGB,notethatthecodeexpectstheinputcolortypetobeGorRGB;no *alphachannel.
*/ if ((transforms & (PNG_TRANSFORM_STRIP_FILLER_AFTER|
PNG_TRANSFORM_STRIP_FILLER_BEFORE)) != 0)
{ #ifdef PNG_WRITE_FILLER_SUPPORTED if ((transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER) != 0)
{ if ((transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) != 0)
png_app_error(png_ptr, "PNG_TRANSFORM_STRIP_FILLER: BEFORE+AFTER not supported");
/* Continue if ignored - this is the pre-1.6.10 behavior */
png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
}
/* Flip BGR pixels to RGB */ if ((transforms & PNG_TRANSFORM_BGR) != 0) #ifdef PNG_WRITE_BGR_SUPPORTED
png_set_bgr(png_ptr); #else
png_app_error(png_ptr, "PNG_TRANSFORM_BGR not supported"); #endif
/* Swap bytes of 16-bit files to most significant byte first */ if ((transforms & PNG_TRANSFORM_SWAP_ENDIAN) != 0) #ifdef PNG_WRITE_SWAP_SUPPORTED
png_set_swap(png_ptr); #else
png_app_error(png_ptr, "PNG_TRANSFORM_SWAP_ENDIAN not supported"); #endif
/* Swap bits of 1-bit, 2-bit, 4-bit packed pixel formats */ if ((transforms & PNG_TRANSFORM_PACKSWAP) != 0) #ifdef PNG_WRITE_PACKSWAP_SUPPORTED
png_set_packswap(png_ptr); #else
png_app_error(png_ptr, "PNG_TRANSFORM_PACKSWAP not supported"); #endif
/* Invert the alpha channel from opacity to transparency */ if ((transforms & PNG_TRANSFORM_INVERT_ALPHA) != 0) #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
png_set_invert_alpha(png_ptr); #else
png_app_error(png_ptr, "PNG_TRANSFORM_INVERT_ALPHA not supported"); #endif
/* ----------------------- end of transformations ------------------- */
/* Write the bits */
png_write_image(png_ptr, info_ptr->row_pointers);
/* It is REQUIRED to call this to finish writing the rest of the file */
png_write_end(png_ptr, info_ptr);
/* Byte count for memory writing */
png_bytep memory;
png_alloc_size_t memory_bytes; /* not used for STDIO */
png_alloc_size_t output_bytes; /* running total */
} png_image_write_control;
/* Write png_uint_16 input to a 16-bit PNG; the png_ptr has already been set to *doanynecessarybyteswapping.Thecomponentorderisdefinedbythe *png_imageformatvalue.
*/ staticint
png_write_image_16bit(png_voidp argument)
{
png_image_write_control *display = png_voidcast(png_image_write_control*,
argument);
png_imagep image = display->image;
png_structrp png_ptr = image->opaque->png_ptr;
/* Work out the output row end and count over this, note that the increment *aboveto'row'meansthatrow_endcanactuallybebeyondtheendofthe *row;thisiscorrect.
*/
row_end = output_row + image->width * (channels+1);
for (; y > 0; --y)
{
png_const_uint_16p in_ptr = input_row;
png_uint_16p out_ptr = output_row;
while (out_ptr < row_end)
{
png_uint_16 alpha = in_ptr[aindex];
png_uint_32 reciprocal = 0; int c;
out_ptr[aindex] = alpha;
/* Calculate a reciprocal. The correct calculation is simply *component/alpha*65535<<15.(I.e.15bitsofprecision);this *allowscorrectroundingbyadding.5beforetheshift.'reciprocal' *isonlyinitializedwhenrequired.
*/ if (alpha > 0 && alpha < 65535)
reciprocal = ((0xffff<<15)+(alpha>>1))/alpha;
c = (int)channels; do/* always at least one channel */
{
png_uint_16 component = *in_ptr++;
/* The following gives 65535 for an alpha of 0, which is fine, *otherwiseif0/0isrepresentedassomeothervaluethereismore *likelytobeadiscontinuitywhichwillprobablydamage *compressionwhenmovingfromafullytransparentareatoa *nearlytransparentone.(Theassumptionhereisthatopaque *areastendnottobe0intensity.)
*/ if (component >= alpha)
component = 65535;
/* component<alpha, so component/alpha is less than one and *component*reciprocalislessthan2^31.
*/ elseif (component > 0 && alpha < 65535)
{
png_uint_32 calc = component * reciprocal;
calc += 16384; /* round to nearest */
component = (png_uint_16)(calc >> 15);
}
*out_ptr++ = component;
} while (--c > 0);
/* Skip to next component (skip the intervening alpha channel) */
++in_ptr;
++out_ptr;
}
/* Given 16-bit input (1 to 4 channels) write 8-bit output. If an alpha channel *ispresentitmustberemovedfromthecomponents,thecomponentsarethen *writteninsRGBencoding.Nocomponentsareaddedorremoved. * *Calculateanalphareciprocaltoreversepre-multiplication.Asabovethe *calculationcanbedoneto15bitsofaccuracy;however,theoutputneedsto *bescaledintherange0..255*65535,soincludethatscalinghere.
*/ # define UNP_RECIPROCAL(alpha) ((((0xffff*0xff)<<7)+((alpha)>>1))/(alpha))
static png_byte
png_unpremultiply(png_uint_32 component, png_uint_32 alpha,
png_uint_32 reciprocal/*from the above macro*/)
{ /* The following gives 1.0 for an alpha of 0, which is fine, otherwise if 0/0 *isrepresentedassomeothervaluethereismorelikelytobea *discontinuitywhichwillprobablydamagecompressionwhenmovingfroma *fullytransparentareatoanearlytransparentone.(Theassumptionhere *isthatopaqueareastendnottobe0intensity.) * *Thereisaroundingproblemhere;ifalphaislessthan128itwillendup *as0whenscaledto8bits.Toavoidintroducingspuriouscolorsintothe *outputchangeforthistoo.
*/ if (component >= alpha || alpha < 128) return255;
/* component<alpha, so component/alpha is less than one and *component*reciprocalislessthan2^31.
*/ elseif (component > 0)
{ /* The test is that alpha/257 (rounded) is less than 255, the first value *thatbecomes255is65407. *NOTE:thismustagreewiththePNG_DIV257macro(whichmust,therefore, *beexact!)[Couldalsotestreciprocal!=0]
*/ if (alpha < 65407)
{
component *= reciprocal;
component += 64; /* round to nearest */
component >>= 7;
}
else
component *= 255;
/* Convert the component to sRGB. */ return (png_byte)PNG_sRGB_FROM_LINEAR(component);
}
if ((image->format & PNG_FORMAT_FLAG_ALPHA) != 0)
{
png_bytep row_end; int aindex;
# ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED if ((image->format & PNG_FORMAT_FLAG_AFIRST) != 0)
{
aindex = -1;
++input_row; /* To point to the first component */
++output_row;
}
else # endif
aindex = (int)channels;
/* Use row_end in place of a loop counter: */
row_end = output_row + image->width * (channels+1);
for (; y > 0; --y)
{
png_const_uint_16p in_ptr = input_row;
png_bytep out_ptr = output_row;
while (out_ptr < row_end)
{
png_uint_16 alpha = in_ptr[aindex];
png_byte alphabyte = (png_byte)PNG_DIV257(alpha);
png_uint_32 reciprocal = 0; int c;
/* Scale and write the alpha channel. */
out_ptr[aindex] = alphabyte;
c = (int)channels; do/* always at least one channel */
*out_ptr++ = png_unpremultiply(*in_ptr++, alpha, reciprocal); while (--c > 0);
/* Skip to next component (skip the intervening alpha channel) */
++in_ptr;
++out_ptr;
} /* while out_ptr < row_end */
png_write_row(png_ptr, png_voidcast(png_const_bytep,
display->local_row));
input_row += display->row_step / 2;
} /* while y */
}
else
{ /* No alpha channel, so the row_end really is the end of the row and it *issufficienttoloopoverthecomponentsonebyone.
*/
png_bytep row_end = output_row + image->width * channels;
for (; y > 0; --y)
{
png_const_uint_16p in_ptr = input_row;
png_bytep out_ptr = output_row;
while (out_ptr < row_end)
{
png_uint_32 component = *in_ptr++;
/* NOTE: the caller must check for cmap != NULL and entries != 0 */
png_uint_32 format = image->format; unsignedint channels = PNG_IMAGE_SAMPLE_CHANNELS(format);
for (i=num_trans=0; i<entries; ++i)
{ /* This gets automatically converted to sRGB with reversal of the *pre-multiplicationifthecolor-maphasanalphachannel.
*/ if ((format & PNG_FORMAT_FLAG_LINEAR) != 0)
{
png_const_uint_16p entry = png_voidcast(png_const_uint_16p, cmap);
/* Calculate a reciprocal, as in the png_write_image_8bit code above *thisisdesignedtoproduceavaluescaledto255*65535when *dividedby128(i.e.asr7).
*/ if (alphabyte > 0 && alphabyte < 255)
reciprocal = (((0xffff*0xff)<<7)+(alpha>>1))/alpha;
tRNS[i] = alphabyte; if (alphabyte < 255)
num_trans = i+1;
/* The following four ints are actually booleans */ int colormap = (format & PNG_FORMAT_FLAG_COLORMAP); int linear = !colormap && (format & PNG_FORMAT_FLAG_LINEAR); /* input */ int alpha = !colormap && (format & PNG_FORMAT_FLAG_ALPHA); int write_16bit = linear && (display->convert_to_8bit == 0);
# ifdef PNG_BENIGN_ERRORS_SUPPORTED /* Make sure we error out on any bad situation */
png_set_benign_errors(png_ptr, 0/*error*/); # endif
/* Default the 'row_stride' parameter if required, also check the row stride *andtotalimagesizetoensurethattheyarewithinthesystemlimits.
*/
{ unsignedint channels = PNG_IMAGE_PIXEL_CHANNELS(image->format);
if (image->width <= 0x7fffffffU/channels) /* no overflow */
{
png_uint_32 check;
png_uint_32 png_row_stride = image->width * channels;
if (display->row_stride == 0)
display->row_stride = (png_int_32)/*SAFE*/png_row_stride;
if (display->row_stride < 0)
check = -(png_uint_32)display->row_stride;
else
check = (png_uint_32)display->row_stride;
if (check >= png_row_stride)
{ /* Now check for overflow of the image buffer calculation; this *limitsthewholeimagesizeto32bitsforAPIcompatibilitywith *thecurrent,32-bit,PNG_IMAGE_BUFFER_SIZEmacro.
*/ if (image->height > 0xffffffffU/png_row_stride)
png_error(image->opaque->png_ptr, "memory image too large");
}
else
png_error(image->opaque->png_ptr, "supplied row stride too small");
}
else
png_error(image->opaque->png_ptr, "image row stride too large");
}
/* Set the required transforms then write the rows in the correct order. */ if ((format & PNG_FORMAT_FLAG_COLORMAP) != 0)
{ if (display->colormap != NULL && image->colormap_entries > 0)
{
png_uint_32 entries = image->colormap_entries;
/* Counter-intuitively the data transformations must be called *after* *png_write_info,notbeforeasinthereadcode,butthe'set'functions *muststillbecalledbefore.Justsetthecolorspaceinformation,never *writeaninterlacedimage.
*/
if (write_16bit != 0)
{ /* The gamma here is 1.0 (linear) and the cHRM chunk matches sRGB. */
png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_LINEAR);
if ((image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB) == 0)
png_set_cHRM_fixed(png_ptr, info_ptr, /* color x y */ /* white */ 31270, 32900, /* red */ 64000, 33000, /* green */ 30000, 60000, /* blue */ 15000, 6000
);
}
/* Else writing an 8-bit file and the *colors* aren't sRGB, but the 8-bit *spacemuststillbegammaencoded.
*/ else
png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_sRGB_INVERSE);
/* Write the file header. */
png_write_info(png_ptr, info_ptr);
/* Now set up the data transformations (*after* the header is written), *removethehandledtransformationsfromthe'format'flagsforchecking. * *Firstcheckforalittleendiansystemifwriting16-bitfiles.
*/ if (write_16bit != 0)
{
png_uint_16 le = 0x0001;
if ((*(png_const_bytep) & le) != 0)
png_set_swap(png_ptr);
}
# ifdef PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED if ((format & PNG_FORMAT_FLAG_BGR) != 0)
{ if (colormap == 0 && (format & PNG_FORMAT_FLAG_COLOR) != 0)
png_set_bgr(png_ptr);
format &= ~PNG_FORMAT_FLAG_BGR;
} # endif
# ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED if ((format & PNG_FORMAT_FLAG_AFIRST) != 0)
{ if (colormap == 0 && (format & PNG_FORMAT_FLAG_ALPHA) != 0)
png_set_swap_alpha(png_ptr);
format &= ~PNG_FORMAT_FLAG_AFIRST;
} # endif
/* If there are 16 or fewer color-map entries we wrote a lower bit depth *above,buttheapplicationdataisstillbytepacked.
*/ if (colormap != 0 && image->colormap_entries <= 16)
png_set_packing(png_ptr);
/* That should have handled all (both) the transforms. */ if ((format & ~(png_uint_32)(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR |
PNG_FORMAT_FLAG_ALPHA | PNG_FORMAT_FLAG_COLORMAP)) != 0)
png_error(png_ptr, "png_write_image: unsupported transformation");
/* Apply 'fast' options if the flag is set. */ if ((image->flags & PNG_IMAGE_FLAG_FAST) != 0)
{
png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_NO_FILTERS); /* NOTE: determined by experiment using pngstest, this reflects some *balancebetweenthetimetowritetheimageonceandthetimetoread *itabout50times.Thespeed-upinpngstestwasabout10-20%ofthe *total(user)timeonaheavilyloadedsystem.
*/ # ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
png_set_compression_level(png_ptr, 3); # endif
}
/* Check for the cases that currently require a pre-transform on the row *beforeitiswritten.Thisonlyapplieswhentheinputis16-bitand *eitherthereisanalphachanneloritisconvertedto8-bit.
*/ if (linear != 0 && (alpha != 0 || display->convert_to_8bit != 0))
{
png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr,
png_get_rowbytes(png_ptr, info_ptr))); int result;
display->local_row = row; if (write_16bit != 0)
result = png_safe_execute(image, png_write_image_16bit, display); else
result = png_safe_execute(image, png_write_image_8bit, display);
display->local_row = NULL;
png_free(png_ptr, row);
/* Skip the 'write_end' on error: */ if (result == 0) return0;
}
/* Otherwise this is the case where the input is in a format currently *supportedbytherestofthelibpngwritecode;callitdirectly.
*/ else
{
png_const_bytep row = png_voidcast(png_const_bytep, display->first_row);
ptrdiff_t row_step = display->row_step;
png_uint_32 y = image->height;
for (; y > 0; --y)
{
png_write_row(png_ptr, row);
row += row_step;
}
}
/* Check for overflow; this should never happen: */ if (size <= ((png_alloc_size_t)-1) - ob)
{ /* I don't think libpng ever does this, but just in case: */ if (size > 0)
{ if (display->memory_bytes >= ob+size) /* writing */
memcpy(display->memory+ob, data, size);
/* The rest of the memory-specific init and write_main in an error protected *environment.Thiscaseneedstousecallbacksforthewriteoperations *sincelibpnghasnobuiltinsupportforwritingtomemory.
*/
png_set_write_fn(display->image->opaque->png_ptr, display/*io_ptr*/,
image_memory_write, image_memory_flush);
return png_image_write_main(display);
}
int PNGAPI
png_image_write_to_memory(png_imagep image, void *memory,
png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8bit, constvoid *buffer, png_int_32 row_stride, constvoid *colormap)
{ /* Write the image to the given buffer, or count the bytes if it is NULL */ if (image != NULL && image->version == PNG_IMAGE_VERSION)
{ if (memory_bytes != NULL && buffer != NULL)
{ /* This is to give the caller an easier error detection in the NULL *caseandguardagainstuninitializedvariableproblems:
*/ if (memory == NULL)
*memory_bytes = 0;
if (png_image_write_init(image) != 0)
{
png_image_write_control display; int result;
result = png_safe_execute(image, png_image_write_memory, &display);
png_image_free(image);
/* write_memory returns true even if we ran out of buffer. */ if (result)
{ /* On out-of-buffer this function returns '0' but still updates *memory_bytes:
*/ if (memory != NULL && display.output_bytes > *memory_bytes)
result = 0;
#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED int PNGAPI
png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit, constvoid *buffer, png_int_32 row_stride, constvoid *colormap)
{ /* Write the image to the given FILE object. */ if (image != NULL && image->version == PNG_IMAGE_VERSION)
{ if (file != NULL && buffer != NULL)
{ if (png_image_write_init(image) != 0)
{
png_image_write_control display; int result;
/* This is slightly evil, but png_init_io doesn't do anything other *thanthisandwehaven'tchangedthestandardIOfunctionsso *thissavesa'safe'function.
*/
image->opaque->png_ptr->io_ptr = file;
int PNGAPI
png_image_write_to_file(png_imagep image, constchar *file_name, int convert_to_8bit, constvoid *buffer, png_int_32 row_stride, constvoid *colormap)
{ /* Write the image to the named file. */ if (image != NULL && image->version == PNG_IMAGE_VERSION)
{ if (file_name != NULL && buffer != NULL)
{
FILE *fp = fopen(file_name, "wb");
if (fp != NULL)
{ if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer,
row_stride, colormap) != 0)
{ int error; /* from fflush/fclose */
/* Make sure the file is flushed correctly. */ if (fflush(fp) == 0 && ferror(fp) == 0)
{ if (fclose(fp) == 0) return1;
error = errno; /* from fclose */
}
else
{
error = errno; /* from fflush or ferror */
(void)fclose(fp);
}
(void)remove(file_name); /* The image has already been cleaned up; this is just used to *settheerror(becausetheoriginalwritesucceeded).
*/ return png_image_error(image, strerror(error));
}
else
{ /* Clean up: just the opened file. */
(void)fclose(fp);
(void)remove(file_name); return0;
}
}
/* there is a chance this has been set after png_write_info was called,
* so it would be set but not written. is there a way to be sure? */ if ((info_ptr->valid & PNG_INFO_acTL) == 0)
png_error(png_ptr, "png_write_frame_head(): acTL not set");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.