michael@0: michael@0: /* pngset.c - storage of image information into info struct michael@0: * michael@0: * Last changed in libpng 1.6.8 [December 19, 2013] michael@0: * Copyright (c) 1998-2013 Glenn Randers-Pehrson michael@0: * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) michael@0: * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) michael@0: * michael@0: * This code is released under the libpng license. michael@0: * For conditions of distribution and use, see the disclaimer michael@0: * and license in png.h michael@0: * michael@0: * The functions here are used during reads to store data from the file michael@0: * into the info struct, and during writes to store application data michael@0: * into the info struct for writing into the file. This abstracts the michael@0: * info struct and allows us to change the structure in the future. michael@0: */ michael@0: michael@0: #include "pngpriv.h" michael@0: michael@0: #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) michael@0: michael@0: #ifdef PNG_bKGD_SUPPORTED michael@0: void PNGAPI michael@0: png_set_bKGD(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: png_const_color_16p background) michael@0: { michael@0: png_debug1(1, "in %s storage function", "bKGD"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL || background == NULL) michael@0: return; michael@0: michael@0: info_ptr->background = *background; michael@0: info_ptr->valid |= PNG_INFO_bKGD; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef PNG_cHRM_SUPPORTED michael@0: void PNGFAPI michael@0: png_set_cHRM_fixed(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, michael@0: png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, michael@0: png_fixed_point blue_x, png_fixed_point blue_y) michael@0: { michael@0: png_xy xy; michael@0: michael@0: png_debug1(1, "in %s storage function", "cHRM fixed"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL) michael@0: return; michael@0: michael@0: xy.redx = red_x; michael@0: xy.redy = red_y; michael@0: xy.greenx = green_x; michael@0: xy.greeny = green_y; michael@0: xy.bluex = blue_x; michael@0: xy.bluey = blue_y; michael@0: xy.whitex = white_x; michael@0: xy.whitey = white_y; michael@0: michael@0: if (png_colorspace_set_chromaticities(png_ptr, &info_ptr->colorspace, &xy, michael@0: 2/* override with app values*/)) michael@0: info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; michael@0: michael@0: png_colorspace_sync_info(png_ptr, info_ptr); michael@0: } michael@0: michael@0: void PNGFAPI michael@0: png_set_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: png_fixed_point int_red_X, png_fixed_point int_red_Y, michael@0: png_fixed_point int_red_Z, png_fixed_point int_green_X, michael@0: png_fixed_point int_green_Y, png_fixed_point int_green_Z, michael@0: png_fixed_point int_blue_X, png_fixed_point int_blue_Y, michael@0: png_fixed_point int_blue_Z) michael@0: { michael@0: png_XYZ XYZ; michael@0: michael@0: png_debug1(1, "in %s storage function", "cHRM XYZ fixed"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL) michael@0: return; michael@0: michael@0: XYZ.red_X = int_red_X; michael@0: XYZ.red_Y = int_red_Y; michael@0: XYZ.red_Z = int_red_Z; michael@0: XYZ.green_X = int_green_X; michael@0: XYZ.green_Y = int_green_Y; michael@0: XYZ.green_Z = int_green_Z; michael@0: XYZ.blue_X = int_blue_X; michael@0: XYZ.blue_Y = int_blue_Y; michael@0: XYZ.blue_Z = int_blue_Z; michael@0: michael@0: if (png_colorspace_set_endpoints(png_ptr, &info_ptr->colorspace, &XYZ, 2)) michael@0: info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; michael@0: michael@0: png_colorspace_sync_info(png_ptr, info_ptr); michael@0: } michael@0: michael@0: # ifdef PNG_FLOATING_POINT_SUPPORTED michael@0: void PNGAPI michael@0: png_set_cHRM(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: double white_x, double white_y, double red_x, double red_y, michael@0: double green_x, double green_y, double blue_x, double blue_y) michael@0: { michael@0: png_set_cHRM_fixed(png_ptr, info_ptr, michael@0: png_fixed(png_ptr, white_x, "cHRM White X"), michael@0: png_fixed(png_ptr, white_y, "cHRM White Y"), michael@0: png_fixed(png_ptr, red_x, "cHRM Red X"), michael@0: png_fixed(png_ptr, red_y, "cHRM Red Y"), michael@0: png_fixed(png_ptr, green_x, "cHRM Green X"), michael@0: png_fixed(png_ptr, green_y, "cHRM Green Y"), michael@0: png_fixed(png_ptr, blue_x, "cHRM Blue X"), michael@0: png_fixed(png_ptr, blue_y, "cHRM Blue Y")); michael@0: } michael@0: michael@0: void PNGAPI michael@0: png_set_cHRM_XYZ(png_const_structrp png_ptr, png_inforp info_ptr, double red_X, michael@0: double red_Y, double red_Z, double green_X, double green_Y, double green_Z, michael@0: double blue_X, double blue_Y, double blue_Z) michael@0: { michael@0: png_set_cHRM_XYZ_fixed(png_ptr, info_ptr, michael@0: png_fixed(png_ptr, red_X, "cHRM Red X"), michael@0: png_fixed(png_ptr, red_Y, "cHRM Red Y"), michael@0: png_fixed(png_ptr, red_Z, "cHRM Red Z"), michael@0: png_fixed(png_ptr, green_X, "cHRM Red X"), michael@0: png_fixed(png_ptr, green_Y, "cHRM Red Y"), michael@0: png_fixed(png_ptr, green_Z, "cHRM Red Z"), michael@0: png_fixed(png_ptr, blue_X, "cHRM Red X"), michael@0: png_fixed(png_ptr, blue_Y, "cHRM Red Y"), michael@0: png_fixed(png_ptr, blue_Z, "cHRM Red Z")); michael@0: } michael@0: # endif /* PNG_FLOATING_POINT_SUPPORTED */ michael@0: michael@0: #endif /* PNG_cHRM_SUPPORTED */ michael@0: michael@0: #ifdef PNG_gAMA_SUPPORTED michael@0: void PNGFAPI michael@0: png_set_gAMA_fixed(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: png_fixed_point file_gamma) michael@0: { michael@0: png_debug1(1, "in %s storage function", "gAMA"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL) michael@0: return; michael@0: michael@0: png_colorspace_set_gamma(png_ptr, &info_ptr->colorspace, file_gamma); michael@0: png_colorspace_sync_info(png_ptr, info_ptr); michael@0: } michael@0: michael@0: # ifdef PNG_FLOATING_POINT_SUPPORTED michael@0: void PNGAPI michael@0: png_set_gAMA(png_const_structrp png_ptr, png_inforp info_ptr, double file_gamma) michael@0: { michael@0: png_set_gAMA_fixed(png_ptr, info_ptr, png_fixed(png_ptr, file_gamma, michael@0: "png_set_gAMA")); michael@0: } michael@0: # endif michael@0: #endif michael@0: michael@0: #ifdef PNG_hIST_SUPPORTED michael@0: void PNGAPI michael@0: png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: png_const_uint_16p hist) michael@0: { michael@0: int i; michael@0: michael@0: png_debug1(1, "in %s storage function", "hIST"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL) michael@0: return; michael@0: michael@0: if (info_ptr->num_palette == 0 || info_ptr->num_palette michael@0: > PNG_MAX_PALETTE_LENGTH) michael@0: { michael@0: png_warning(png_ptr, michael@0: "Invalid palette size, hIST allocation skipped"); michael@0: michael@0: return; michael@0: } michael@0: michael@0: png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0); michael@0: michael@0: /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in michael@0: * version 1.2.1 michael@0: */ michael@0: info_ptr->hist = png_voidcast(png_uint_16p, png_malloc_warn(png_ptr, michael@0: PNG_MAX_PALETTE_LENGTH * (sizeof (png_uint_16)))); michael@0: michael@0: if (info_ptr->hist == NULL) michael@0: { michael@0: png_warning(png_ptr, "Insufficient memory for hIST chunk data"); michael@0: return; michael@0: } michael@0: michael@0: info_ptr->free_me |= PNG_FREE_HIST; michael@0: michael@0: for (i = 0; i < info_ptr->num_palette; i++) michael@0: info_ptr->hist[i] = hist[i]; michael@0: michael@0: info_ptr->valid |= PNG_INFO_hIST; michael@0: } michael@0: #endif michael@0: michael@0: void PNGAPI michael@0: png_set_IHDR(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: png_uint_32 width, png_uint_32 height, int bit_depth, michael@0: int color_type, int interlace_type, int compression_type, michael@0: int filter_type) michael@0: { michael@0: png_debug1(1, "in %s storage function", "IHDR"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL) michael@0: return; michael@0: michael@0: info_ptr->width = width; michael@0: info_ptr->height = height; michael@0: info_ptr->bit_depth = (png_byte)bit_depth; michael@0: info_ptr->color_type = (png_byte)color_type; michael@0: info_ptr->compression_type = (png_byte)compression_type; michael@0: info_ptr->filter_type = (png_byte)filter_type; michael@0: info_ptr->interlace_type = (png_byte)interlace_type; michael@0: michael@0: png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height, michael@0: info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type, michael@0: info_ptr->compression_type, info_ptr->filter_type); michael@0: michael@0: if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) michael@0: info_ptr->channels = 1; michael@0: michael@0: else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) michael@0: info_ptr->channels = 3; michael@0: michael@0: else michael@0: info_ptr->channels = 1; michael@0: michael@0: if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) michael@0: info_ptr->channels++; michael@0: michael@0: info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); michael@0: michael@0: info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width); michael@0: michael@0: #ifdef PNG_APNG_SUPPORTED michael@0: /* for non-animated png. this may be overwritten from an acTL chunk later */ michael@0: info_ptr->num_frames = 1; michael@0: #endif michael@0: } michael@0: michael@0: #ifdef PNG_oFFs_SUPPORTED michael@0: void PNGAPI michael@0: png_set_oFFs(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: png_int_32 offset_x, png_int_32 offset_y, int unit_type) michael@0: { michael@0: png_debug1(1, "in %s storage function", "oFFs"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL) michael@0: return; michael@0: michael@0: info_ptr->x_offset = offset_x; michael@0: info_ptr->y_offset = offset_y; michael@0: info_ptr->offset_unit_type = (png_byte)unit_type; michael@0: info_ptr->valid |= PNG_INFO_oFFs; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef PNG_pCAL_SUPPORTED michael@0: void PNGAPI michael@0: png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: png_const_charp purpose, png_int_32 X0, png_int_32 X1, int type, michael@0: int nparams, png_const_charp units, png_charpp params) michael@0: { michael@0: png_size_t length; michael@0: int i; michael@0: michael@0: png_debug1(1, "in %s storage function", "pCAL"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL || purpose == NULL || units == NULL michael@0: || (nparams > 0 && params == NULL)) michael@0: return; michael@0: michael@0: length = strlen(purpose) + 1; michael@0: png_debug1(3, "allocating purpose for info (%lu bytes)", michael@0: (unsigned long)length); michael@0: michael@0: /* TODO: validate format of calibration name and unit name */ michael@0: michael@0: /* Check that the type matches the specification. */ michael@0: if (type < 0 || type > 3) michael@0: png_error(png_ptr, "Invalid pCAL equation type"); michael@0: michael@0: if (nparams < 0 || nparams > 255) michael@0: png_error(png_ptr, "Invalid pCAL parameter count"); michael@0: michael@0: /* Validate params[nparams] */ michael@0: for (i=0; ipcal_purpose = png_voidcast(png_charp, michael@0: png_malloc_warn(png_ptr, length)); michael@0: michael@0: if (info_ptr->pcal_purpose == NULL) michael@0: { michael@0: png_warning(png_ptr, "Insufficient memory for pCAL purpose"); michael@0: return; michael@0: } michael@0: michael@0: memcpy(info_ptr->pcal_purpose, purpose, length); michael@0: michael@0: png_debug(3, "storing X0, X1, type, and nparams in info"); michael@0: info_ptr->pcal_X0 = X0; michael@0: info_ptr->pcal_X1 = X1; michael@0: info_ptr->pcal_type = (png_byte)type; michael@0: info_ptr->pcal_nparams = (png_byte)nparams; michael@0: michael@0: length = strlen(units) + 1; michael@0: png_debug1(3, "allocating units for info (%lu bytes)", michael@0: (unsigned long)length); michael@0: michael@0: info_ptr->pcal_units = png_voidcast(png_charp, michael@0: png_malloc_warn(png_ptr, length)); michael@0: michael@0: if (info_ptr->pcal_units == NULL) michael@0: { michael@0: png_warning(png_ptr, "Insufficient memory for pCAL units"); michael@0: return; michael@0: } michael@0: michael@0: memcpy(info_ptr->pcal_units, units, length); michael@0: michael@0: info_ptr->pcal_params = png_voidcast(png_charpp, png_malloc_warn(png_ptr, michael@0: (png_size_t)((nparams + 1) * (sizeof (png_charp))))); michael@0: michael@0: if (info_ptr->pcal_params == NULL) michael@0: { michael@0: png_warning(png_ptr, "Insufficient memory for pCAL params"); michael@0: return; michael@0: } michael@0: michael@0: memset(info_ptr->pcal_params, 0, (nparams + 1) * (sizeof (png_charp))); michael@0: michael@0: for (i = 0; i < nparams; i++) michael@0: { michael@0: length = strlen(params[i]) + 1; michael@0: png_debug2(3, "allocating parameter %d for info (%lu bytes)", i, michael@0: (unsigned long)length); michael@0: michael@0: info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length); michael@0: michael@0: if (info_ptr->pcal_params[i] == NULL) michael@0: { michael@0: png_warning(png_ptr, "Insufficient memory for pCAL parameter"); michael@0: return; michael@0: } michael@0: michael@0: memcpy(info_ptr->pcal_params[i], params[i], length); michael@0: } michael@0: michael@0: info_ptr->valid |= PNG_INFO_pCAL; michael@0: info_ptr->free_me |= PNG_FREE_PCAL; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef PNG_sCAL_SUPPORTED michael@0: void PNGAPI michael@0: png_set_sCAL_s(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: int unit, png_const_charp swidth, png_const_charp sheight) michael@0: { michael@0: png_size_t lengthw = 0, lengthh = 0; michael@0: michael@0: png_debug1(1, "in %s storage function", "sCAL"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL) michael@0: return; michael@0: michael@0: /* Double check the unit (should never get here with an invalid michael@0: * unit unless this is an API call.) michael@0: */ michael@0: if (unit != 1 && unit != 2) michael@0: png_error(png_ptr, "Invalid sCAL unit"); michael@0: michael@0: if (swidth == NULL || (lengthw = strlen(swidth)) == 0 || michael@0: swidth[0] == 45 /* '-' */ || !png_check_fp_string(swidth, lengthw)) michael@0: png_error(png_ptr, "Invalid sCAL width"); michael@0: michael@0: if (sheight == NULL || (lengthh = strlen(sheight)) == 0 || michael@0: sheight[0] == 45 /* '-' */ || !png_check_fp_string(sheight, lengthh)) michael@0: png_error(png_ptr, "Invalid sCAL height"); michael@0: michael@0: info_ptr->scal_unit = (png_byte)unit; michael@0: michael@0: ++lengthw; michael@0: michael@0: png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthw); michael@0: michael@0: info_ptr->scal_s_width = png_voidcast(png_charp, michael@0: png_malloc_warn(png_ptr, lengthw)); michael@0: michael@0: if (info_ptr->scal_s_width == NULL) michael@0: { michael@0: png_warning(png_ptr, "Memory allocation failed while processing sCAL"); michael@0: return; michael@0: } michael@0: michael@0: memcpy(info_ptr->scal_s_width, swidth, lengthw); michael@0: michael@0: ++lengthh; michael@0: michael@0: png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthh); michael@0: michael@0: info_ptr->scal_s_height = png_voidcast(png_charp, michael@0: png_malloc_warn(png_ptr, lengthh)); michael@0: michael@0: if (info_ptr->scal_s_height == NULL) michael@0: { michael@0: png_free (png_ptr, info_ptr->scal_s_width); michael@0: info_ptr->scal_s_width = NULL; michael@0: michael@0: png_warning(png_ptr, "Memory allocation failed while processing sCAL"); michael@0: return; michael@0: } michael@0: michael@0: memcpy(info_ptr->scal_s_height, sheight, lengthh); michael@0: michael@0: info_ptr->valid |= PNG_INFO_sCAL; michael@0: info_ptr->free_me |= PNG_FREE_SCAL; michael@0: } michael@0: michael@0: # ifdef PNG_FLOATING_POINT_SUPPORTED michael@0: void PNGAPI michael@0: png_set_sCAL(png_const_structrp png_ptr, png_inforp info_ptr, int unit, michael@0: double width, double height) michael@0: { michael@0: png_debug1(1, "in %s storage function", "sCAL"); michael@0: michael@0: /* Check the arguments. */ michael@0: if (width <= 0) michael@0: png_warning(png_ptr, "Invalid sCAL width ignored"); michael@0: michael@0: else if (height <= 0) michael@0: png_warning(png_ptr, "Invalid sCAL height ignored"); michael@0: michael@0: else michael@0: { michael@0: /* Convert 'width' and 'height' to ASCII. */ michael@0: char swidth[PNG_sCAL_MAX_DIGITS+1]; michael@0: char sheight[PNG_sCAL_MAX_DIGITS+1]; michael@0: michael@0: png_ascii_from_fp(png_ptr, swidth, (sizeof swidth), width, michael@0: PNG_sCAL_PRECISION); michael@0: png_ascii_from_fp(png_ptr, sheight, (sizeof sheight), height, michael@0: PNG_sCAL_PRECISION); michael@0: michael@0: png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight); michael@0: } michael@0: } michael@0: # endif michael@0: michael@0: # ifdef PNG_FIXED_POINT_SUPPORTED michael@0: void PNGAPI michael@0: png_set_sCAL_fixed(png_const_structrp png_ptr, png_inforp info_ptr, int unit, michael@0: png_fixed_point width, png_fixed_point height) michael@0: { michael@0: png_debug1(1, "in %s storage function", "sCAL"); michael@0: michael@0: /* Check the arguments. */ michael@0: if (width <= 0) michael@0: png_warning(png_ptr, "Invalid sCAL width ignored"); michael@0: michael@0: else if (height <= 0) michael@0: png_warning(png_ptr, "Invalid sCAL height ignored"); michael@0: michael@0: else michael@0: { michael@0: /* Convert 'width' and 'height' to ASCII. */ michael@0: char swidth[PNG_sCAL_MAX_DIGITS+1]; michael@0: char sheight[PNG_sCAL_MAX_DIGITS+1]; michael@0: michael@0: png_ascii_from_fixed(png_ptr, swidth, (sizeof swidth), width); michael@0: png_ascii_from_fixed(png_ptr, sheight, (sizeof sheight), height); michael@0: michael@0: png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight); michael@0: } michael@0: } michael@0: # endif michael@0: #endif michael@0: michael@0: #ifdef PNG_pHYs_SUPPORTED michael@0: void PNGAPI michael@0: png_set_pHYs(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: png_uint_32 res_x, png_uint_32 res_y, int unit_type) michael@0: { michael@0: png_debug1(1, "in %s storage function", "pHYs"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL) michael@0: return; michael@0: michael@0: info_ptr->x_pixels_per_unit = res_x; michael@0: info_ptr->y_pixels_per_unit = res_y; michael@0: info_ptr->phys_unit_type = (png_byte)unit_type; michael@0: info_ptr->valid |= PNG_INFO_pHYs; michael@0: } michael@0: #endif michael@0: michael@0: void PNGAPI michael@0: png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr, michael@0: png_const_colorp palette, int num_palette) michael@0: { michael@0: michael@0: png_debug1(1, "in %s storage function", "PLTE"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL) michael@0: return; michael@0: michael@0: if (num_palette < 0 || num_palette > PNG_MAX_PALETTE_LENGTH) michael@0: { michael@0: if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) michael@0: png_error(png_ptr, "Invalid palette length"); michael@0: michael@0: else michael@0: { michael@0: png_warning(png_ptr, "Invalid palette length"); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: if ((num_palette > 0 && palette == NULL) || michael@0: (num_palette == 0 michael@0: # ifdef PNG_MNG_FEATURES_SUPPORTED michael@0: && (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0 michael@0: # endif michael@0: )) michael@0: { michael@0: png_error(png_ptr, "Invalid palette"); michael@0: return; michael@0: } michael@0: michael@0: /* It may not actually be necessary to set png_ptr->palette here; michael@0: * we do it for backward compatibility with the way the png_handle_tRNS michael@0: * function used to do the allocation. michael@0: * michael@0: * 1.6.0: the above statement appears to be incorrect; something has to set michael@0: * the palette inside png_struct on read. michael@0: */ michael@0: png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); michael@0: michael@0: /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead michael@0: * of num_palette entries, in case of an invalid PNG file that has michael@0: * too-large sample values. michael@0: */ michael@0: png_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr, michael@0: PNG_MAX_PALETTE_LENGTH * (sizeof (png_color)))); michael@0: michael@0: if (num_palette > 0) michael@0: memcpy(png_ptr->palette, palette, num_palette * (sizeof (png_color))); michael@0: info_ptr->palette = png_ptr->palette; michael@0: info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette; michael@0: michael@0: info_ptr->free_me |= PNG_FREE_PLTE; michael@0: michael@0: info_ptr->valid |= PNG_INFO_PLTE; michael@0: } michael@0: michael@0: #ifdef PNG_sBIT_SUPPORTED michael@0: void PNGAPI michael@0: png_set_sBIT(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: png_const_color_8p sig_bit) michael@0: { michael@0: png_debug1(1, "in %s storage function", "sBIT"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL || sig_bit == NULL) michael@0: return; michael@0: michael@0: info_ptr->sig_bit = *sig_bit; michael@0: info_ptr->valid |= PNG_INFO_sBIT; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef PNG_sRGB_SUPPORTED michael@0: void PNGAPI michael@0: png_set_sRGB(png_const_structrp png_ptr, png_inforp info_ptr, int srgb_intent) michael@0: { michael@0: png_debug1(1, "in %s storage function", "sRGB"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL) michael@0: return; michael@0: michael@0: (void)png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, srgb_intent); michael@0: png_colorspace_sync_info(png_ptr, info_ptr); michael@0: } michael@0: michael@0: void PNGAPI michael@0: png_set_sRGB_gAMA_and_cHRM(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: int srgb_intent) michael@0: { michael@0: png_debug1(1, "in %s storage function", "sRGB_gAMA_and_cHRM"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL) michael@0: return; michael@0: michael@0: if (png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, srgb_intent)) michael@0: { michael@0: /* This causes the gAMA and cHRM to be written too */ michael@0: info_ptr->colorspace.flags |= michael@0: PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM; michael@0: } michael@0: michael@0: png_colorspace_sync_info(png_ptr, info_ptr); michael@0: } michael@0: #endif /* sRGB */ michael@0: michael@0: michael@0: #ifdef PNG_iCCP_SUPPORTED michael@0: void PNGAPI michael@0: png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: png_const_charp name, int compression_type, michael@0: png_const_bytep profile, png_uint_32 proflen) michael@0: { michael@0: png_charp new_iccp_name; michael@0: png_bytep new_iccp_profile; michael@0: png_size_t length; michael@0: michael@0: png_debug1(1, "in %s storage function", "iCCP"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL) michael@0: return; michael@0: michael@0: if (compression_type != PNG_COMPRESSION_TYPE_BASE) michael@0: png_app_error(png_ptr, "Invalid iCCP compression method"); michael@0: michael@0: /* Set the colorspace first because this validates the profile; do not michael@0: * override previously set app cHRM or gAMA here (because likely as not the michael@0: * application knows better than libpng what the correct values are.) Pass michael@0: * the info_ptr color_type field to png_colorspace_set_ICC because in the michael@0: * write case it has not yet been stored in png_ptr. michael@0: */ michael@0: { michael@0: int result = png_colorspace_set_ICC(png_ptr, &info_ptr->colorspace, name, michael@0: proflen, profile, info_ptr->color_type); michael@0: michael@0: png_colorspace_sync_info(png_ptr, info_ptr); michael@0: michael@0: /* Don't do any of the copying if the profile was bad, or inconsistent. */ michael@0: if (!result) michael@0: return; michael@0: michael@0: /* But do write the gAMA and cHRM chunks from the profile. */ michael@0: info_ptr->colorspace.flags |= michael@0: PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM; michael@0: } michael@0: michael@0: length = strlen(name)+1; michael@0: new_iccp_name = png_voidcast(png_charp, png_malloc_warn(png_ptr, length)); michael@0: michael@0: if (new_iccp_name == NULL) michael@0: { michael@0: png_benign_error(png_ptr, "Insufficient memory to process iCCP chunk"); michael@0: return; michael@0: } michael@0: michael@0: memcpy(new_iccp_name, name, length); michael@0: new_iccp_profile = png_voidcast(png_bytep, michael@0: png_malloc_warn(png_ptr, proflen)); michael@0: michael@0: if (new_iccp_profile == NULL) michael@0: { michael@0: png_free(png_ptr, new_iccp_name); michael@0: png_benign_error(png_ptr, michael@0: "Insufficient memory to process iCCP profile"); michael@0: return; michael@0: } michael@0: michael@0: memcpy(new_iccp_profile, profile, proflen); michael@0: michael@0: png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0); michael@0: michael@0: info_ptr->iccp_proflen = proflen; michael@0: info_ptr->iccp_name = new_iccp_name; michael@0: info_ptr->iccp_profile = new_iccp_profile; michael@0: info_ptr->free_me |= PNG_FREE_ICCP; michael@0: info_ptr->valid |= PNG_INFO_iCCP; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef PNG_TEXT_SUPPORTED michael@0: void PNGAPI michael@0: png_set_text(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: png_const_textp text_ptr, int num_text) michael@0: { michael@0: int ret; michael@0: ret = png_set_text_2(png_ptr, info_ptr, text_ptr, num_text); michael@0: michael@0: if (ret) michael@0: png_error(png_ptr, "Insufficient memory to store text"); michael@0: } michael@0: michael@0: int /* PRIVATE */ michael@0: png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: png_const_textp text_ptr, int num_text) michael@0: { michael@0: int i; michael@0: michael@0: png_debug1(1, "in %lx storage function", png_ptr == NULL ? "unexpected" : michael@0: (unsigned long)png_ptr->chunk_name); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL || num_text <= 0 || text_ptr == NULL) michael@0: return(0); michael@0: michael@0: /* Make sure we have enough space in the "text" array in info_struct michael@0: * to hold all of the incoming text_ptr objects. This compare can't overflow michael@0: * because max_text >= num_text (anyway, subtract of two positive integers michael@0: * can't overflow in any case.) michael@0: */ michael@0: if (num_text > info_ptr->max_text - info_ptr->num_text) michael@0: { michael@0: int old_num_text = info_ptr->num_text; michael@0: int max_text; michael@0: png_textp new_text = NULL; michael@0: michael@0: /* Calculate an appropriate max_text, checking for overflow. */ michael@0: max_text = old_num_text; michael@0: if (num_text <= INT_MAX - max_text) michael@0: { michael@0: max_text += num_text; michael@0: michael@0: /* Round up to a multiple of 8 */ michael@0: if (max_text < INT_MAX-8) michael@0: max_text = (max_text + 8) & ~0x7; michael@0: michael@0: else michael@0: max_text = INT_MAX; michael@0: michael@0: /* Now allocate a new array and copy the old members in, this does all michael@0: * the overflow checks. michael@0: */ michael@0: new_text = png_voidcast(png_textp,png_realloc_array(png_ptr, michael@0: info_ptr->text, old_num_text, max_text-old_num_text, michael@0: sizeof *new_text)); michael@0: } michael@0: michael@0: if (new_text == NULL) michael@0: { michael@0: png_chunk_report(png_ptr, "too many text chunks", michael@0: PNG_CHUNK_WRITE_ERROR); michael@0: return 1; michael@0: } michael@0: michael@0: png_free(png_ptr, info_ptr->text); michael@0: michael@0: info_ptr->text = new_text; michael@0: info_ptr->free_me |= PNG_FREE_TEXT; michael@0: info_ptr->max_text = max_text; michael@0: /* num_text is adjusted below as the entries are copied in */ michael@0: michael@0: png_debug1(3, "allocated %d entries for info_ptr->text", max_text); michael@0: } michael@0: michael@0: for (i = 0; i < num_text; i++) michael@0: { michael@0: size_t text_length, key_len; michael@0: size_t lang_len, lang_key_len; michael@0: png_textp textp = &(info_ptr->text[info_ptr->num_text]); michael@0: michael@0: if (text_ptr[i].key == NULL) michael@0: continue; michael@0: michael@0: if (text_ptr[i].compression < PNG_TEXT_COMPRESSION_NONE || michael@0: text_ptr[i].compression >= PNG_TEXT_COMPRESSION_LAST) michael@0: { michael@0: png_chunk_report(png_ptr, "text compression mode is out of range", michael@0: PNG_CHUNK_WRITE_ERROR); michael@0: continue; michael@0: } michael@0: michael@0: key_len = strlen(text_ptr[i].key); michael@0: michael@0: if (text_ptr[i].compression <= 0) michael@0: { michael@0: lang_len = 0; michael@0: lang_key_len = 0; michael@0: } michael@0: michael@0: else michael@0: # ifdef PNG_iTXt_SUPPORTED michael@0: { michael@0: /* Set iTXt data */ michael@0: michael@0: if (text_ptr[i].lang != NULL) michael@0: lang_len = strlen(text_ptr[i].lang); michael@0: michael@0: else michael@0: lang_len = 0; michael@0: michael@0: if (text_ptr[i].lang_key != NULL) michael@0: lang_key_len = strlen(text_ptr[i].lang_key); michael@0: michael@0: else michael@0: lang_key_len = 0; michael@0: } michael@0: # else /* PNG_iTXt_SUPPORTED */ michael@0: { michael@0: png_chunk_report(png_ptr, "iTXt chunk not supported", michael@0: PNG_CHUNK_WRITE_ERROR); michael@0: continue; michael@0: } michael@0: # endif michael@0: michael@0: if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0') michael@0: { michael@0: text_length = 0; michael@0: # ifdef PNG_iTXt_SUPPORTED michael@0: if (text_ptr[i].compression > 0) michael@0: textp->compression = PNG_ITXT_COMPRESSION_NONE; michael@0: michael@0: else michael@0: # endif michael@0: textp->compression = PNG_TEXT_COMPRESSION_NONE; michael@0: } michael@0: michael@0: else michael@0: { michael@0: text_length = strlen(text_ptr[i].text); michael@0: textp->compression = text_ptr[i].compression; michael@0: } michael@0: michael@0: textp->key = png_voidcast(png_charp,png_malloc_base(png_ptr, michael@0: key_len + text_length + lang_len + lang_key_len + 4)); michael@0: michael@0: if (textp->key == NULL) michael@0: { michael@0: png_chunk_report(png_ptr, "text chunk: out of memory", michael@0: PNG_CHUNK_WRITE_ERROR); michael@0: return 1; michael@0: } michael@0: michael@0: png_debug2(2, "Allocated %lu bytes at %p in png_set_text", michael@0: (unsigned long)(png_uint_32) michael@0: (key_len + lang_len + lang_key_len + text_length + 4), michael@0: textp->key); michael@0: michael@0: memcpy(textp->key, text_ptr[i].key, key_len); michael@0: *(textp->key + key_len) = '\0'; michael@0: michael@0: if (text_ptr[i].compression > 0) michael@0: { michael@0: textp->lang = textp->key + key_len + 1; michael@0: memcpy(textp->lang, text_ptr[i].lang, lang_len); michael@0: *(textp->lang + lang_len) = '\0'; michael@0: textp->lang_key = textp->lang + lang_len + 1; michael@0: memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len); michael@0: *(textp->lang_key + lang_key_len) = '\0'; michael@0: textp->text = textp->lang_key + lang_key_len + 1; michael@0: } michael@0: michael@0: else michael@0: { michael@0: textp->lang=NULL; michael@0: textp->lang_key=NULL; michael@0: textp->text = textp->key + key_len + 1; michael@0: } michael@0: michael@0: if (text_length) michael@0: memcpy(textp->text, text_ptr[i].text, text_length); michael@0: michael@0: *(textp->text + text_length) = '\0'; michael@0: michael@0: # ifdef PNG_iTXt_SUPPORTED michael@0: if (textp->compression > 0) michael@0: { michael@0: textp->text_length = 0; michael@0: textp->itxt_length = text_length; michael@0: } michael@0: michael@0: else michael@0: # endif michael@0: { michael@0: textp->text_length = text_length; michael@0: textp->itxt_length = 0; michael@0: } michael@0: michael@0: info_ptr->num_text++; michael@0: png_debug1(3, "transferred text chunk %d", info_ptr->num_text); michael@0: } michael@0: michael@0: return(0); michael@0: } michael@0: #endif michael@0: michael@0: #ifdef PNG_tIME_SUPPORTED michael@0: void PNGAPI michael@0: png_set_tIME(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: png_const_timep mod_time) michael@0: { michael@0: png_debug1(1, "in %s storage function", "tIME"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL || mod_time == NULL || michael@0: (png_ptr->mode & PNG_WROTE_tIME)) michael@0: return; michael@0: michael@0: if (mod_time->month == 0 || mod_time->month > 12 || michael@0: mod_time->day == 0 || mod_time->day > 31 || michael@0: mod_time->hour > 23 || mod_time->minute > 59 || michael@0: mod_time->second > 60) michael@0: { michael@0: png_warning(png_ptr, "Ignoring invalid time value"); michael@0: return; michael@0: } michael@0: michael@0: info_ptr->mod_time = *mod_time; michael@0: info_ptr->valid |= PNG_INFO_tIME; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef PNG_tRNS_SUPPORTED michael@0: void PNGAPI michael@0: png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, michael@0: png_const_bytep trans_alpha, int num_trans, png_const_color_16p trans_color) michael@0: { michael@0: png_debug1(1, "in %s storage function", "tRNS"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL) michael@0: return; michael@0: michael@0: if (trans_alpha != NULL) michael@0: { michael@0: /* It may not actually be necessary to set png_ptr->trans_alpha here; michael@0: * we do it for backward compatibility with the way the png_handle_tRNS michael@0: * function used to do the allocation. michael@0: * michael@0: * 1.6.0: The above statement is incorrect; png_handle_tRNS effectively michael@0: * relies on png_set_tRNS storing the information in png_struct michael@0: * (otherwise it won't be there for the code in pngrtran.c). michael@0: */ michael@0: michael@0: png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); michael@0: michael@0: /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */ michael@0: png_ptr->trans_alpha = info_ptr->trans_alpha = png_voidcast(png_bytep, michael@0: png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH)); michael@0: michael@0: if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH) michael@0: memcpy(info_ptr->trans_alpha, trans_alpha, (png_size_t)num_trans); michael@0: } michael@0: michael@0: if (trans_color != NULL) michael@0: { michael@0: int sample_max = (1 << info_ptr->bit_depth); michael@0: michael@0: if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY && michael@0: trans_color->gray > sample_max) || michael@0: (info_ptr->color_type == PNG_COLOR_TYPE_RGB && michael@0: (trans_color->red > sample_max || michael@0: trans_color->green > sample_max || michael@0: trans_color->blue > sample_max))) michael@0: png_warning(png_ptr, michael@0: "tRNS chunk has out-of-range samples for bit_depth"); michael@0: michael@0: info_ptr->trans_color = *trans_color; michael@0: michael@0: if (num_trans == 0) michael@0: num_trans = 1; michael@0: } michael@0: michael@0: info_ptr->num_trans = (png_uint_16)num_trans; michael@0: michael@0: if (num_trans != 0) michael@0: { michael@0: info_ptr->valid |= PNG_INFO_tRNS; michael@0: info_ptr->free_me |= PNG_FREE_TRNS; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: #ifdef PNG_sPLT_SUPPORTED michael@0: void PNGAPI michael@0: png_set_sPLT(png_const_structrp png_ptr, michael@0: png_inforp info_ptr, png_const_sPLT_tp entries, int nentries) michael@0: /* michael@0: * entries - array of png_sPLT_t structures michael@0: * to be added to the list of palettes michael@0: * in the info structure. michael@0: * michael@0: * nentries - number of palette structures to be michael@0: * added. michael@0: */ michael@0: { michael@0: png_sPLT_tp np; michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL || nentries <= 0 || entries == NULL) michael@0: return; michael@0: michael@0: /* Use the internal realloc function, which checks for all the possible michael@0: * overflows. Notice that the parameters are (int) and (size_t) michael@0: */ michael@0: np = png_voidcast(png_sPLT_tp,png_realloc_array(png_ptr, michael@0: info_ptr->splt_palettes, info_ptr->splt_palettes_num, nentries, michael@0: sizeof *np)); michael@0: michael@0: if (np == NULL) michael@0: { michael@0: /* Out of memory or too many chunks */ michael@0: png_chunk_report(png_ptr, "too many sPLT chunks", PNG_CHUNK_WRITE_ERROR); michael@0: return; michael@0: } michael@0: michael@0: png_free(png_ptr, info_ptr->splt_palettes); michael@0: info_ptr->splt_palettes = np; michael@0: info_ptr->free_me |= PNG_FREE_SPLT; michael@0: michael@0: np += info_ptr->splt_palettes_num; michael@0: michael@0: do michael@0: { michael@0: png_size_t length; michael@0: michael@0: /* Skip invalid input entries */ michael@0: if (entries->name == NULL || entries->entries == NULL) michael@0: { michael@0: /* png_handle_sPLT doesn't do this, so this is an app error */ michael@0: png_app_error(png_ptr, "png_set_sPLT: invalid sPLT"); michael@0: /* Just skip the invalid entry */ michael@0: continue; michael@0: } michael@0: michael@0: np->depth = entries->depth; michael@0: michael@0: /* In the even of out-of-memory just return - there's no point keeping on michael@0: * trying to add sPLT chunks. michael@0: */ michael@0: length = strlen(entries->name) + 1; michael@0: np->name = png_voidcast(png_charp, png_malloc_base(png_ptr, length)); michael@0: michael@0: if (np->name == NULL) michael@0: break; michael@0: michael@0: memcpy(np->name, entries->name, length); michael@0: michael@0: /* IMPORTANT: we have memory now that won't get freed if something else michael@0: * goes wrong, this code must free it. png_malloc_array produces no michael@0: * warnings, use a png_chunk_report (below) if there is an error. michael@0: */ michael@0: np->entries = png_voidcast(png_sPLT_entryp, png_malloc_array(png_ptr, michael@0: entries->nentries, sizeof (png_sPLT_entry))); michael@0: michael@0: if (np->entries == NULL) michael@0: { michael@0: png_free(png_ptr, np->name); michael@0: break; michael@0: } michael@0: michael@0: np->nentries = entries->nentries; michael@0: /* This multiply can't overflow because png_malloc_array has already michael@0: * checked it when doing the allocation. michael@0: */ michael@0: memcpy(np->entries, entries->entries, michael@0: entries->nentries * sizeof (png_sPLT_entry)); michael@0: michael@0: /* Note that 'continue' skips the advance of the out pointer and out michael@0: * count, so an invalid entry is not added. michael@0: */ michael@0: info_ptr->valid |= PNG_INFO_sPLT; michael@0: ++(info_ptr->splt_palettes_num); michael@0: ++np; michael@0: } michael@0: while (++entries, --nentries); michael@0: michael@0: if (nentries > 0) michael@0: png_chunk_report(png_ptr, "sPLT out of memory", PNG_CHUNK_WRITE_ERROR); michael@0: } michael@0: #endif /* PNG_sPLT_SUPPORTED */ michael@0: michael@0: #ifdef PNG_APNG_SUPPORTED michael@0: png_uint_32 PNGAPI michael@0: png_set_acTL(png_structp png_ptr, png_infop info_ptr, michael@0: png_uint_32 num_frames, png_uint_32 num_plays) michael@0: { michael@0: png_debug1(1, "in %s storage function", "acTL"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL) michael@0: { michael@0: png_warning(png_ptr, michael@0: "Call to png_set_acTL() with NULL png_ptr " michael@0: "or info_ptr ignored"); michael@0: return (0); michael@0: } michael@0: if (num_frames == 0) michael@0: { michael@0: png_warning(png_ptr, michael@0: "Ignoring attempt to set acTL with num_frames zero"); michael@0: return (0); michael@0: } michael@0: if (num_frames > PNG_UINT_31_MAX) michael@0: { michael@0: png_warning(png_ptr, michael@0: "Ignoring attempt to set acTL with num_frames > 2^31-1"); michael@0: return (0); michael@0: } michael@0: if (num_plays > PNG_UINT_31_MAX) michael@0: { michael@0: png_warning(png_ptr, michael@0: "Ignoring attempt to set acTL with num_plays " michael@0: "> 2^31-1"); michael@0: return (0); michael@0: } michael@0: michael@0: info_ptr->num_frames = num_frames; michael@0: info_ptr->num_plays = num_plays; michael@0: michael@0: info_ptr->valid |= PNG_INFO_acTL; michael@0: michael@0: return (1); michael@0: } michael@0: michael@0: /* delay_num and delay_den can hold any 16-bit values including zero */ michael@0: png_uint_32 PNGAPI michael@0: png_set_next_frame_fcTL(png_structp png_ptr, png_infop info_ptr, michael@0: png_uint_32 width, png_uint_32 height, michael@0: png_uint_32 x_offset, png_uint_32 y_offset, michael@0: png_uint_16 delay_num, png_uint_16 delay_den, michael@0: png_byte dispose_op, png_byte blend_op) michael@0: { michael@0: png_debug1(1, "in %s storage function", "fcTL"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL) michael@0: { michael@0: png_warning(png_ptr, michael@0: "Call to png_set_fcTL() with NULL png_ptr or info_ptr " michael@0: "ignored"); michael@0: return (0); michael@0: } michael@0: michael@0: png_ensure_fcTL_is_valid(png_ptr, width, height, x_offset, y_offset, michael@0: delay_num, delay_den, dispose_op, blend_op); michael@0: michael@0: if (blend_op == PNG_BLEND_OP_OVER) michael@0: { michael@0: if (!(png_ptr->color_type & PNG_COLOR_MASK_ALPHA) && michael@0: !(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) michael@0: { michael@0: png_warning(png_ptr, "PNG_BLEND_OP_OVER is meaningless " michael@0: "and wasteful for opaque images, ignored"); michael@0: blend_op = PNG_BLEND_OP_SOURCE; michael@0: } michael@0: } michael@0: michael@0: info_ptr->next_frame_width = width; michael@0: info_ptr->next_frame_height = height; michael@0: info_ptr->next_frame_x_offset = x_offset; michael@0: info_ptr->next_frame_y_offset = y_offset; michael@0: info_ptr->next_frame_delay_num = delay_num; michael@0: info_ptr->next_frame_delay_den = delay_den; michael@0: info_ptr->next_frame_dispose_op = dispose_op; michael@0: info_ptr->next_frame_blend_op = blend_op; michael@0: michael@0: info_ptr->valid |= PNG_INFO_fcTL; michael@0: michael@0: return (1); michael@0: } michael@0: michael@0: void /* PRIVATE */ michael@0: png_ensure_fcTL_is_valid(png_structp png_ptr, michael@0: png_uint_32 width, png_uint_32 height, michael@0: png_uint_32 x_offset, png_uint_32 y_offset, michael@0: png_uint_16 delay_num, png_uint_16 delay_den, michael@0: png_byte dispose_op, png_byte blend_op) michael@0: { michael@0: if (width > PNG_UINT_31_MAX) michael@0: png_error(png_ptr, "invalid width in fcTL (> 2^31-1)"); michael@0: if (height > PNG_UINT_31_MAX) michael@0: png_error(png_ptr, "invalid height in fcTL (> 2^31-1)"); michael@0: if (x_offset > PNG_UINT_31_MAX) michael@0: png_error(png_ptr, "invalid x_offset in fcTL (> 2^31-1)"); michael@0: if (y_offset > PNG_UINT_31_MAX) michael@0: png_error(png_ptr, "invalid y_offset in fcTL (> 2^31-1)"); michael@0: if (width + x_offset > png_ptr->first_frame_width || michael@0: height + y_offset > png_ptr->first_frame_height) michael@0: png_error(png_ptr, "dimensions of a frame are greater than" michael@0: "the ones in IHDR"); michael@0: michael@0: if (dispose_op != PNG_DISPOSE_OP_NONE && michael@0: dispose_op != PNG_DISPOSE_OP_BACKGROUND && michael@0: dispose_op != PNG_DISPOSE_OP_PREVIOUS) michael@0: png_error(png_ptr, "invalid dispose_op in fcTL"); michael@0: michael@0: if (blend_op != PNG_BLEND_OP_SOURCE && michael@0: blend_op != PNG_BLEND_OP_OVER) michael@0: png_error(png_ptr, "invalid blend_op in fcTL"); michael@0: michael@0: PNG_UNUSED(delay_num) michael@0: PNG_UNUSED(delay_den) michael@0: } michael@0: michael@0: png_uint_32 PNGAPI michael@0: png_set_first_frame_is_hidden(png_structp png_ptr, png_infop info_ptr, michael@0: png_byte is_hidden) michael@0: { michael@0: png_debug(1, "in png_first_frame_is_hidden()"); michael@0: michael@0: if (png_ptr == NULL) michael@0: return 0; michael@0: michael@0: if (is_hidden) michael@0: png_ptr->apng_flags |= PNG_FIRST_FRAME_HIDDEN; michael@0: else michael@0: png_ptr->apng_flags &= ~PNG_FIRST_FRAME_HIDDEN; michael@0: michael@0: PNG_UNUSED(info_ptr) michael@0: michael@0: return 1; michael@0: } michael@0: #endif /* PNG_APNG_SUPPORTED */ michael@0: michael@0: #ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED michael@0: static png_byte michael@0: check_location(png_const_structrp png_ptr, int location) michael@0: { michael@0: location &= (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT); michael@0: michael@0: /* New in 1.6.0; copy the location and check it. This is an API michael@0: * change, previously the app had to use the michael@0: * png_set_unknown_chunk_location API below for each chunk. michael@0: */ michael@0: if (location == 0 && !(png_ptr->mode & PNG_IS_READ_STRUCT)) michael@0: { michael@0: /* Write struct, so unknown chunks come from the app */ michael@0: png_app_warning(png_ptr, michael@0: "png_set_unknown_chunks now expects a valid location"); michael@0: /* Use the old behavior */ michael@0: location = (png_byte)(png_ptr->mode & michael@0: (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT)); michael@0: } michael@0: michael@0: /* This need not be an internal error - if the app calls michael@0: * png_set_unknown_chunks on a read pointer it must get the location right. michael@0: */ michael@0: if (location == 0) michael@0: png_error(png_ptr, "invalid location in png_set_unknown_chunks"); michael@0: michael@0: /* Now reduce the location to the top-most set bit by removing each least michael@0: * significant bit in turn. michael@0: */ michael@0: while (location != (location & -location)) michael@0: location &= ~(location & -location); michael@0: michael@0: /* The cast is safe because 'location' is a bit mask and only the low four michael@0: * bits are significant. michael@0: */ michael@0: return (png_byte)location; michael@0: } michael@0: michael@0: void PNGAPI michael@0: png_set_unknown_chunks(png_const_structrp png_ptr, michael@0: png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns) michael@0: { michael@0: png_unknown_chunkp np; michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL || num_unknowns <= 0 || michael@0: unknowns == NULL) michael@0: return; michael@0: michael@0: /* Check for the failure cases where support has been disabled at compile michael@0: * time. This code is hardly ever compiled - it's here because michael@0: * STORE_UNKNOWN_CHUNKS is set by both read and write code (compiling in this michael@0: * code) but may be meaningless if the read or write handling of unknown michael@0: * chunks is not compiled in. michael@0: */ michael@0: # if !defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) && \ michael@0: defined(PNG_READ_SUPPORTED) michael@0: if (png_ptr->mode & PNG_IS_READ_STRUCT) michael@0: { michael@0: png_app_error(png_ptr, "no unknown chunk support on read"); michael@0: return; michael@0: } michael@0: # endif michael@0: # if !defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) && \ michael@0: defined(PNG_WRITE_SUPPORTED) michael@0: if (!(png_ptr->mode & PNG_IS_READ_STRUCT)) michael@0: { michael@0: png_app_error(png_ptr, "no unknown chunk support on write"); michael@0: return; michael@0: } michael@0: # endif michael@0: michael@0: /* Prior to 1.6.0 this code used png_malloc_warn; however, this meant that michael@0: * unknown critical chunks could be lost with just a warning resulting in michael@0: * undefined behavior. Now png_chunk_report is used to provide behavior michael@0: * appropriate to read or write. michael@0: */ michael@0: np = png_voidcast(png_unknown_chunkp, png_realloc_array(png_ptr, michael@0: info_ptr->unknown_chunks, info_ptr->unknown_chunks_num, num_unknowns, michael@0: sizeof *np)); michael@0: michael@0: if (np == NULL) michael@0: { michael@0: png_chunk_report(png_ptr, "too many unknown chunks", michael@0: PNG_CHUNK_WRITE_ERROR); michael@0: return; michael@0: } michael@0: michael@0: png_free(png_ptr, info_ptr->unknown_chunks); michael@0: info_ptr->unknown_chunks = np; /* safe because it is initialized */ michael@0: info_ptr->free_me |= PNG_FREE_UNKN; michael@0: michael@0: np += info_ptr->unknown_chunks_num; michael@0: michael@0: /* Increment unknown_chunks_num each time round the loop to protect the michael@0: * just-allocated chunk data. michael@0: */ michael@0: for (; num_unknowns > 0; --num_unknowns, ++unknowns) michael@0: { michael@0: memcpy(np->name, unknowns->name, (sizeof np->name)); michael@0: np->name[(sizeof np->name)-1] = '\0'; michael@0: np->location = check_location(png_ptr, unknowns->location); michael@0: michael@0: if (unknowns->size == 0) michael@0: { michael@0: np->data = NULL; michael@0: np->size = 0; michael@0: } michael@0: michael@0: else michael@0: { michael@0: np->data = png_voidcast(png_bytep, michael@0: png_malloc_base(png_ptr, unknowns->size)); michael@0: michael@0: if (np->data == NULL) michael@0: { michael@0: png_chunk_report(png_ptr, "unknown chunk: out of memory", michael@0: PNG_CHUNK_WRITE_ERROR); michael@0: /* But just skip storing the unknown chunk */ michael@0: continue; michael@0: } michael@0: michael@0: memcpy(np->data, unknowns->data, unknowns->size); michael@0: np->size = unknowns->size; michael@0: } michael@0: michael@0: /* These increments are skipped on out-of-memory for the data - the michael@0: * unknown chunk entry gets overwritten if the png_chunk_report returns. michael@0: * This is correct in the read case (the chunk is just dropped.) michael@0: */ michael@0: ++np; michael@0: ++(info_ptr->unknown_chunks_num); michael@0: } michael@0: } michael@0: michael@0: void PNGAPI michael@0: png_set_unknown_chunk_location(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: int chunk, int location) michael@0: { michael@0: /* This API is pretty pointless in 1.6.0 because the location can be set michael@0: * before the call to png_set_unknown_chunks. michael@0: * michael@0: * TODO: add a png_app_warning in 1.7 michael@0: */ michael@0: if (png_ptr != NULL && info_ptr != NULL && chunk >= 0 && michael@0: chunk < info_ptr->unknown_chunks_num) michael@0: { michael@0: if ((location & (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT)) == 0) michael@0: { michael@0: png_app_error(png_ptr, "invalid unknown chunk location"); michael@0: /* Fake out the pre 1.6.0 behavior: */ michael@0: if ((location & PNG_HAVE_IDAT)) /* undocumented! */ michael@0: location = PNG_AFTER_IDAT; michael@0: michael@0: else michael@0: location = PNG_HAVE_IHDR; /* also undocumented */ michael@0: } michael@0: michael@0: info_ptr->unknown_chunks[chunk].location = michael@0: check_location(png_ptr, location); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: michael@0: #ifdef PNG_MNG_FEATURES_SUPPORTED michael@0: png_uint_32 PNGAPI michael@0: png_permit_mng_features (png_structrp png_ptr, png_uint_32 mng_features) michael@0: { michael@0: png_debug(1, "in png_permit_mng_features"); michael@0: michael@0: if (png_ptr == NULL) michael@0: return 0; michael@0: michael@0: png_ptr->mng_features_permitted = mng_features & PNG_ALL_MNG_FEATURES; michael@0: michael@0: return png_ptr->mng_features_permitted; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED michael@0: static unsigned int michael@0: add_one_chunk(png_bytep list, unsigned int count, png_const_bytep add, int keep) michael@0: { michael@0: unsigned int i; michael@0: michael@0: /* Utility function: update the 'keep' state of a chunk if it is already in michael@0: * the list, otherwise add it to the list. michael@0: */ michael@0: for (i=0; i= PNG_HANDLE_CHUNK_LAST) michael@0: { michael@0: png_app_error(png_ptr, "png_set_keep_unknown_chunks: invalid keep"); michael@0: return; michael@0: } michael@0: michael@0: if (num_chunks_in <= 0) michael@0: { michael@0: png_ptr->unknown_default = keep; michael@0: michael@0: /* '0' means just set the flags, so stop here */ michael@0: if (num_chunks_in == 0) michael@0: return; michael@0: } michael@0: michael@0: if (num_chunks_in < 0) michael@0: { michael@0: /* Ignore all unknown chunks and all chunks recognized by michael@0: * libpng except for IHDR, PLTE, tRNS, IDAT, and IEND michael@0: */ michael@0: static PNG_CONST png_byte chunks_to_ignore[] = { michael@0: 98, 75, 71, 68, '\0', /* bKGD */ michael@0: 99, 72, 82, 77, '\0', /* cHRM */ michael@0: 103, 65, 77, 65, '\0', /* gAMA */ michael@0: 104, 73, 83, 84, '\0', /* hIST */ michael@0: 105, 67, 67, 80, '\0', /* iCCP */ michael@0: 105, 84, 88, 116, '\0', /* iTXt */ michael@0: 111, 70, 70, 115, '\0', /* oFFs */ michael@0: 112, 67, 65, 76, '\0', /* pCAL */ michael@0: 112, 72, 89, 115, '\0', /* pHYs */ michael@0: 115, 66, 73, 84, '\0', /* sBIT */ michael@0: 115, 67, 65, 76, '\0', /* sCAL */ michael@0: 115, 80, 76, 84, '\0', /* sPLT */ michael@0: 115, 84, 69, 82, '\0', /* sTER */ michael@0: 115, 82, 71, 66, '\0', /* sRGB */ michael@0: 116, 69, 88, 116, '\0', /* tEXt */ michael@0: 116, 73, 77, 69, '\0', /* tIME */ michael@0: 122, 84, 88, 116, '\0' /* zTXt */ michael@0: }; michael@0: michael@0: chunk_list = chunks_to_ignore; michael@0: num_chunks = (sizeof chunks_to_ignore)/5; michael@0: } michael@0: michael@0: else /* num_chunks_in > 0 */ michael@0: { michael@0: if (chunk_list == NULL) michael@0: { michael@0: /* Prior to 1.6.0 this was silently ignored, now it is an app_error michael@0: * which can be switched off. michael@0: */ michael@0: png_app_error(png_ptr, "png_set_keep_unknown_chunks: no chunk list"); michael@0: return; michael@0: } michael@0: michael@0: num_chunks = num_chunks_in; michael@0: } michael@0: michael@0: old_num_chunks = png_ptr->num_chunk_list; michael@0: if (png_ptr->chunk_list == NULL) michael@0: old_num_chunks = 0; michael@0: michael@0: /* Since num_chunks is always restricted to UINT_MAX/5 this can't overflow. michael@0: */ michael@0: if (num_chunks + old_num_chunks > UINT_MAX/5) michael@0: { michael@0: png_app_error(png_ptr, "png_set_keep_unknown_chunks: too many chunks"); michael@0: return; michael@0: } michael@0: michael@0: /* If these chunks are being reset to the default then no more memory is michael@0: * required because add_one_chunk above doesn't extend the list if the 'keep' michael@0: * parameter is the default. michael@0: */ michael@0: if (keep) michael@0: { michael@0: new_list = png_voidcast(png_bytep, png_malloc(png_ptr, michael@0: 5 * (num_chunks + old_num_chunks))); michael@0: michael@0: if (old_num_chunks > 0) michael@0: memcpy(new_list, png_ptr->chunk_list, 5*old_num_chunks); michael@0: } michael@0: michael@0: else if (old_num_chunks > 0) michael@0: new_list = png_ptr->chunk_list; michael@0: michael@0: else michael@0: new_list = NULL; michael@0: michael@0: /* Add the new chunks together with each one's handling code. If the chunk michael@0: * already exists the code is updated, otherwise the chunk is added to the michael@0: * end. (In libpng 1.6.0 order no longer matters because this code enforces michael@0: * the earlier convention that the last setting is the one that is used.) michael@0: */ michael@0: if (new_list != NULL) michael@0: { michael@0: png_const_bytep inlist; michael@0: png_bytep outlist; michael@0: unsigned int i; michael@0: michael@0: for (i=0; ichunk_list != new_list) michael@0: png_free(png_ptr, new_list); michael@0: michael@0: new_list = NULL; michael@0: } michael@0: } michael@0: michael@0: else michael@0: num_chunks = 0; michael@0: michael@0: png_ptr->num_chunk_list = num_chunks; michael@0: michael@0: if (png_ptr->chunk_list != new_list) michael@0: { michael@0: if (png_ptr->chunk_list != NULL) michael@0: png_free(png_ptr, png_ptr->chunk_list); michael@0: michael@0: png_ptr->chunk_list = new_list; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: #ifdef PNG_READ_USER_CHUNKS_SUPPORTED michael@0: void PNGAPI michael@0: png_set_read_user_chunk_fn(png_structrp png_ptr, png_voidp user_chunk_ptr, michael@0: png_user_chunk_ptr read_user_chunk_fn) michael@0: { michael@0: png_debug(1, "in png_set_read_user_chunk_fn"); michael@0: michael@0: if (png_ptr == NULL) michael@0: return; michael@0: michael@0: png_ptr->read_user_chunk_fn = read_user_chunk_fn; michael@0: png_ptr->user_chunk_ptr = user_chunk_ptr; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef PNG_INFO_IMAGE_SUPPORTED michael@0: void PNGAPI michael@0: png_set_rows(png_const_structrp png_ptr, png_inforp info_ptr, michael@0: png_bytepp row_pointers) michael@0: { michael@0: png_debug1(1, "in %s storage function", "rows"); michael@0: michael@0: if (png_ptr == NULL || info_ptr == NULL) michael@0: return; michael@0: michael@0: if (info_ptr->row_pointers && (info_ptr->row_pointers != row_pointers)) michael@0: png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); michael@0: michael@0: info_ptr->row_pointers = row_pointers; michael@0: michael@0: if (row_pointers) michael@0: info_ptr->valid |= PNG_INFO_IDAT; michael@0: } michael@0: #endif michael@0: michael@0: void PNGAPI michael@0: png_set_compression_buffer_size(png_structrp png_ptr, png_size_t size) michael@0: { michael@0: if (png_ptr == NULL) michael@0: return; michael@0: michael@0: if (size == 0 || size > PNG_UINT_31_MAX) michael@0: png_error(png_ptr, "invalid compression buffer size"); michael@0: michael@0: # ifdef PNG_SEQUENTIAL_READ_SUPPORTED michael@0: if (png_ptr->mode & PNG_IS_READ_STRUCT) michael@0: { michael@0: png_ptr->IDAT_read_size = (png_uint_32)size; /* checked above */ michael@0: return; michael@0: } michael@0: # endif michael@0: michael@0: # ifdef PNG_WRITE_SUPPORTED michael@0: if (!(png_ptr->mode & PNG_IS_READ_STRUCT)) michael@0: { michael@0: if (png_ptr->zowner != 0) michael@0: { michael@0: png_warning(png_ptr, michael@0: "Compression buffer size cannot be changed because it is in use"); michael@0: return; michael@0: } michael@0: michael@0: if (size > ZLIB_IO_MAX) michael@0: { michael@0: png_warning(png_ptr, michael@0: "Compression buffer size limited to system maximum"); michael@0: size = ZLIB_IO_MAX; /* must fit */ michael@0: } michael@0: michael@0: else if (size < 6) michael@0: { michael@0: /* Deflate will potentially go into an infinite loop on a SYNC_FLUSH michael@0: * if this is permitted. michael@0: */ michael@0: png_warning(png_ptr, michael@0: "Compression buffer size cannot be reduced below 6"); michael@0: return; michael@0: } michael@0: michael@0: if (png_ptr->zbuffer_size != size) michael@0: { michael@0: png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list); michael@0: png_ptr->zbuffer_size = (uInt)size; michael@0: } michael@0: } michael@0: # endif michael@0: } michael@0: michael@0: void PNGAPI michael@0: png_set_invalid(png_const_structrp png_ptr, png_inforp info_ptr, int mask) michael@0: { michael@0: if (png_ptr && info_ptr) michael@0: info_ptr->valid &= ~mask; michael@0: } michael@0: michael@0: michael@0: #ifdef PNG_SET_USER_LIMITS_SUPPORTED michael@0: /* This function was added to libpng 1.2.6 */ michael@0: void PNGAPI michael@0: png_set_user_limits (png_structrp png_ptr, png_uint_32 user_width_max, michael@0: png_uint_32 user_height_max) michael@0: { michael@0: /* Images with dimensions larger than these limits will be michael@0: * rejected by png_set_IHDR(). To accept any PNG datastream michael@0: * regardless of dimensions, set both limits to 0x7ffffffL. michael@0: */ michael@0: if (png_ptr == NULL) michael@0: return; michael@0: michael@0: png_ptr->user_width_max = user_width_max; michael@0: png_ptr->user_height_max = user_height_max; michael@0: } michael@0: michael@0: /* This function was added to libpng 1.4.0 */ michael@0: void PNGAPI michael@0: png_set_chunk_cache_max (png_structrp png_ptr, png_uint_32 user_chunk_cache_max) michael@0: { michael@0: if (png_ptr) michael@0: png_ptr->user_chunk_cache_max = user_chunk_cache_max; michael@0: } michael@0: michael@0: /* This function was added to libpng 1.4.1 */ michael@0: void PNGAPI michael@0: png_set_chunk_malloc_max (png_structrp png_ptr, michael@0: png_alloc_size_t user_chunk_malloc_max) michael@0: { michael@0: if (png_ptr) michael@0: png_ptr->user_chunk_malloc_max = user_chunk_malloc_max; michael@0: } michael@0: #endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ michael@0: michael@0: michael@0: #ifdef PNG_BENIGN_ERRORS_SUPPORTED michael@0: void PNGAPI michael@0: png_set_benign_errors(png_structrp png_ptr, int allowed) michael@0: { michael@0: png_debug(1, "in png_set_benign_errors"); michael@0: michael@0: /* If allowed is 1, png_benign_error() is treated as a warning. michael@0: * michael@0: * If allowed is 0, png_benign_error() is treated as an error (which michael@0: * is the default behavior if png_set_benign_errors() is not called). michael@0: */ michael@0: michael@0: if (allowed) michael@0: png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN | michael@0: PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN; michael@0: michael@0: else michael@0: png_ptr->flags &= ~(PNG_FLAG_BENIGN_ERRORS_WARN | michael@0: PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN); michael@0: } michael@0: #endif /* PNG_BENIGN_ERRORS_SUPPORTED */ michael@0: michael@0: #ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED michael@0: /* Whether to report invalid palette index; added at libng-1.5.10. michael@0: * It is possible for an indexed (color-type==3) PNG file to contain michael@0: * pixels with invalid (out-of-range) indexes if the PLTE chunk has michael@0: * fewer entries than the image's bit-depth would allow. We recover michael@0: * from this gracefully by filling any incomplete palette with zeroes michael@0: * (opaque black). By default, when this occurs libpng will issue michael@0: * a benign error. This API can be used to override that behavior. michael@0: */ michael@0: void PNGAPI michael@0: png_set_check_for_invalid_index(png_structrp png_ptr, int allowed) michael@0: { michael@0: png_debug(1, "in png_set_check_for_invalid_index"); michael@0: michael@0: if (allowed > 0) michael@0: png_ptr->num_palette_max = 0; michael@0: michael@0: else michael@0: png_ptr->num_palette_max = -1; michael@0: } michael@0: #endif michael@0: #endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */