Path: blob/master/thirdparty/libwebp/src/webp/decode.h
21075 views
// Copyright 2010 Google Inc. All Rights Reserved.1//2// Use of this source code is governed by a BSD-style license3// that can be found in the COPYING file in the root of the source4// tree. An additional intellectual property rights grant can be found5// in the file PATENTS. All contributing project authors may6// be found in the AUTHORS file in the root of the source tree.7// -----------------------------------------------------------------------------8//9// Main decoding functions for WebP images.10//11// Author: Skal ([email protected])1213#ifndef WEBP_WEBP_DECODE_H_14#define WEBP_WEBP_DECODE_H_1516#include <stddef.h>1718#include "./types.h"1920#ifdef __cplusplus21extern "C" {22#endif2324#define WEBP_DECODER_ABI_VERSION 0x0210 // MAJOR(8b) + MINOR(8b)2526// Note: forward declaring enumerations is not allowed in (strict) C and C++,27// the types are left here for reference.28// typedef enum VP8StatusCode VP8StatusCode;29// typedef enum WEBP_CSP_MODE WEBP_CSP_MODE;30typedef struct WebPRGBABuffer WebPRGBABuffer;31typedef struct WebPYUVABuffer WebPYUVABuffer;32typedef struct WebPDecBuffer WebPDecBuffer;33typedef struct WebPIDecoder WebPIDecoder;34typedef struct WebPBitstreamFeatures WebPBitstreamFeatures;35typedef struct WebPDecoderOptions WebPDecoderOptions;36typedef struct WebPDecoderConfig WebPDecoderConfig;3738// Return the decoder's version number, packed in hexadecimal using 8bits for39// each of major/minor/revision. E.g: v2.5.7 is 0x020507.40WEBP_EXTERN int WebPGetDecoderVersion(void);4142// Retrieve basic header information: width, height.43// This function will also validate the header, returning true on success,44// false otherwise. '*width' and '*height' are only valid on successful return.45// Pointers 'width' and 'height' can be passed NULL if deemed irrelevant.46// Note: The following chunk sequences (before the raw VP8/VP8L data) are47// considered valid by this function:48// RIFF + VP8(L)49// RIFF + VP8X + (optional chunks) + VP8(L)50// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.51// VP8(L) <-- Not a valid WebP format: only allowed for internal purpose.52WEBP_NODISCARD WEBP_EXTERN int WebPGetInfo(53const uint8_t* data, size_t data_size, int* width, int* height);5455// Decodes WebP images pointed to by 'data' and returns RGBA samples, along56// with the dimensions in *width and *height. The ordering of samples in57// memory is R, G, B, A, R, G, B, A... in scan order (endian-independent).58// The returned pointer should be deleted calling WebPFree().59// Returns NULL in case of error.60WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGBA(61const uint8_t* data, size_t data_size, int* width, int* height);6263// Same as WebPDecodeRGBA, but returning A, R, G, B, A, R, G, B... ordered data.64WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeARGB(65const uint8_t* data, size_t data_size, int* width, int* height);6667// Same as WebPDecodeRGBA, but returning B, G, R, A, B, G, R, A... ordered data.68WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGRA(69const uint8_t* data, size_t data_size, int* width, int* height);7071// Same as WebPDecodeRGBA, but returning R, G, B, R, G, B... ordered data.72// If the bitstream contains transparency, it is ignored.73WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGB(74const uint8_t* data, size_t data_size, int* width, int* height);7576// Same as WebPDecodeRGB, but returning B, G, R, B, G, R... ordered data.77WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGR(78const uint8_t* data, size_t data_size, int* width, int* height);7980// Decode WebP images pointed to by 'data' to Y'UV format(*). The pointer81// returned is the Y samples buffer. Upon return, *u and *v will point to82// the U and V chroma data. These U and V buffers need NOT be passed to83// WebPFree(), unlike the returned Y luma one. The dimension of the U and V84// planes are both (*width + 1) / 2 and (*height + 1) / 2.85// Upon return, the Y buffer has a stride returned as '*stride', while U and V86// have a common stride returned as '*uv_stride'.87// 'width' and 'height' may be NULL, the other pointers must not be.88// Returns NULL in case of error.89// (*) Also named Y'CbCr. See: https://en.wikipedia.org/wiki/YCbCr90WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeYUV(91const uint8_t* data, size_t data_size, int* width, int* height,92uint8_t** u, uint8_t** v, int* stride, int* uv_stride);9394// These five functions are variants of the above ones, that decode the image95// directly into a pre-allocated buffer 'output_buffer'. The maximum storage96// available in this buffer is indicated by 'output_buffer_size'. If this97// storage is not sufficient (or an error occurred), NULL is returned.98// Otherwise, output_buffer is returned, for convenience.99// The parameter 'output_stride' specifies the distance (in bytes)100// between scanlines. Hence, output_buffer_size is expected to be at least101// output_stride x picture-height.102WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGBAInto(103const uint8_t* data, size_t data_size,104uint8_t* output_buffer, size_t output_buffer_size, int output_stride);105WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeARGBInto(106const uint8_t* data, size_t data_size,107uint8_t* output_buffer, size_t output_buffer_size, int output_stride);108WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGRAInto(109const uint8_t* data, size_t data_size,110uint8_t* output_buffer, size_t output_buffer_size, int output_stride);111112// RGB and BGR variants. Here too the transparency information, if present,113// will be dropped and ignored.114WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGBInto(115const uint8_t* data, size_t data_size,116uint8_t* output_buffer, size_t output_buffer_size, int output_stride);117WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGRInto(118const uint8_t* data, size_t data_size,119uint8_t* output_buffer, size_t output_buffer_size, int output_stride);120121// WebPDecodeYUVInto() is a variant of WebPDecodeYUV() that operates directly122// into pre-allocated luma/chroma plane buffers. This function requires the123// strides to be passed: one for the luma plane and one for each of the124// chroma ones. The size of each plane buffer is passed as 'luma_size',125// 'u_size' and 'v_size' respectively.126// Pointer to the luma plane ('*luma') is returned or NULL if an error occurred127// during decoding (or because some buffers were found to be too small).128WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeYUVInto(129const uint8_t* data, size_t data_size,130uint8_t* luma, size_t luma_size, int luma_stride,131uint8_t* u, size_t u_size, int u_stride,132uint8_t* v, size_t v_size, int v_stride);133134//------------------------------------------------------------------------------135// Output colorspaces and buffer136137// Colorspaces138// Note: the naming describes the byte-ordering of packed samples in memory.139// For instance, MODE_BGRA relates to samples ordered as B,G,R,A,B,G,R,A,...140// Non-capital names (e.g.:MODE_Argb) relates to pre-multiplied RGB channels.141// RGBA-4444 and RGB-565 colorspaces are represented by following byte-order:142// RGBA-4444: [r3 r2 r1 r0 g3 g2 g1 g0], [b3 b2 b1 b0 a3 a2 a1 a0], ...143// RGB-565: [r4 r3 r2 r1 r0 g5 g4 g3], [g2 g1 g0 b4 b3 b2 b1 b0], ...144// In the case WEBP_SWAP_16BITS_CSP is defined, the bytes are swapped for145// these two modes:146// RGBA-4444: [b3 b2 b1 b0 a3 a2 a1 a0], [r3 r2 r1 r0 g3 g2 g1 g0], ...147// RGB-565: [g2 g1 g0 b4 b3 b2 b1 b0], [r4 r3 r2 r1 r0 g5 g4 g3], ...148149typedef enum WEBP_CSP_MODE {150MODE_RGB = 0, MODE_RGBA = 1,151MODE_BGR = 2, MODE_BGRA = 3,152MODE_ARGB = 4, MODE_RGBA_4444 = 5,153MODE_RGB_565 = 6,154// RGB-premultiplied transparent modes (alpha value is preserved)155MODE_rgbA = 7,156MODE_bgrA = 8,157MODE_Argb = 9,158MODE_rgbA_4444 = 10,159// YUV modes must come after RGB ones.160MODE_YUV = 11, MODE_YUVA = 12, // yuv 4:2:0161MODE_LAST = 13162} WEBP_CSP_MODE;163164// Some useful macros:165static WEBP_INLINE int WebPIsPremultipliedMode(WEBP_CSP_MODE mode) {166return (mode == MODE_rgbA || mode == MODE_bgrA || mode == MODE_Argb ||167mode == MODE_rgbA_4444);168}169170static WEBP_INLINE int WebPIsAlphaMode(WEBP_CSP_MODE mode) {171return (mode == MODE_RGBA || mode == MODE_BGRA || mode == MODE_ARGB ||172mode == MODE_RGBA_4444 || mode == MODE_YUVA ||173WebPIsPremultipliedMode(mode));174}175176static WEBP_INLINE int WebPIsRGBMode(WEBP_CSP_MODE mode) {177return (mode < MODE_YUV);178}179180//------------------------------------------------------------------------------181// WebPDecBuffer: Generic structure for describing the output sample buffer.182183struct WebPRGBABuffer { // view as RGBA184uint8_t* rgba; // pointer to RGBA samples185int stride; // stride in bytes from one scanline to the next.186size_t size; // total size of the *rgba buffer.187};188189struct WebPYUVABuffer { // view as YUVA190uint8_t* y, *u, *v, *a; // pointer to luma, chroma U/V, alpha samples191int y_stride; // luma stride192int u_stride, v_stride; // chroma strides193int a_stride; // alpha stride194size_t y_size; // luma plane size195size_t u_size, v_size; // chroma planes size196size_t a_size; // alpha-plane size197};198199// Output buffer200struct WebPDecBuffer {201WEBP_CSP_MODE colorspace; // Colorspace.202int width, height; // Dimensions.203int is_external_memory; // If non-zero, 'internal_memory' pointer is not204// used. If value is '2' or more, the external205// memory is considered 'slow' and multiple206// read/write will be avoided.207union {208WebPRGBABuffer RGBA;209WebPYUVABuffer YUVA;210} u; // Nameless union of buffer parameters.211uint32_t pad[4]; // padding for later use212213uint8_t* private_memory; // Internally allocated memory (only when214// is_external_memory is 0). Should not be used215// externally, but accessed via the buffer union.216};217218// Internal, version-checked, entry point219WEBP_NODISCARD WEBP_EXTERN int WebPInitDecBufferInternal(WebPDecBuffer*, int);220221// Initialize the structure as empty. Must be called before any other use.222// Returns false in case of version mismatch223WEBP_NODISCARD static WEBP_INLINE int WebPInitDecBuffer(WebPDecBuffer* buffer) {224return WebPInitDecBufferInternal(buffer, WEBP_DECODER_ABI_VERSION);225}226227// Free any memory associated with the buffer. Must always be called last.228// Note: doesn't free the 'buffer' structure itself.229WEBP_EXTERN void WebPFreeDecBuffer(WebPDecBuffer* buffer);230231//------------------------------------------------------------------------------232// Enumeration of the status codes233234typedef enum WEBP_NODISCARD VP8StatusCode {235VP8_STATUS_OK = 0,236VP8_STATUS_OUT_OF_MEMORY,237VP8_STATUS_INVALID_PARAM,238VP8_STATUS_BITSTREAM_ERROR,239VP8_STATUS_UNSUPPORTED_FEATURE,240VP8_STATUS_SUSPENDED,241VP8_STATUS_USER_ABORT,242VP8_STATUS_NOT_ENOUGH_DATA243} VP8StatusCode;244245//------------------------------------------------------------------------------246// Incremental decoding247//248// This API allows streamlined decoding of partial data.249// Picture can be incrementally decoded as data become available thanks to the250// WebPIDecoder object. This object can be left in a SUSPENDED state if the251// picture is only partially decoded, pending additional input.252// Code example:253/*254WebPInitDecBuffer(&output_buffer);255output_buffer.colorspace = mode;256...257WebPIDecoder* idec = WebPINewDecoder(&output_buffer);258while (additional_data_is_available) {259// ... (get additional data in some new_data[] buffer)260status = WebPIAppend(idec, new_data, new_data_size);261if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) {262break; // an error occurred.263}264265// The above call decodes the current available buffer.266// Part of the image can now be refreshed by calling267// WebPIDecGetRGB()/WebPIDecGetYUVA() etc.268}269WebPIDelete(idec);270*/271272// Creates a new incremental decoder with the supplied buffer parameter.273// This output_buffer can be passed NULL, in which case a default output buffer274// is used (with MODE_RGB). Otherwise, an internal reference to 'output_buffer'275// is kept, which means that the lifespan of 'output_buffer' must be larger than276// that of the returned WebPIDecoder object.277// The supplied 'output_buffer' content MUST NOT be changed between calls to278// WebPIAppend() or WebPIUpdate() unless 'output_buffer.is_external_memory' is279// not set to 0. In such a case, it is allowed to modify the pointers, size and280// stride of output_buffer.u.RGBA or output_buffer.u.YUVA, provided they remain281// within valid bounds.282// All other fields of WebPDecBuffer MUST remain constant between calls.283// Returns NULL if the allocation failed.284WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewDecoder(285WebPDecBuffer* output_buffer);286287// This function allocates and initializes an incremental-decoder object, which288// will output the RGB/A samples specified by 'csp' into a preallocated289// buffer 'output_buffer'. The size of this buffer is at least290// 'output_buffer_size' and the stride (distance in bytes between two scanlines)291// is specified by 'output_stride'.292// Additionally, output_buffer can be passed NULL in which case the output293// buffer will be allocated automatically when the decoding starts. The294// colorspace 'csp' is taken into account for allocating this buffer. All other295// parameters are ignored.296// Returns NULL if the allocation failed, or if some parameters are invalid.297WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewRGB(298WEBP_CSP_MODE csp,299uint8_t* output_buffer, size_t output_buffer_size, int output_stride);300301// This function allocates and initializes an incremental-decoder object, which302// will output the raw luma/chroma samples into a preallocated planes if303// supplied. The luma plane is specified by its pointer 'luma', its size304// 'luma_size' and its stride 'luma_stride'. Similarly, the chroma-u plane305// is specified by the 'u', 'u_size' and 'u_stride' parameters, and the chroma-v306// plane by 'v' and 'v_size'. And same for the alpha-plane. The 'a' pointer307// can be pass NULL in case one is not interested in the transparency plane.308// Conversely, 'luma' can be passed NULL if no preallocated planes are supplied.309// In this case, the output buffer will be automatically allocated (using310// MODE_YUVA) when decoding starts. All parameters are then ignored.311// Returns NULL if the allocation failed or if a parameter is invalid.312WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewYUVA(313uint8_t* luma, size_t luma_size, int luma_stride,314uint8_t* u, size_t u_size, int u_stride,315uint8_t* v, size_t v_size, int v_stride,316uint8_t* a, size_t a_size, int a_stride);317318// Deprecated version of the above, without the alpha plane.319// Kept for backward compatibility.320WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewYUV(321uint8_t* luma, size_t luma_size, int luma_stride,322uint8_t* u, size_t u_size, int u_stride,323uint8_t* v, size_t v_size, int v_stride);324325// Deletes the WebPIDecoder object and associated memory. Must always be called326// if WebPINewDecoder, WebPINewRGB or WebPINewYUV succeeded.327WEBP_EXTERN void WebPIDelete(WebPIDecoder* idec);328329// Copies and decodes the next available data. Returns VP8_STATUS_OK when330// the image is successfully decoded. Returns VP8_STATUS_SUSPENDED when more331// data is expected. Returns error in other cases.332WEBP_EXTERN VP8StatusCode WebPIAppend(333WebPIDecoder* idec, const uint8_t* data, size_t data_size);334335// A variant of the above function to be used when data buffer contains336// partial data from the beginning. In this case data buffer is not copied337// to the internal memory.338// Note that the value of the 'data' pointer can change between calls to339// WebPIUpdate, for instance when the data buffer is resized to fit larger data.340WEBP_EXTERN VP8StatusCode WebPIUpdate(341WebPIDecoder* idec, const uint8_t* data, size_t data_size);342343// Returns the RGB/A image decoded so far. Returns NULL if output params344// are not initialized yet. The RGB/A output type corresponds to the colorspace345// specified during call to WebPINewDecoder() or WebPINewRGB().346// *last_y is the index of last decoded row in raster scan order. Some pointers347// (*last_y, *width etc.) can be NULL if corresponding information is not348// needed. The values in these pointers are only valid on successful (non-NULL)349// return.350WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPIDecGetRGB(351const WebPIDecoder* idec, int* last_y,352int* width, int* height, int* stride);353354// Same as above function to get a YUVA image. Returns pointer to the luma355// plane or NULL in case of error. If there is no alpha information356// the alpha pointer '*a' will be returned NULL.357WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPIDecGetYUVA(358const WebPIDecoder* idec, int* last_y,359uint8_t** u, uint8_t** v, uint8_t** a,360int* width, int* height, int* stride, int* uv_stride, int* a_stride);361362// Deprecated alpha-less version of WebPIDecGetYUVA(): it will ignore the363// alpha information (if present). Kept for backward compatibility.364WEBP_NODISCARD static WEBP_INLINE uint8_t* WebPIDecGetYUV(365const WebPIDecoder* idec, int* last_y, uint8_t** u, uint8_t** v,366int* width, int* height, int* stride, int* uv_stride) {367return WebPIDecGetYUVA(idec, last_y, u, v, NULL, width, height,368stride, uv_stride, NULL);369}370371// Generic call to retrieve information about the displayable area.372// If non NULL, the left/right/width/height pointers are filled with the visible373// rectangular area so far.374// Returns NULL in case the incremental decoder object is in an invalid state.375// Otherwise returns the pointer to the internal representation. This structure376// is read-only, tied to WebPIDecoder's lifespan and should not be modified.377WEBP_NODISCARD WEBP_EXTERN const WebPDecBuffer* WebPIDecodedArea(378const WebPIDecoder* idec, int* left, int* top, int* width, int* height);379380//------------------------------------------------------------------------------381// Advanced decoding parametrization382//383// Code sample for using the advanced decoding API384/*385// A) Init a configuration object386WebPDecoderConfig config;387CHECK(WebPInitDecoderConfig(&config));388389// B) optional: retrieve the bitstream's features.390CHECK(WebPGetFeatures(data, data_size, &config.input) == VP8_STATUS_OK);391392// C) Adjust 'config', if needed393config.options.no_fancy_upsampling = 1;394config.output.colorspace = MODE_BGRA;395// etc.396397// Note that you can also make config.output point to an externally398// supplied memory buffer, provided it's big enough to store the decoded399// picture. Otherwise, config.output will just be used to allocate memory400// and store the decoded picture.401402// D) Decode!403CHECK(WebPDecode(data, data_size, &config) == VP8_STATUS_OK);404405// E) Decoded image is now in config.output (and config.output.u.RGBA)406407// F) Reclaim memory allocated in config's object. It's safe to call408// this function even if the memory is external and wasn't allocated409// by WebPDecode().410WebPFreeDecBuffer(&config.output);411*/412413// Features gathered from the bitstream414struct WebPBitstreamFeatures {415int width; // Width in pixels, as read from the bitstream.416int height; // Height in pixels, as read from the bitstream.417int has_alpha; // True if the bitstream contains an alpha channel.418int has_animation; // True if the bitstream is an animation.419int format; // 0 = undefined (/mixed), 1 = lossy, 2 = lossless420421uint32_t pad[5]; // padding for later use422};423424// Internal, version-checked, entry point425WEBP_EXTERN VP8StatusCode WebPGetFeaturesInternal(426const uint8_t*, size_t, WebPBitstreamFeatures*, int);427428// Retrieve features from the bitstream. The *features structure is filled429// with information gathered from the bitstream.430// Returns VP8_STATUS_OK when the features are successfully retrieved. Returns431// VP8_STATUS_NOT_ENOUGH_DATA when more data is needed to retrieve the432// features from headers. Returns error in other cases.433// Note: The following chunk sequences (before the raw VP8/VP8L data) are434// considered valid by this function:435// RIFF + VP8(L)436// RIFF + VP8X + (optional chunks) + VP8(L)437// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.438// VP8(L) <-- Not a valid WebP format: only allowed for internal purpose.439static WEBP_INLINE VP8StatusCode WebPGetFeatures(440const uint8_t* data, size_t data_size,441WebPBitstreamFeatures* features) {442return WebPGetFeaturesInternal(data, data_size, features,443WEBP_DECODER_ABI_VERSION);444}445446// Decoding options447struct WebPDecoderOptions {448int bypass_filtering; // if true, skip the in-loop filtering449int no_fancy_upsampling; // if true, use faster pointwise upsampler450int use_cropping; // if true, cropping is applied _first_451int crop_left, crop_top; // top-left position for cropping.452// Will be snapped to even values.453int crop_width, crop_height; // dimension of the cropping area454int use_scaling; // if true, scaling is applied _afterward_455int scaled_width, scaled_height; // final resolution. if one is 0, it is456// guessed from the other one to keep the457// original ratio.458int use_threads; // if true, use multi-threaded decoding459int dithering_strength; // dithering strength (0=Off, 100=full)460int flip; // if true, flip output vertically461int alpha_dithering_strength; // alpha dithering strength in [0..100]462463uint32_t pad[5]; // padding for later use464};465466// Main object storing the configuration for advanced decoding.467struct WebPDecoderConfig {468WebPBitstreamFeatures input; // Immutable bitstream features (optional)469WebPDecBuffer output; // Output buffer (can point to external mem)470WebPDecoderOptions options; // Decoding options471};472473// Internal, version-checked, entry point474WEBP_NODISCARD WEBP_EXTERN int WebPInitDecoderConfigInternal(WebPDecoderConfig*,475int);476477// Initialize the configuration as empty. This function must always be478// called first, unless WebPGetFeatures() is to be called.479// Returns false in case of mismatched version.480WEBP_NODISCARD static WEBP_INLINE int WebPInitDecoderConfig(481WebPDecoderConfig* config) {482return WebPInitDecoderConfigInternal(config, WEBP_DECODER_ABI_VERSION);483}484485// Returns true if 'config' is non-NULL and all configuration parameters are486// within their valid ranges.487WEBP_NODISCARD WEBP_EXTERN int WebPValidateDecoderConfig(488const WebPDecoderConfig* config);489490// Instantiate a new incremental decoder object with the requested491// configuration. The bitstream can be passed using 'data' and 'data_size'492// parameter, in which case the features will be parsed and stored into493// config->input. Otherwise, 'data' can be NULL and no parsing will occur.494// Note that 'config' can be NULL too, in which case a default configuration495// is used. If 'config' is not NULL, it must outlive the WebPIDecoder object496// as some references to its fields will be used. No internal copy of 'config'497// is made.498// The return WebPIDecoder object must always be deleted calling WebPIDelete().499// Returns NULL in case of error (and config->status will then reflect500// the error condition, if available).501WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPIDecode(502const uint8_t* data, size_t data_size, WebPDecoderConfig* config);503504// Non-incremental version. This version decodes the full data at once, taking505// 'config' into account. Returns decoding status (which should be VP8_STATUS_OK506// if the decoding was successful). Note that 'config' cannot be NULL.507WEBP_EXTERN VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,508WebPDecoderConfig* config);509510#ifdef __cplusplus511} // extern "C"512#endif513514#endif // WEBP_WEBP_DECODE_H_515516517