mirror of
				https://github.com/webmproject/libwebp.git
				synced 2025-10-31 02:15:42 +01:00 
			
		
		
		
	add extra meaning to WebPDecBuffer::is_external_memory
If value is '2', it means the buffer is a 'slow' one, like GPU-mapped memory.
This change is backward compatible (setting is_external_memory to 2
will be a no-op in previous libraries)
dwebp:  add flags to force a particular colorspace format
new flags is:
   -pixel_format {RGB,RGBA,BGR,BGRA,ARGB,RGBA_4444,RGB_565,
                  rgbA,bgrA,Argb,rgbA_4444,YUV,YUVA}
and also,external_memory {0,1,2}
These flags are mostly for debuggging purpose, and hence are not documented.
Change-Id: Iac88ce1e10b35163dd7af57f9660f062f5d8ed5e
			
			
This commit is contained in:
		
							
								
								
									
										177
									
								
								examples/dwebp.c
									
									
									
									
									
								
							
							
						
						
									
										177
									
								
								examples/dwebp.c
									
									
									
									
									
								
							| @@ -67,8 +67,13 @@ typedef enum { | |||||||
|   PGM, |   PGM, | ||||||
|   BMP, |   BMP, | ||||||
|   TIFF, |   TIFF, | ||||||
|   YUV, |   RAW_YUV, | ||||||
|   ALPHA_PLANE_ONLY  // this is for experimenting only |   ALPHA_PLANE_ONLY,  // this is for experimenting only | ||||||
|  |   // forced colorspace output (for testing, mostly) | ||||||
|  |   RGB, RGBA, BGR, BGRA, ARGB, | ||||||
|  |   RGBA_4444, RGB_565, | ||||||
|  |   rgbA, bgrA, Argb, rgbA_4444, | ||||||
|  |   YUV, YUVA | ||||||
| } OutputFileFormat; | } OutputFileFormat; | ||||||
|  |  | ||||||
| #ifdef HAVE_WINCODEC_H | #ifdef HAVE_WINCODEC_H | ||||||
| @@ -175,7 +180,7 @@ static int WritePNG(const char* out_file_name, int use_stdout, | |||||||
|   const uint32_t height = buffer->height; |   const uint32_t height = buffer->height; | ||||||
|   uint8_t* const rgb = buffer->u.RGBA.rgba; |   uint8_t* const rgb = buffer->u.RGBA.rgba; | ||||||
|   const int stride = buffer->u.RGBA.stride; |   const int stride = buffer->u.RGBA.stride; | ||||||
|   const int has_alpha = (buffer->colorspace == MODE_BGRA); |   const int has_alpha = WebPIsAlphaMode(buffer->colorspace); | ||||||
|  |  | ||||||
|   return SUCCEEDED(WriteUsingWIC(out_file_name, use_stdout, |   return SUCCEEDED(WriteUsingWIC(out_file_name, use_stdout, | ||||||
|                                  MAKE_REFGUID(GUID_ContainerFormatPng), |                                  MAKE_REFGUID(GUID_ContainerFormatPng), | ||||||
| @@ -193,7 +198,7 @@ static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) { | |||||||
|   const uint32_t height = buffer->height; |   const uint32_t height = buffer->height; | ||||||
|   uint8_t* const rgb = buffer->u.RGBA.rgba; |   uint8_t* const rgb = buffer->u.RGBA.rgba; | ||||||
|   const int stride = buffer->u.RGBA.stride; |   const int stride = buffer->u.RGBA.stride; | ||||||
|   const int has_alpha = (buffer->colorspace == MODE_RGBA); |   const int has_alpha = WebPIsAlphaMode(buffer->colorspace); | ||||||
|   volatile png_structp png; |   volatile png_structp png; | ||||||
|   volatile png_infop info; |   volatile png_infop info; | ||||||
|   png_uint_32 y; |   png_uint_32 y; | ||||||
| @@ -259,6 +264,24 @@ static int WritePPM(FILE* fout, const WebPDecBuffer* const buffer, int alpha) { | |||||||
|   return 1; |   return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Save 16b mode (RGBA4444, RGB565, ...) for debugging purpose. | ||||||
|  | static int Write16bAsPGM(FILE* fout, const WebPDecBuffer* const buffer) { | ||||||
|  |   const uint32_t width = buffer->width; | ||||||
|  |   const uint32_t height = buffer->height; | ||||||
|  |   const uint8_t* const rgba = buffer->u.RGBA.rgba; | ||||||
|  |   const int stride = buffer->u.RGBA.stride; | ||||||
|  |   const uint32_t bytes_per_px = 2; | ||||||
|  |   uint32_t y; | ||||||
|  |  | ||||||
|  |   fprintf(fout, "P5\n%u %u\n255\n", width * bytes_per_px, height); | ||||||
|  |   for (y = 0; y < height; ++y) { | ||||||
|  |     if (fwrite(rgba + y * stride, width, bytes_per_px, fout) != bytes_per_px) { | ||||||
|  |       return 0; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
| static void PutLE16(uint8_t* const dst, uint32_t value) { | static void PutLE16(uint8_t* const dst, uint32_t value) { | ||||||
|   dst[0] = (value >> 0) & 0xff; |   dst[0] = (value >> 0) & 0xff; | ||||||
|   dst[1] = (value >> 8) & 0xff; |   dst[1] = (value >> 8) & 0xff; | ||||||
| @@ -271,7 +294,7 @@ static void PutLE32(uint8_t* const dst, uint32_t value) { | |||||||
|  |  | ||||||
| #define BMP_HEADER_SIZE 54 | #define BMP_HEADER_SIZE 54 | ||||||
| static int WriteBMP(FILE* fout, const WebPDecBuffer* const buffer) { | static int WriteBMP(FILE* fout, const WebPDecBuffer* const buffer) { | ||||||
|   const int has_alpha = (buffer->colorspace != MODE_BGR); |   const int has_alpha = WebPIsAlphaMode(buffer->colorspace); | ||||||
|   const uint32_t width = buffer->width; |   const uint32_t width = buffer->width; | ||||||
|   const uint32_t height = buffer->height; |   const uint32_t height = buffer->height; | ||||||
|   const uint8_t* const rgba = buffer->u.RGBA.rgba; |   const uint8_t* const rgba = buffer->u.RGBA.rgba; | ||||||
| @@ -332,7 +355,7 @@ static int WriteBMP(FILE* fout, const WebPDecBuffer* const buffer) { | |||||||
| #define TIFF_HEADER_SIZE (EXTRA_DATA_OFFSET + EXTRA_DATA_SIZE) | #define TIFF_HEADER_SIZE (EXTRA_DATA_OFFSET + EXTRA_DATA_SIZE) | ||||||
|  |  | ||||||
| static int WriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) { | static int WriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) { | ||||||
|   const int has_alpha = (buffer->colorspace != MODE_RGB); |   const int has_alpha = WebPIsAlphaMode(buffer->colorspace); | ||||||
|   const uint32_t width = buffer->width; |   const uint32_t width = buffer->width; | ||||||
|   const uint32_t height = buffer->height; |   const uint32_t height = buffer->height; | ||||||
|   const uint8_t* const rgba = buffer->u.RGBA.rgba; |   const uint8_t* const rgba = buffer->u.RGBA.rgba; | ||||||
| @@ -418,7 +441,7 @@ static int WriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) { | |||||||
| // format=PGM: save a grayscale PGM file using the IMC4 layout | // format=PGM: save a grayscale PGM file using the IMC4 layout | ||||||
| // (http://www.fourcc.org/yuv.php#IMC4). This is a very convenient format for | // (http://www.fourcc.org/yuv.php#IMC4). This is a very convenient format for | ||||||
| // viewing the samples, esp. for odd dimensions. | // viewing the samples, esp. for odd dimensions. | ||||||
| // format=YUV: just save the Y/U/V/A planes sequentially without header. | // format=RAW_YUV: just save the Y/U/V/A planes sequentially without header. | ||||||
| static int WritePGMOrYUV(FILE* fout, const WebPDecBuffer* const buffer, | static int WritePGMOrYUV(FILE* fout, const WebPDecBuffer* const buffer, | ||||||
|                          OutputFileFormat format) { |                          OutputFileFormat format) { | ||||||
|   const int width = buffer->width; |   const int width = buffer->width; | ||||||
| @@ -426,7 +449,7 @@ static int WritePGMOrYUV(FILE* fout, const WebPDecBuffer* const buffer, | |||||||
|   const WebPYUVABuffer* const yuv = &buffer->u.YUVA; |   const WebPYUVABuffer* const yuv = &buffer->u.YUVA; | ||||||
|   int ok = 1; |   int ok = 1; | ||||||
|   int y; |   int y; | ||||||
|   const int pad = (format == YUV) ? 0 : 1; |   const int pad = (format == RAW_YUV) ? 0 : 1; | ||||||
|   const int uv_width = (width + 1) / 2; |   const int uv_width = (width + 1) / 2; | ||||||
|   const int uv_height = (height + 1) / 2; |   const int uv_height = (height + 1) / 2; | ||||||
|   const int out_stride = (width + pad) & ~pad; |   const int out_stride = (width + pad) & ~pad; | ||||||
| @@ -487,7 +510,9 @@ static int SaveOutput(const WebPDecBuffer* const buffer, | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (format == PNG) { |   if (format == PNG || | ||||||
|  |       format == RGBA || format == BGRA || format == ARGB || | ||||||
|  |       format == rgbA || format == bgrA || format == Argb) { | ||||||
| #ifdef HAVE_WINCODEC_H | #ifdef HAVE_WINCODEC_H | ||||||
|     ok &= WritePNG(out_file, use_stdout, buffer); |     ok &= WritePNG(out_file, use_stdout, buffer); | ||||||
| #else | #else | ||||||
| @@ -495,14 +520,17 @@ static int SaveOutput(const WebPDecBuffer* const buffer, | |||||||
| #endif | #endif | ||||||
|   } else if (format == PAM) { |   } else if (format == PAM) { | ||||||
|     ok &= WritePPM(fout, buffer, 1); |     ok &= WritePPM(fout, buffer, 1); | ||||||
|   } else if (format == PPM) { |   } else if (format == PPM || format == RGB || format == BGR) { | ||||||
|     ok &= WritePPM(fout, buffer, 0); |     ok &= WritePPM(fout, buffer, 0); | ||||||
|  |   } else if (format == RGBA_4444 || format == RGB_565 || format == rgbA_4444) { | ||||||
|  |     ok &= Write16bAsPGM(fout, buffer); | ||||||
|   } else if (format == BMP) { |   } else if (format == BMP) { | ||||||
|     ok &= WriteBMP(fout, buffer); |     ok &= WriteBMP(fout, buffer); | ||||||
|   } else if (format == TIFF) { |   } else if (format == TIFF) { | ||||||
|     ok &= WriteTIFF(fout, buffer); |     ok &= WriteTIFF(fout, buffer); | ||||||
|   } else if (format == PGM || format == YUV) { |   } else if (format == PGM || format == RAW_YUV || | ||||||
|     ok &= WritePGMOrYUV(fout, buffer, format); |              format == YUV || format == YUVA) { | ||||||
|  |     ok &= WritePGMOrYUV(fout, buffer, format == RAW_YUV ? RAW_YUV : PGM); | ||||||
|   } else if (format == ALPHA_PLANE_ONLY) { |   } else if (format == ALPHA_PLANE_ONLY) { | ||||||
|     ok &= WriteAlphaPlane(fout, buffer); |     ok &= WriteAlphaPlane(fout, buffer); | ||||||
|   } |   } | ||||||
| @@ -569,6 +597,70 @@ static const char* const kFormatType[] = { | |||||||
|   "unspecified", "lossy", "lossless" |   "unspecified", "lossy", "lossless" | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | static uint8_t* AllocateExternalBuffer(WebPDecoderConfig* config, | ||||||
|  |                                        OutputFileFormat format, | ||||||
|  |                                        int use_external_memory) { | ||||||
|  |   uint8_t* external_buffer = NULL; | ||||||
|  |   WebPDecBuffer* const output_buffer = &config->output; | ||||||
|  |   int w = config->input.width; | ||||||
|  |   int h = config->input.height; | ||||||
|  |   if (config->options.use_scaling) { | ||||||
|  |     w = config->options.scaled_width; | ||||||
|  |     h = config->options.scaled_height; | ||||||
|  |   } else if (config->options.use_cropping) { | ||||||
|  |     w = config->options.crop_width; | ||||||
|  |     h = config->options.crop_height; | ||||||
|  |   } | ||||||
|  |   if (format >= RGB && format <= rgbA_4444) { | ||||||
|  |     const int bpp = (format == RGB || format == BGR) ? 3 | ||||||
|  |                   : (format == RGBA_4444 || format == rgbA_4444 || | ||||||
|  |                      format == RGB_565) ? 2 | ||||||
|  |                   : 4; | ||||||
|  |     uint32_t stride = bpp * w + 7;   // <- just for exercising | ||||||
|  |     external_buffer = (uint8_t*)malloc(stride * h); | ||||||
|  |     if (external_buffer == NULL) return NULL; | ||||||
|  |     output_buffer->u.RGBA.stride = stride; | ||||||
|  |     output_buffer->u.RGBA.size = stride * h; | ||||||
|  |     output_buffer->u.RGBA.rgba = external_buffer; | ||||||
|  |   } else {    // YUV and YUVA | ||||||
|  |     const int has_alpha = WebPIsAlphaMode(output_buffer->colorspace); | ||||||
|  |     uint8_t* tmp; | ||||||
|  |     uint32_t stride = w + 3; | ||||||
|  |     uint32_t uv_stride = (w + 1) / 2 + 13; | ||||||
|  |     uint32_t total_size = stride * h * (has_alpha ? 2 : 1) | ||||||
|  |                         + 2 * uv_stride * (h + 1) / 2; | ||||||
|  |     assert(format >= YUV && format <= YUVA); | ||||||
|  |     external_buffer = (uint8_t*)malloc(total_size); | ||||||
|  |     if (external_buffer == NULL) return NULL; | ||||||
|  |     tmp = external_buffer; | ||||||
|  |     output_buffer->u.YUVA.y = tmp; | ||||||
|  |     output_buffer->u.YUVA.y_stride = stride; | ||||||
|  |     output_buffer->u.YUVA.y_size = stride * h; | ||||||
|  |     tmp += output_buffer->u.YUVA.y_size; | ||||||
|  |     if (has_alpha) { | ||||||
|  |       output_buffer->u.YUVA.a = tmp; | ||||||
|  |       output_buffer->u.YUVA.a_stride = stride; | ||||||
|  |       output_buffer->u.YUVA.a_size = stride * h; | ||||||
|  |       tmp += output_buffer->u.YUVA.a_size; | ||||||
|  |     } else { | ||||||
|  |       output_buffer->u.YUVA.a = NULL; | ||||||
|  |       output_buffer->u.YUVA.a_stride = 0; | ||||||
|  |     } | ||||||
|  |     output_buffer->u.YUVA.u = tmp; | ||||||
|  |     output_buffer->u.YUVA.u_stride = uv_stride; | ||||||
|  |     output_buffer->u.YUVA.u_size = uv_stride * (h + 1) / 2; | ||||||
|  |     tmp += output_buffer->u.YUVA.u_size; | ||||||
|  |  | ||||||
|  |     output_buffer->u.YUVA.v = tmp; | ||||||
|  |     output_buffer->u.YUVA.v_stride = uv_stride; | ||||||
|  |     output_buffer->u.YUVA.v_size = uv_stride * (h + 1) / 2; | ||||||
|  |     tmp += output_buffer->u.YUVA.v_size; | ||||||
|  |     assert(tmp <= external_buffer + total_size); | ||||||
|  |   } | ||||||
|  |   output_buffer->is_external_memory = use_external_memory; | ||||||
|  |   return external_buffer; | ||||||
|  | } | ||||||
|  |  | ||||||
| int main(int argc, const char *argv[]) { | int main(int argc, const char *argv[]) { | ||||||
|   int ok = 0; |   int ok = 0; | ||||||
|   const char *in_file = NULL; |   const char *in_file = NULL; | ||||||
| @@ -578,6 +670,10 @@ int main(int argc, const char *argv[]) { | |||||||
|   WebPDecBuffer* const output_buffer = &config.output; |   WebPDecBuffer* const output_buffer = &config.output; | ||||||
|   WebPBitstreamFeatures* const bitstream = &config.input; |   WebPBitstreamFeatures* const bitstream = &config.input; | ||||||
|   OutputFileFormat format = PNG; |   OutputFileFormat format = PNG; | ||||||
|  |   uint8_t* external_buffer = NULL; | ||||||
|  |   int use_external_memory = 0; | ||||||
|  |   const uint8_t* data = NULL; | ||||||
|  |  | ||||||
|   int incremental = 0; |   int incremental = 0; | ||||||
|   int c; |   int c; | ||||||
|  |  | ||||||
| @@ -617,7 +713,32 @@ int main(int argc, const char *argv[]) { | |||||||
|     } else if (!strcmp(argv[c], "-pgm")) { |     } else if (!strcmp(argv[c], "-pgm")) { | ||||||
|       format = PGM; |       format = PGM; | ||||||
|     } else if (!strcmp(argv[c], "-yuv")) { |     } else if (!strcmp(argv[c], "-yuv")) { | ||||||
|       format = YUV; |       format = RAW_YUV; | ||||||
|  |     } else if (!strcmp(argv[c], "-pixel_format") && c < argc - 1) { | ||||||
|  |       const char* const fmt = argv[++c]; | ||||||
|  |       if      (!strcmp(fmt, "RGB"))  format = RGB; | ||||||
|  |       else if (!strcmp(fmt, "RGBA")) format = RGBA; | ||||||
|  |       else if (!strcmp(fmt, "BGR"))  format = BGR; | ||||||
|  |       else if (!strcmp(fmt, "BGRA")) format = BGRA; | ||||||
|  |       else if (!strcmp(fmt, "ARGB")) format = ARGB; | ||||||
|  |       else if (!strcmp(fmt, "RGBA_4444")) format = RGBA_4444; | ||||||
|  |       else if (!strcmp(fmt, "RGB_565")) format = RGB_565; | ||||||
|  |       else if (!strcmp(fmt, "rgbA")) format = rgbA; | ||||||
|  |       else if (!strcmp(fmt, "bgrA")) format = bgrA; | ||||||
|  |       else if (!strcmp(fmt, "Argb")) format = Argb; | ||||||
|  |       else if (!strcmp(fmt, "rgbA_4444")) format = rgbA_4444; | ||||||
|  |       else if (!strcmp(fmt, "YUV"))  format = YUV; | ||||||
|  |       else if (!strcmp(fmt, "YUVA")) format = YUVA; | ||||||
|  |       else { | ||||||
|  |         fprintf(stderr, "Can't parse pixel_format %s\n", fmt); | ||||||
|  |         parse_error = 1; | ||||||
|  |       } | ||||||
|  |     } else if (!strcmp(argv[c], "-external_memory") && c < argc - 1) { | ||||||
|  |       use_external_memory = ExUtilGetInt(argv[++c], 0, &parse_error); | ||||||
|  |       parse_error |= (use_external_memory > 2 || use_external_memory < 0); | ||||||
|  |       if (parse_error) { | ||||||
|  |         fprintf(stderr, "Can't parse 'external_memory' value %s\n", argv[c]); | ||||||
|  |       } | ||||||
|     } else if (!strcmp(argv[c], "-mt")) { |     } else if (!strcmp(argv[c], "-mt")) { | ||||||
|       config.options.use_threads = 1; |       config.options.use_threads = 1; | ||||||
|     } else if (!strcmp(argv[c], "-alpha_dither")) { |     } else if (!strcmp(argv[c], "-alpha_dither")) { | ||||||
| @@ -676,7 +797,6 @@ int main(int argc, const char *argv[]) { | |||||||
|   { |   { | ||||||
|     VP8StatusCode status = VP8_STATUS_OK; |     VP8StatusCode status = VP8_STATUS_OK; | ||||||
|     size_t data_size = 0; |     size_t data_size = 0; | ||||||
|     const uint8_t* data = NULL; |  | ||||||
|     if (!ExUtilLoadWebP(in_file, &data, &data_size, bitstream)) { |     if (!ExUtilLoadWebP(in_file, &data, &data_size, bitstream)) { | ||||||
|       return -1; |       return -1; | ||||||
|     } |     } | ||||||
| @@ -703,15 +823,33 @@ int main(int argc, const char *argv[]) { | |||||||
|             bitstream->has_alpha ? MODE_rgbA : MODE_RGB; |             bitstream->has_alpha ? MODE_rgbA : MODE_RGB; | ||||||
|         break; |         break; | ||||||
|       case PGM: |       case PGM: | ||||||
|       case YUV: |       case RAW_YUV: | ||||||
|         output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV; |         output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV; | ||||||
|         break; |         break; | ||||||
|       case ALPHA_PLANE_ONLY: |       case ALPHA_PLANE_ONLY: | ||||||
|         output_buffer->colorspace = MODE_YUVA; |         output_buffer->colorspace = MODE_YUVA; | ||||||
|         break; |         break; | ||||||
|       default: |       // forced modes: | ||||||
|         free((void*)data); |       case RGB: output_buffer->colorspace = MODE_RGB; break; | ||||||
|         return -1; |       case RGBA: output_buffer->colorspace = MODE_RGBA; break; | ||||||
|  |       case BGR: output_buffer->colorspace = MODE_BGR; break; | ||||||
|  |       case BGRA: output_buffer->colorspace = MODE_BGRA; break; | ||||||
|  |       case ARGB: output_buffer->colorspace = MODE_ARGB; break; | ||||||
|  |       case RGBA_4444: output_buffer->colorspace = MODE_RGBA_4444; break; | ||||||
|  |       case RGB_565: output_buffer->colorspace = MODE_RGB_565; break; | ||||||
|  |       case rgbA: output_buffer->colorspace = MODE_rgbA; break; | ||||||
|  |       case bgrA: output_buffer->colorspace = MODE_bgrA; break; | ||||||
|  |       case Argb: output_buffer->colorspace = MODE_Argb; break; | ||||||
|  |       case rgbA_4444: output_buffer->colorspace = MODE_rgbA_4444; break; | ||||||
|  |       case YUV: output_buffer->colorspace = MODE_YUV; break; | ||||||
|  |       case YUVA: output_buffer->colorspace = MODE_YUVA; break; | ||||||
|  |       default: goto Exit; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (use_external_memory > 0 && format >= RGB) { | ||||||
|  |       external_buffer = AllocateExternalBuffer(&config, format, | ||||||
|  |                                                use_external_memory); | ||||||
|  |       if (external_buffer == NULL) goto Exit; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (incremental) { |     if (incremental) { | ||||||
| @@ -720,7 +858,6 @@ int main(int argc, const char *argv[]) { | |||||||
|       status = ExUtilDecodeWebP(data, data_size, verbose, &config); |       status = ExUtilDecodeWebP(data, data_size, verbose, &config); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     free((void*)data); |  | ||||||
|     ok = (status == VP8_STATUS_OK); |     ok = (status == VP8_STATUS_OK); | ||||||
|     if (!ok) { |     if (!ok) { | ||||||
|       ExUtilPrintWebPError(in_file, status); |       ExUtilPrintWebPError(in_file, status); | ||||||
| @@ -750,6 +887,8 @@ int main(int argc, const char *argv[]) { | |||||||
|   } |   } | ||||||
|  Exit: |  Exit: | ||||||
|   WebPFreeDecBuffer(output_buffer); |   WebPFreeDecBuffer(output_buffer); | ||||||
|  |   free((void*)external_buffer); | ||||||
|  |   free((void*)data); | ||||||
|   return ok ? 0 : -1; |   return ok ? 0 : -1; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -92,7 +92,7 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) { | |||||||
|     return VP8_STATUS_INVALID_PARAM; |     return VP8_STATUS_INVALID_PARAM; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (!buffer->is_external_memory && buffer->private_memory == NULL) { |   if (buffer->is_external_memory <= 0 && buffer->private_memory == NULL) { | ||||||
|     uint8_t* output; |     uint8_t* output; | ||||||
|     int uv_stride = 0, a_stride = 0; |     int uv_stride = 0, a_stride = 0; | ||||||
|     uint64_t uv_size = 0, a_size = 0, total_size; |     uint64_t uv_size = 0, a_size = 0, total_size; | ||||||
| @@ -227,7 +227,7 @@ int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) { | |||||||
|  |  | ||||||
| void WebPFreeDecBuffer(WebPDecBuffer* buffer) { | void WebPFreeDecBuffer(WebPDecBuffer* buffer) { | ||||||
|   if (buffer != NULL) { |   if (buffer != NULL) { | ||||||
|     if (!buffer->is_external_memory) { |     if (buffer->is_external_memory <= 0) { | ||||||
|       WebPSafeFree(buffer->private_memory); |       WebPSafeFree(buffer->private_memory); | ||||||
|     } |     } | ||||||
|     buffer->private_memory = NULL; |     buffer->private_memory = NULL; | ||||||
| @@ -256,5 +256,45 @@ void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst) { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| //------------------------------------------------------------------------------ | VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src_buf, | ||||||
|  |                                       WebPDecBuffer* const dst_buf) { | ||||||
|  |   assert(src_buf != NULL && dst_buf != NULL); | ||||||
|  |   assert(src_buf->colorspace == dst_buf->colorspace); | ||||||
|  |  | ||||||
|  |   dst_buf->width = src_buf->width; | ||||||
|  |   dst_buf->height = src_buf->height; | ||||||
|  |   if (CheckDecBuffer(dst_buf) != VP8_STATUS_OK) { | ||||||
|  |     return VP8_STATUS_INVALID_PARAM; | ||||||
|  |   } | ||||||
|  |   if (WebPIsRGBMode(src_buf->colorspace)) { | ||||||
|  |     const WebPRGBABuffer* const src = &src_buf->u.RGBA; | ||||||
|  |     const WebPRGBABuffer* const dst = &dst_buf->u.RGBA; | ||||||
|  |     WebPCopyPlane(src->rgba, src->stride, dst->rgba, dst->stride, | ||||||
|  |                   src_buf->width * kModeBpp[src_buf->colorspace], | ||||||
|  |                   src_buf->height); | ||||||
|  |   } else { | ||||||
|  |     const WebPYUVABuffer* const src = &src_buf->u.YUVA; | ||||||
|  |     const WebPYUVABuffer* const dst = &dst_buf->u.YUVA; | ||||||
|  |     WebPCopyPlane(src->y, src->y_stride, dst->y, dst->y_stride, | ||||||
|  |                   src_buf->width, src_buf->height); | ||||||
|  |     WebPCopyPlane(src->u, src->u_stride, dst->u, dst->u_stride, | ||||||
|  |                   (src_buf->width + 1) / 2, (src_buf->height + 1) / 2); | ||||||
|  |     WebPCopyPlane(src->v, src->v_stride, dst->v, dst->v_stride, | ||||||
|  |                   (src_buf->width + 1) / 2, (src_buf->height + 1) / 2); | ||||||
|  |     if (WebPIsAlphaMode(src_buf->colorspace)) { | ||||||
|  |       WebPCopyPlane(src->a, src->a_stride, dst->a, dst->a_stride, | ||||||
|  |                     src_buf->width, src_buf->height); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return VP8_STATUS_OK; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int WebPAvoidSlowMemory(const WebPDecBuffer* const output, | ||||||
|  |                         const WebPBitstreamFeatures* const features) { | ||||||
|  |   assert(output != NULL); | ||||||
|  |   return (output->is_external_memory >= 2) && | ||||||
|  |          WebPIsPremultipliedMode(output->colorspace) && | ||||||
|  |          (features != NULL && features->has_alpha); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|   | |||||||
| @@ -70,7 +70,9 @@ struct WebPIDecoder { | |||||||
|   VP8Io io_; |   VP8Io io_; | ||||||
|  |  | ||||||
|   MemBuffer mem_;          // input memory buffer. |   MemBuffer mem_;          // input memory buffer. | ||||||
|   WebPDecBuffer output_;   // output buffer (when no external one is supplied) |   WebPDecBuffer output_;   // output buffer (when no external one is supplied, | ||||||
|  |                            // or if the external one has slow-memory) | ||||||
|  |   WebPDecBuffer* final_output_;  // Slow-memory output to copy to eventually. | ||||||
|   size_t chunk_size_;      // Compressed VP8/VP8L size extracted from Header. |   size_t chunk_size_;      // Compressed VP8/VP8L size extracted from Header. | ||||||
|  |  | ||||||
|   int last_mb_y_;          // last row reached for intra-mode decoding |   int last_mb_y_;          // last row reached for intra-mode decoding | ||||||
| @@ -249,10 +251,16 @@ static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) { | |||||||
|  |  | ||||||
|   idec->state_ = STATE_DONE; |   idec->state_ = STATE_DONE; | ||||||
|   if (options != NULL && options->flip) { |   if (options != NULL && options->flip) { | ||||||
|     return WebPFlipBuffer(output); |     const VP8StatusCode status = WebPFlipBuffer(output); | ||||||
|   } else { |     if (status != VP8_STATUS_OK) return status; | ||||||
|     return VP8_STATUS_OK; |  | ||||||
|   } |   } | ||||||
|  |   if (idec->final_output_ != NULL) { | ||||||
|  |     WebPCopyDecBufferPixels(output, idec->final_output_);  // do the slow-copy | ||||||
|  |     WebPFreeDecBuffer(&idec->output_); | ||||||
|  |     *output = *idec->final_output_; | ||||||
|  |     idec->final_output_ = NULL; | ||||||
|  |   } | ||||||
|  |   return VP8_STATUS_OK; | ||||||
| } | } | ||||||
|  |  | ||||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||||
| @@ -575,9 +583,10 @@ static VP8StatusCode IDecode(WebPIDecoder* idec) { | |||||||
| } | } | ||||||
|  |  | ||||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||||
| // Public functions | // Internal constructor | ||||||
|  |  | ||||||
| WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) { | static WebPIDecoder* NewDecoder(WebPDecBuffer* const output_buffer, | ||||||
|  |                                 const WebPBitstreamFeatures* const features) { | ||||||
|   WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec)); |   WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec)); | ||||||
|   if (idec == NULL) { |   if (idec == NULL) { | ||||||
|     return NULL; |     return NULL; | ||||||
| @@ -593,25 +602,46 @@ WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) { | |||||||
|   VP8InitIo(&idec->io_); |   VP8InitIo(&idec->io_); | ||||||
|  |  | ||||||
|   WebPResetDecParams(&idec->params_); |   WebPResetDecParams(&idec->params_); | ||||||
|   idec->params_.output = (output_buffer != NULL) ? output_buffer |   if (output_buffer == NULL || WebPAvoidSlowMemory(output_buffer, features)) { | ||||||
|                                                  : &idec->output_; |     idec->params_.output = &idec->output_; | ||||||
|  |     idec->final_output_ = output_buffer; | ||||||
|  |     if (output_buffer != NULL) { | ||||||
|  |       idec->params_.output->colorspace = output_buffer->colorspace; | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|  |     idec->params_.output = output_buffer; | ||||||
|  |     idec->final_output_ = NULL; | ||||||
|  |   } | ||||||
|   WebPInitCustomIo(&idec->params_, &idec->io_);  // Plug the I/O functions. |   WebPInitCustomIo(&idec->params_, &idec->io_);  // Plug the I/O functions. | ||||||
|  |  | ||||||
|   return idec; |   return idec; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | // Public functions | ||||||
|  |  | ||||||
|  | WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) { | ||||||
|  |   return NewDecoder(output_buffer, NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
| WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size, | WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size, | ||||||
|                           WebPDecoderConfig* config) { |                           WebPDecoderConfig* config) { | ||||||
|   WebPIDecoder* idec; |   WebPIDecoder* idec; | ||||||
|  |   WebPBitstreamFeatures tmp_features; | ||||||
|  |   WebPBitstreamFeatures* const features = | ||||||
|  |       (config == NULL) ? &tmp_features : &config->input; | ||||||
|  |   memset(&tmp_features, 0, sizeof(tmp_features)); | ||||||
|  |  | ||||||
|   // Parse the bitstream's features, if requested: |   // Parse the bitstream's features, if requested: | ||||||
|   if (data != NULL && data_size > 0 && config != NULL) { |   if (data != NULL && data_size > 0) { | ||||||
|     if (WebPGetFeatures(data, data_size, &config->input) != VP8_STATUS_OK) { |     if (WebPGetFeatures(data, data_size, features) != VP8_STATUS_OK) { | ||||||
|       return NULL; |       return NULL; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Create an instance of the incremental decoder |   // Create an instance of the incremental decoder | ||||||
|   idec = WebPINewDecoder(config ? &config->output : NULL); |   idec = (config != NULL) ? NewDecoder(&config->output, features) | ||||||
|  |                           : NewDecoder(NULL, features); | ||||||
|   if (idec == NULL) { |   if (idec == NULL) { | ||||||
|     return NULL; |     return NULL; | ||||||
|   } |   } | ||||||
| @@ -645,11 +675,11 @@ void WebPIDelete(WebPIDecoder* idec) { | |||||||
|  |  | ||||||
| WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer, | WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer, | ||||||
|                           size_t output_buffer_size, int output_stride) { |                           size_t output_buffer_size, int output_stride) { | ||||||
|   const int is_external_memory = (output_buffer != NULL); |   const int is_external_memory = (output_buffer != NULL) ? 1 : 0; | ||||||
|   WebPIDecoder* idec; |   WebPIDecoder* idec; | ||||||
|  |  | ||||||
|   if (mode >= MODE_YUV) return NULL; |   if (mode >= MODE_YUV) return NULL; | ||||||
|   if (!is_external_memory) {    // Overwrite parameters to sane values. |   if (is_external_memory == 0) {    // Overwrite parameters to sane values. | ||||||
|     output_buffer_size = 0; |     output_buffer_size = 0; | ||||||
|     output_stride = 0; |     output_stride = 0; | ||||||
|   } else {  // A buffer was passed. Validate the other params. |   } else {  // A buffer was passed. Validate the other params. | ||||||
| @@ -671,11 +701,11 @@ WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride, | |||||||
|                            uint8_t* u, size_t u_size, int u_stride, |                            uint8_t* u, size_t u_size, int u_stride, | ||||||
|                            uint8_t* v, size_t v_size, int v_stride, |                            uint8_t* v, size_t v_size, int v_stride, | ||||||
|                            uint8_t* a, size_t a_size, int a_stride) { |                            uint8_t* a, size_t a_size, int a_stride) { | ||||||
|   const int is_external_memory = (luma != NULL); |   const int is_external_memory = (luma != NULL) ? 1 : 0; | ||||||
|   WebPIDecoder* idec; |   WebPIDecoder* idec; | ||||||
|   WEBP_CSP_MODE colorspace; |   WEBP_CSP_MODE colorspace; | ||||||
|  |  | ||||||
|   if (!is_external_memory) {    // Overwrite parameters to sane values. |   if (is_external_memory == 0) {    // Overwrite parameters to sane values. | ||||||
|     luma_size = u_size = v_size = a_size = 0; |     luma_size = u_size = v_size = a_size = 0; | ||||||
|     luma_stride = u_stride = v_stride = a_stride = 0; |     luma_stride = u_stride = v_stride = a_stride = 0; | ||||||
|     u = v = a = NULL; |     u = v = a = NULL; | ||||||
| @@ -783,6 +813,9 @@ static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) { | |||||||
|   if (idec->state_ <= STATE_VP8_PARTS0) { |   if (idec->state_ <= STATE_VP8_PARTS0) { | ||||||
|     return NULL; |     return NULL; | ||||||
|   } |   } | ||||||
|  |   if (idec->final_output_ != NULL) { | ||||||
|  |     return NULL;   // not yet slow-copied | ||||||
|  |   } | ||||||
|   return idec->params_.output; |   return idec->params_.output; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -792,7 +825,7 @@ const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec, | |||||||
|   const WebPDecBuffer* const src = GetOutputBuffer(idec); |   const WebPDecBuffer* const src = GetOutputBuffer(idec); | ||||||
|   if (left != NULL) *left = 0; |   if (left != NULL) *left = 0; | ||||||
|   if (top != NULL) *top = 0; |   if (top != NULL) *top = 0; | ||||||
|   if (src) { |   if (src != NULL) { | ||||||
|     if (width != NULL) *width = src->width; |     if (width != NULL) *width = src->width; | ||||||
|     if (height != NULL) *height = idec->params_.last_y; |     if (height != NULL) *height = idec->params_.last_y; | ||||||
|   } else { |   } else { | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ extern "C" { | |||||||
| // version numbers | // version numbers | ||||||
| #define DEC_MAJ_VERSION 0 | #define DEC_MAJ_VERSION 0 | ||||||
| #define DEC_MIN_VERSION 5 | #define DEC_MIN_VERSION 5 | ||||||
| #define DEC_REV_VERSION 0 | #define DEC_REV_VERSION 1 | ||||||
|  |  | ||||||
| // YUV-cache parameters. Cache is 32-bytes wide (= one cacheline). | // YUV-cache parameters. Cache is 32-bytes wide (= one cacheline). | ||||||
| // Constraints are: We need to store one 16x16 block of luma samples (y), | // Constraints are: We need to store one 16x16 block of luma samples (y), | ||||||
|   | |||||||
| @@ -514,6 +514,8 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size, | |||||||
|     WebPFreeDecBuffer(params->output); |     WebPFreeDecBuffer(params->output); | ||||||
|   } else { |   } else { | ||||||
|     if (params->options != NULL && params->options->flip) { |     if (params->options != NULL && params->options->flip) { | ||||||
|  |       // This restores the original stride values if options->flip was used | ||||||
|  |       // during the call to WebPAllocateDecBuffer above. | ||||||
|       status = WebPFlipBuffer(params->output); |       status = WebPFlipBuffer(params->output); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -758,9 +760,24 @@ VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size, | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   WebPResetDecParams(¶ms); |   WebPResetDecParams(¶ms); | ||||||
|   params.output = &config->output; |  | ||||||
|   params.options = &config->options; |   params.options = &config->options; | ||||||
|   status = DecodeInto(data, data_size, ¶ms); |   params.output = &config->output; | ||||||
|  |   if (WebPAvoidSlowMemory(params.output, &config->input)) { | ||||||
|  |     // decoding to slow memory: use a temporary in-mem buffer to decode into. | ||||||
|  |     WebPDecBuffer in_mem_buffer; | ||||||
|  |     WebPInitDecBuffer(&in_mem_buffer); | ||||||
|  |     in_mem_buffer.colorspace = config->output.colorspace; | ||||||
|  |     in_mem_buffer.width = config->input.width; | ||||||
|  |     in_mem_buffer.height = config->input.height; | ||||||
|  |     params.output = &in_mem_buffer; | ||||||
|  |     status = DecodeInto(data, data_size, ¶ms); | ||||||
|  |     if (status == VP8_STATUS_OK) {  // do the slow-copy | ||||||
|  |       status = WebPCopyDecBufferPixels(&in_mem_buffer, &config->output); | ||||||
|  |     } | ||||||
|  |     WebPFreeDecBuffer(&in_mem_buffer); | ||||||
|  |   } else { | ||||||
|  |     status = DecodeInto(data, data_size, ¶ms); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   return status; |   return status; | ||||||
| } | } | ||||||
| @@ -826,4 +843,3 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options, | |||||||
| } | } | ||||||
|  |  | ||||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||||
|  |  | ||||||
|   | |||||||
| @@ -45,11 +45,20 @@ struct WebPDecParams { | |||||||
|   OutputFunc emit;               // output RGB or YUV samples |   OutputFunc emit;               // output RGB or YUV samples | ||||||
|   OutputAlphaFunc emit_alpha;    // output alpha channel |   OutputAlphaFunc emit_alpha;    // output alpha channel | ||||||
|   OutputRowFunc emit_alpha_row;  // output one line of rescaled alpha values |   OutputRowFunc emit_alpha_row;  // output one line of rescaled alpha values | ||||||
|  |  | ||||||
|  |   WebPDecBuffer* final_output;   // In case the user supplied a slow-memory | ||||||
|  |                                  // output, we decode image in temporary buffer | ||||||
|  |                                  // (this::output) and copy it here. | ||||||
|  |   WebPDecBuffer tmp_buffer;      // this::output will point to this one in case | ||||||
|  |                                  // of slow memory. | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // Should be called first, before any use of the WebPDecParams object. | // Should be called first, before any use of the WebPDecParams object. | ||||||
| void WebPResetDecParams(WebPDecParams* const params); | void WebPResetDecParams(WebPDecParams* const params); | ||||||
|  |  | ||||||
|  | // Delete all memory (after an error occurred, for instance) | ||||||
|  | void WebPFreeDecParams(WebPDecParams* const params); | ||||||
|  |  | ||||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||||
| // Header parsing helpers | // Header parsing helpers | ||||||
|  |  | ||||||
| @@ -107,13 +116,23 @@ VP8StatusCode WebPAllocateDecBuffer(int width, int height, | |||||||
| VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer); | VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer); | ||||||
|  |  | ||||||
| // Copy 'src' into 'dst' buffer, making sure 'dst' is not marked as owner of the | // Copy 'src' into 'dst' buffer, making sure 'dst' is not marked as owner of the | ||||||
| // memory (still held by 'src'). | // memory (still held by 'src'). No pixels are copied. | ||||||
| void WebPCopyDecBuffer(const WebPDecBuffer* const src, | void WebPCopyDecBuffer(const WebPDecBuffer* const src, | ||||||
|                        WebPDecBuffer* const dst); |                        WebPDecBuffer* const dst); | ||||||
|  |  | ||||||
| // Copy and transfer ownership from src to dst (beware of parameter order!) | // Copy and transfer ownership from src to dst (beware of parameter order!) | ||||||
| void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst); | void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst); | ||||||
|  |  | ||||||
|  | // Copy pixels from 'src' into a *preallocated* 'dst' buffer. Returns | ||||||
|  | // VP8_STATUS_INVALID_PARAM if the 'dst' is not set up correctly for the copy. | ||||||
|  | VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src, | ||||||
|  |                                       WebPDecBuffer* const dst); | ||||||
|  |  | ||||||
|  | // Returns true if decoding will be slow with the current configuration | ||||||
|  | // and bitstream features. | ||||||
|  | int WebPAvoidSlowMemory(const WebPDecBuffer* const output, | ||||||
|  |                         const WebPBitstreamFeatures* const features); | ||||||
|  |  | ||||||
| //------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||||
|  |  | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
|   | |||||||
| @@ -197,7 +197,10 @@ struct WebPYUVABuffer {              // view as YUVA | |||||||
| struct WebPDecBuffer { | struct WebPDecBuffer { | ||||||
|   WEBP_CSP_MODE colorspace;  // Colorspace. |   WEBP_CSP_MODE colorspace;  // Colorspace. | ||||||
|   int width, height;         // Dimensions. |   int width, height;         // Dimensions. | ||||||
|   int is_external_memory;    // If true, 'internal_memory' pointer is not used. |   int is_external_memory;    // If non-zero, 'internal_memory' pointer is not | ||||||
|  |                              // used. If value is '2' or more, the external | ||||||
|  |                              // memory is considered 'slow' and multiple | ||||||
|  |                              // read/write will be avoided. | ||||||
|   union { |   union { | ||||||
|     WebPRGBABuffer RGBA; |     WebPRGBABuffer RGBA; | ||||||
|     WebPYUVABuffer YUVA; |     WebPYUVABuffer YUVA; | ||||||
| @@ -205,7 +208,7 @@ struct WebPDecBuffer { | |||||||
|   uint32_t       pad[4];     // padding for later use |   uint32_t       pad[4];     // padding for later use | ||||||
|  |  | ||||||
|   uint8_t* private_memory;   // Internally allocated memory (only when |   uint8_t* private_memory;   // Internally allocated memory (only when | ||||||
|                              // is_external_memory is false). Should not be used |                              // is_external_memory is 0). Should not be used | ||||||
|                              // externally, but accessed via the buffer union. |                              // externally, but accessed via the buffer union. | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -269,7 +272,7 @@ typedef enum VP8StatusCode { | |||||||
| // that of the returned WebPIDecoder object. | // that of the returned WebPIDecoder object. | ||||||
| // The supplied 'output_buffer' content MUST NOT be changed between calls to | // The supplied 'output_buffer' content MUST NOT be changed between calls to | ||||||
| // WebPIAppend() or WebPIUpdate() unless 'output_buffer.is_external_memory' is | // WebPIAppend() or WebPIUpdate() unless 'output_buffer.is_external_memory' is | ||||||
| // set to 1. In such a case, it is allowed to modify the pointers, size and | // not set to 0. In such a case, it is allowed to modify the pointers, size and | ||||||
| // stride of output_buffer.u.RGBA or output_buffer.u.YUVA, provided they remain | // stride of output_buffer.u.RGBA or output_buffer.u.YUVA, provided they remain | ||||||
| // within valid bounds. | // within valid bounds. | ||||||
| // All other fields of WebPDecBuffer MUST remain constant between calls. | // All other fields of WebPDecBuffer MUST remain constant between calls. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user