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, | ||||
|   BMP, | ||||
|   TIFF, | ||||
|   YUV, | ||||
|   ALPHA_PLANE_ONLY  // this is for experimenting only | ||||
|   RAW_YUV, | ||||
|   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; | ||||
|  | ||||
| #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; | ||||
|   uint8_t* const rgb = buffer->u.RGBA.rgba; | ||||
|   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, | ||||
|                                  MAKE_REFGUID(GUID_ContainerFormatPng), | ||||
| @@ -193,7 +198,7 @@ static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) { | ||||
|   const uint32_t height = buffer->height; | ||||
|   uint8_t* const rgb = buffer->u.RGBA.rgba; | ||||
|   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_infop info; | ||||
|   png_uint_32 y; | ||||
| @@ -259,6 +264,24 @@ static int WritePPM(FILE* fout, const WebPDecBuffer* const buffer, int alpha) { | ||||
|   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) { | ||||
|   dst[0] = (value >> 0) & 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 | ||||
| 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 height = buffer->height; | ||||
|   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) | ||||
|  | ||||
| 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 height = buffer->height; | ||||
|   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 | ||||
| // (http://www.fourcc.org/yuv.php#IMC4). This is a very convenient format for | ||||
| // 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, | ||||
|                          OutputFileFormat format) { | ||||
|   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; | ||||
|   int ok = 1; | ||||
|   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_height = (height + 1) / 2; | ||||
|   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 | ||||
|     ok &= WritePNG(out_file, use_stdout, buffer); | ||||
| #else | ||||
| @@ -495,14 +520,17 @@ static int SaveOutput(const WebPDecBuffer* const buffer, | ||||
| #endif | ||||
|   } else if (format == PAM) { | ||||
|     ok &= WritePPM(fout, buffer, 1); | ||||
|   } else if (format == PPM) { | ||||
|   } else if (format == PPM || format == RGB || format == BGR) { | ||||
|     ok &= WritePPM(fout, buffer, 0); | ||||
|   } else if (format == RGBA_4444 || format == RGB_565 || format == rgbA_4444) { | ||||
|     ok &= Write16bAsPGM(fout, buffer); | ||||
|   } else if (format == BMP) { | ||||
|     ok &= WriteBMP(fout, buffer); | ||||
|   } else if (format == TIFF) { | ||||
|     ok &= WriteTIFF(fout, buffer); | ||||
|   } else if (format == PGM || format == YUV) { | ||||
|     ok &= WritePGMOrYUV(fout, buffer, format); | ||||
|   } else if (format == PGM || format == RAW_YUV || | ||||
|              format == YUV || format == YUVA) { | ||||
|     ok &= WritePGMOrYUV(fout, buffer, format == RAW_YUV ? RAW_YUV : PGM); | ||||
|   } else if (format == ALPHA_PLANE_ONLY) { | ||||
|     ok &= WriteAlphaPlane(fout, buffer); | ||||
|   } | ||||
| @@ -569,6 +597,70 @@ static const char* const kFormatType[] = { | ||||
|   "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 ok = 0; | ||||
|   const char *in_file = NULL; | ||||
| @@ -578,6 +670,10 @@ int main(int argc, const char *argv[]) { | ||||
|   WebPDecBuffer* const output_buffer = &config.output; | ||||
|   WebPBitstreamFeatures* const bitstream = &config.input; | ||||
|   OutputFileFormat format = PNG; | ||||
|   uint8_t* external_buffer = NULL; | ||||
|   int use_external_memory = 0; | ||||
|   const uint8_t* data = NULL; | ||||
|  | ||||
|   int incremental = 0; | ||||
|   int c; | ||||
|  | ||||
| @@ -617,7 +713,32 @@ int main(int argc, const char *argv[]) { | ||||
|     } else if (!strcmp(argv[c], "-pgm")) { | ||||
|       format = PGM; | ||||
|     } 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")) { | ||||
|       config.options.use_threads = 1; | ||||
|     } else if (!strcmp(argv[c], "-alpha_dither")) { | ||||
| @@ -676,7 +797,6 @@ int main(int argc, const char *argv[]) { | ||||
|   { | ||||
|     VP8StatusCode status = VP8_STATUS_OK; | ||||
|     size_t data_size = 0; | ||||
|     const uint8_t* data = NULL; | ||||
|     if (!ExUtilLoadWebP(in_file, &data, &data_size, bitstream)) { | ||||
|       return -1; | ||||
|     } | ||||
| @@ -703,15 +823,33 @@ int main(int argc, const char *argv[]) { | ||||
|             bitstream->has_alpha ? MODE_rgbA : MODE_RGB; | ||||
|         break; | ||||
|       case PGM: | ||||
|       case YUV: | ||||
|       case RAW_YUV: | ||||
|         output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV; | ||||
|         break; | ||||
|       case ALPHA_PLANE_ONLY: | ||||
|         output_buffer->colorspace = MODE_YUVA; | ||||
|         break; | ||||
|       default: | ||||
|         free((void*)data); | ||||
|         return -1; | ||||
|       // forced modes: | ||||
|       case RGB: output_buffer->colorspace = MODE_RGB; break; | ||||
|       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) { | ||||
| @@ -720,7 +858,6 @@ int main(int argc, const char *argv[]) { | ||||
|       status = ExUtilDecodeWebP(data, data_size, verbose, &config); | ||||
|     } | ||||
|  | ||||
|     free((void*)data); | ||||
|     ok = (status == VP8_STATUS_OK); | ||||
|     if (!ok) { | ||||
|       ExUtilPrintWebPError(in_file, status); | ||||
| @@ -750,6 +887,8 @@ int main(int argc, const char *argv[]) { | ||||
|   } | ||||
|  Exit: | ||||
|   WebPFreeDecBuffer(output_buffer); | ||||
|   free((void*)external_buffer); | ||||
|   free((void*)data); | ||||
|   return ok ? 0 : -1; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -92,7 +92,7 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) { | ||||
|     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; | ||||
|     int uv_stride = 0, a_stride = 0; | ||||
|     uint64_t uv_size = 0, a_size = 0, total_size; | ||||
| @@ -227,7 +227,7 @@ int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) { | ||||
|  | ||||
| void WebPFreeDecBuffer(WebPDecBuffer* buffer) { | ||||
|   if (buffer != NULL) { | ||||
|     if (!buffer->is_external_memory) { | ||||
|     if (buffer->is_external_memory <= 0) { | ||||
|       WebPSafeFree(buffer->private_memory); | ||||
|     } | ||||
|     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_; | ||||
|  | ||||
|   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. | ||||
|  | ||||
|   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; | ||||
|   if (options != NULL && options->flip) { | ||||
|     return WebPFlipBuffer(output); | ||||
|   } else { | ||||
|     return VP8_STATUS_OK; | ||||
|     const VP8StatusCode status = WebPFlipBuffer(output); | ||||
|     if (status != VP8_STATUS_OK) return status; | ||||
|   } | ||||
|   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)); | ||||
|   if (idec == NULL) { | ||||
|     return NULL; | ||||
| @@ -593,25 +602,46 @@ WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) { | ||||
|   VP8InitIo(&idec->io_); | ||||
|  | ||||
|   WebPResetDecParams(&idec->params_); | ||||
|   idec->params_.output = (output_buffer != NULL) ? output_buffer | ||||
|                                                  : &idec->output_; | ||||
|   if (output_buffer == NULL || WebPAvoidSlowMemory(output_buffer, features)) { | ||||
|     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. | ||||
|  | ||||
|   return idec; | ||||
| } | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // Public functions | ||||
|  | ||||
| WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) { | ||||
|   return NewDecoder(output_buffer, NULL); | ||||
| } | ||||
|  | ||||
| WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size, | ||||
|                           WebPDecoderConfig* config) { | ||||
|   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: | ||||
|   if (data != NULL && data_size > 0 && config != NULL) { | ||||
|     if (WebPGetFeatures(data, data_size, &config->input) != VP8_STATUS_OK) { | ||||
|   if (data != NULL && data_size > 0) { | ||||
|     if (WebPGetFeatures(data, data_size, features) != VP8_STATUS_OK) { | ||||
|       return NULL; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // 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) { | ||||
|     return NULL; | ||||
|   } | ||||
| @@ -645,11 +675,11 @@ void WebPIDelete(WebPIDecoder* idec) { | ||||
|  | ||||
| WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer, | ||||
|                           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; | ||||
|  | ||||
|   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_stride = 0; | ||||
|   } 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* v, size_t v_size, int v_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; | ||||
|   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_stride = u_stride = v_stride = a_stride = 0; | ||||
|     u = v = a = NULL; | ||||
| @@ -783,6 +813,9 @@ static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) { | ||||
|   if (idec->state_ <= STATE_VP8_PARTS0) { | ||||
|     return NULL; | ||||
|   } | ||||
|   if (idec->final_output_ != NULL) { | ||||
|     return NULL;   // not yet slow-copied | ||||
|   } | ||||
|   return idec->params_.output; | ||||
| } | ||||
|  | ||||
| @@ -792,7 +825,7 @@ const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec, | ||||
|   const WebPDecBuffer* const src = GetOutputBuffer(idec); | ||||
|   if (left != NULL) *left = 0; | ||||
|   if (top != NULL) *top = 0; | ||||
|   if (src) { | ||||
|   if (src != NULL) { | ||||
|     if (width != NULL) *width = src->width; | ||||
|     if (height != NULL) *height = idec->params_.last_y; | ||||
|   } else { | ||||
|   | ||||
| @@ -32,7 +32,7 @@ extern "C" { | ||||
| // version numbers | ||||
| #define DEC_MAJ_VERSION 0 | ||||
| #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). | ||||
| // 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); | ||||
|   } else { | ||||
|     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); | ||||
|     } | ||||
|   } | ||||
| @@ -758,9 +760,24 @@ VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size, | ||||
|   } | ||||
|  | ||||
|   WebPResetDecParams(¶ms); | ||||
|   params.output = &config->output; | ||||
|   params.options = &config->options; | ||||
|   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; | ||||
| } | ||||
| @@ -826,4 +843,3 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options, | ||||
| } | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
|   | ||||
| @@ -45,11 +45,20 @@ struct WebPDecParams { | ||||
|   OutputFunc emit;               // output RGB or YUV samples | ||||
|   OutputAlphaFunc emit_alpha;    // output alpha channel | ||||
|   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. | ||||
| void WebPResetDecParams(WebPDecParams* const params); | ||||
|  | ||||
| // Delete all memory (after an error occurred, for instance) | ||||
| void WebPFreeDecParams(WebPDecParams* const params); | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // Header parsing helpers | ||||
|  | ||||
| @@ -107,13 +116,23 @@ VP8StatusCode WebPAllocateDecBuffer(int width, int height, | ||||
| VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer); | ||||
|  | ||||
| // 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, | ||||
|                        WebPDecBuffer* const dst); | ||||
|  | ||||
| // Copy and transfer ownership from src to dst (beware of parameter order!) | ||||
| 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 | ||||
|   | ||||
| @@ -197,7 +197,10 @@ struct WebPYUVABuffer {              // view as YUVA | ||||
| struct WebPDecBuffer { | ||||
|   WEBP_CSP_MODE colorspace;  // Colorspace. | ||||
|   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 { | ||||
|     WebPRGBABuffer RGBA; | ||||
|     WebPYUVABuffer YUVA; | ||||
| @@ -205,7 +208,7 @@ struct WebPDecBuffer { | ||||
|   uint32_t       pad[4];     // padding for later use | ||||
|  | ||||
|   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. | ||||
| }; | ||||
|  | ||||
| @@ -269,7 +272,7 @@ typedef enum VP8StatusCode { | ||||
| // that of the returned WebPIDecoder object. | ||||
| // The supplied 'output_buffer' content MUST NOT be changed between calls to | ||||
| // 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 | ||||
| // within valid bounds. | ||||
| // All other fields of WebPDecBuffer MUST remain constant between calls. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user