mirror of
				https://github.com/webmproject/libwebp.git
				synced 2025-10-31 02:15:42 +01:00 
			
		
		
		
	reduce number of copies and mallocs in alpha plane enc/dec
Change-Id: Ice48d2dd403c18870567ee6e1b210b1eb3d5b44f
This commit is contained in:
		| @@ -23,29 +23,6 @@ extern "C" { | |||||||
| #define MAX_SYMBOLS      255 | #define MAX_SYMBOLS      255 | ||||||
| #define ALPHA_HEADER_LEN 2 | #define ALPHA_HEADER_LEN 2 | ||||||
|  |  | ||||||
| // ----------------------------------------------------------------------------- |  | ||||||
| // Alpha Encode. |  | ||||||
|  |  | ||||||
| static int EncodeIdent(const uint8_t* data, int width, int height, |  | ||||||
|                        uint8_t** output, size_t* output_size) { |  | ||||||
|   const size_t data_size = height * width; |  | ||||||
|   uint8_t* alpha = NULL; |  | ||||||
|   assert((output != NULL) && (output_size != NULL)); |  | ||||||
|  |  | ||||||
|   if (data == NULL) { |  | ||||||
|     return 0; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   alpha = (uint8_t*)malloc(data_size); |  | ||||||
|   if (alpha == NULL) { |  | ||||||
|     return 0; |  | ||||||
|   } |  | ||||||
|   memcpy(alpha, data, data_size); |  | ||||||
|   *output_size = data_size; |  | ||||||
|   *output = alpha; |  | ||||||
|   return 1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ----------------------------------------------------------------------------- | // ----------------------------------------------------------------------------- | ||||||
| // Zlib-like encoding using TCoder | // Zlib-like encoding using TCoder | ||||||
|  |  | ||||||
| @@ -80,7 +57,7 @@ static size_t GetLongestMatch(const uint8_t* const data, | |||||||
| } | } | ||||||
|  |  | ||||||
| static int EncodeZlibTCoder(const uint8_t* data, int width, int height, | static int EncodeZlibTCoder(const uint8_t* data, int width, int height, | ||||||
|                             uint8_t** output, size_t* output_size) { |                             VP8BitWriter* const bw) { | ||||||
|   int ok = 0; |   int ok = 0; | ||||||
|   const size_t data_size = width * height; |   const size_t data_size = width * height; | ||||||
|   const size_t MAX_DIST = 3 * width; |   const size_t MAX_DIST = 3 * width; | ||||||
| @@ -201,26 +178,19 @@ static int EncodeZlibTCoder(const uint8_t* data, int width, int height, | |||||||
|   // Final bitstream assembly. |   // Final bitstream assembly. | ||||||
|   { |   { | ||||||
|     int n; |     int n; | ||||||
|     VP8BitWriter bw; |  | ||||||
|     VP8BitWriterInit(&bw, 0); |  | ||||||
|     TCoderInit(coder); |     TCoderInit(coder); | ||||||
|     TCoderInit(coderd); |     TCoderInit(coderd); | ||||||
|     TCoderInit(coderl); |     TCoderInit(coderl); | ||||||
|     for (n = 0; n < num_tokens; ++n) { |     for (n = 0; n < num_tokens; ++n) { | ||||||
|       const Token* const t = &msg[n]; |       const Token* const t = &msg[n]; | ||||||
|       const int is_literal = (t->dist == 0); |       const int is_literal = (t->dist == 0); | ||||||
|       TCoderEncode(coderd, t->dist, &bw); |       TCoderEncode(coderd, t->dist, bw); | ||||||
|       if (is_literal) {  // literal |       if (is_literal) {  // literal | ||||||
|         TCoderEncode(coder, t->literal, &bw); |         TCoderEncode(coder, t->literal, bw); | ||||||
|       } else { |       } else { | ||||||
|         TCoderEncode(coderl, t->len - MIN_LEN, &bw); |         TCoderEncode(coderl, t->len - MIN_LEN, bw); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // clean up |  | ||||||
|     VP8BitWriterFinish(&bw); |  | ||||||
|     *output = VP8BitWriterBuf(&bw); |  | ||||||
|     *output_size = VP8BitWriterSize(&bw); |  | ||||||
|     ok = 1; |     ok = 1; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -229,32 +199,45 @@ static int EncodeZlibTCoder(const uint8_t* data, int width, int height, | |||||||
|   if (coderl) TCoderDelete(coderl); |   if (coderl) TCoderDelete(coderl); | ||||||
|   if (coderd) TCoderDelete(coderd); |   if (coderd) TCoderDelete(coderd); | ||||||
|   free(msg); |   free(msg); | ||||||
|  |   return ok && !bw->error_; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int EncodeAlphaInternal(const uint8_t* data, int width, int height, | ||||||
|  |                                int method, VP8BitWriter* const bw) { | ||||||
|  |   int ok = 0; | ||||||
|  |   if (method == 0) { | ||||||
|  |     ok = VP8BitWriterAppend(bw, data, width * height); | ||||||
|  |     ok = ok && !bw->error_; | ||||||
|  |   } else if (method == 1) { | ||||||
|  |     ok = EncodeZlibTCoder(data, width, height, bw); | ||||||
|  |     VP8BitWriterFinish(bw); | ||||||
|  |   } | ||||||
|   return ok; |   return ok; | ||||||
| } | } | ||||||
|  |  | ||||||
| // ----------------------------------------------------------------------------- | // ----------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | // TODO(skal): move to dsp/ ? | ||||||
|  | static void CopyPlane(const uint8_t* src, int src_stride, | ||||||
|  |                       uint8_t* dst, int dst_stride, int width, int height) { | ||||||
|  |   while (height-- > 0) { | ||||||
|  |     memcpy(dst, src, width); | ||||||
|  |     src += src_stride; | ||||||
|  |     dst += dst_stride; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| int EncodeAlpha(const uint8_t* data, int width, int height, int stride, | int EncodeAlpha(const uint8_t* data, int width, int height, int stride, | ||||||
|                 int quality, int method, |                 int quality, int method, | ||||||
|                 uint8_t** output, size_t* output_size) { |                 uint8_t** output, size_t* output_size) { | ||||||
|   const int kMaxImageDim = (1 << 14) - 1; |  | ||||||
|   uint8_t* compressed_alpha = NULL; |  | ||||||
|   uint8_t* quant_alpha = NULL; |   uint8_t* quant_alpha = NULL; | ||||||
|   uint8_t* out = NULL; |  | ||||||
|   size_t compressed_size = 0; |  | ||||||
|   const size_t data_size = height * width; |   const size_t data_size = height * width; | ||||||
|   float mse = 0.0; |   int ok = 1; | ||||||
|   int ok = 0; |  | ||||||
|   int h; |  | ||||||
|  |  | ||||||
|   if ((data == NULL) || (output == NULL) || (output_size == NULL)) { |   // quick sanity checks | ||||||
|     return 0; |   assert(data != NULL && output != NULL && output_size != NULL); | ||||||
|   } |   assert(width > 0 && height > 0); | ||||||
|  |   assert(stride >= width); | ||||||
|   if (width <= 0 || width > kMaxImageDim || |  | ||||||
|       height <= 0 || height > kMaxImageDim || stride < width) { |  | ||||||
|     return 0; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (quality < 0 || quality > 100) { |   if (quality < 0 || quality > 100) { | ||||||
|     return 0; |     return 0; | ||||||
| @@ -269,74 +252,45 @@ int EncodeAlpha(const uint8_t* data, int width, int height, int stride, | |||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Extract the alpha data (WidthXHeight) from raw_data (StrideXHeight). |   // Extract alpha data (width x height) from raw_data (stride x height). | ||||||
|   for (h = 0; h < height; ++h) { |   CopyPlane(data, stride, quant_alpha, width, width, height); | ||||||
|     memcpy(quant_alpha + h * width, data + h * stride, width); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (quality < 100) {  // No Quantization required for 'quality = 100'. |   if (quality < 100) {  // No Quantization required for 'quality = 100'. | ||||||
|     // 16 Alpha levels gives quite a low MSE w.r.t Original Alpha plane hence |     // 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence | ||||||
|     // mapped to moderate quality 70. Hence Quality:[0, 70] -> Levels:[2, 16] |     // mapped to moderate quality 70. Hence Quality:[0, 70] -> Levels:[2, 16] | ||||||
|     // and Quality:]70, 100] -> Levels:]16, 256]. |     // and Quality:]70, 100] -> Levels:]16, 256]. | ||||||
|     const int alpha_levels = (quality <= 70) ? |     const int alpha_levels = (quality <= 70) ? (2 + quality / 5) | ||||||
|                              2 + quality / 5 : |                                              : (16 + (quality - 70) * 8); | ||||||
|                              16 + (quality - 70) * 8; |     ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, NULL); | ||||||
|  |   } | ||||||
|  |  | ||||||
|     ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, &mse); |   if (ok) { | ||||||
|  |     uint8_t header[ALPHA_HEADER_LEN]; | ||||||
|  |     VP8BitWriter bw; | ||||||
|  |     VP8BitWriterInit(&bw, | ||||||
|  |         (method == 0) ? (2 + data_size) | ||||||
|  |                       : (data_size >> 5)  /* rough estimate of final size */); | ||||||
|  |     header[0] = method & 0xff;    // Compression Method. | ||||||
|  |     header[1] = 0;                // reserved byte for later use | ||||||
|  |     VP8BitWriterAppend(&bw, header, sizeof(header)); | ||||||
|  |  | ||||||
|  |     ok = EncodeAlphaInternal(quant_alpha, width, height, method, &bw); | ||||||
|     if (!ok) { |     if (!ok) { | ||||||
|       free(quant_alpha); |       VP8BitWriterWipeOut(&bw); | ||||||
|       return 0; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (method == 0) { |  | ||||||
|     ok = EncodeIdent(quant_alpha, width, height, |  | ||||||
|                      &compressed_alpha, &compressed_size); |  | ||||||
|   } else if (method == 1) { |  | ||||||
|     ok = EncodeZlibTCoder(quant_alpha, width, height, |  | ||||||
|                           &compressed_alpha, &compressed_size); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   free(quant_alpha); |  | ||||||
|   if (!ok) { |  | ||||||
|     return 0; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   out = (uint8_t*)malloc(compressed_size + ALPHA_HEADER_LEN); |  | ||||||
|   if (out == NULL) { |  | ||||||
|     free(compressed_alpha); |  | ||||||
|     return 0; |  | ||||||
|     } else { |     } else { | ||||||
|     *output = out; |       *output = VP8BitWriterBuf(&bw); | ||||||
|  |       *output_size = VP8BitWriterSize(&bw); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Alpha bit-stream Header: |   free(quant_alpha); | ||||||
|   // Byte0: Compression Method. |   return ok; | ||||||
|   // Byte1: Reserved for later extension. |  | ||||||
|   out[0] = method & 0xff; |  | ||||||
|   out[1] = 0;  // Reserved Byte. |  | ||||||
|   out += ALPHA_HEADER_LEN; |  | ||||||
|   memcpy(out, compressed_alpha, compressed_size); |  | ||||||
|   free(compressed_alpha); |  | ||||||
|   out += compressed_size; |  | ||||||
|  |  | ||||||
|   *output_size = out - *output; |  | ||||||
|  |  | ||||||
|   return 1; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // ----------------------------------------------------------------------------- | // ----------------------------------------------------------------------------- | ||||||
| // Alpha Decode. | // Alpha Decode. | ||||||
|  |  | ||||||
| static int DecodeIdent(const uint8_t* data, size_t data_size, | static int DecompressZlibTCoder(VP8BitReader* const br, int width, | ||||||
|                        uint8_t* output) { |  | ||||||
|   assert((data != NULL) && (output != NULL)); |  | ||||||
|   memcpy(output, data, data_size); |  | ||||||
|   return 1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int DecompressZlibTCoder(const uint8_t* data, size_t data_size, |  | ||||||
|                                 int width, int height, |  | ||||||
|                                 uint8_t* output, size_t output_size) { |                                 uint8_t* output, size_t output_size) { | ||||||
|   int ok = 1; |   int ok = 1; | ||||||
|   const size_t MAX_DIST = 3 * width; |   const size_t MAX_DIST = 3 * width; | ||||||
| @@ -348,20 +302,17 @@ static int DecompressZlibTCoder(const uint8_t* data, size_t data_size, | |||||||
|   if (coder == NULL || coderd == NULL || coderl == NULL) { |   if (coder == NULL || coderd == NULL || coderl == NULL) { | ||||||
|     goto End; |     goto End; | ||||||
|   } |   } | ||||||
|   (void)height;     // unused parameter |  | ||||||
|  |  | ||||||
|   { |   { | ||||||
|     size_t pos = 0; |     size_t pos = 0; | ||||||
|     VP8BitReader br; |     assert(br != NULL); | ||||||
|     VP8InitBitReader(&br, data, data + data_size); |     while (pos < output_size && !br->eof_) { | ||||||
|     while (pos < output_size && !br.eof_) { |       const size_t dist = TCoderDecode(coderd, br); | ||||||
|       const size_t dist = TCoderDecode(coderd, &br); |  | ||||||
|       if (dist == 0) { |       if (dist == 0) { | ||||||
|         const int literal = TCoderDecode(coder, &br); |         output[pos] = TCoderDecode(coder, br); | ||||||
|         output[pos] = literal; |  | ||||||
|         ++pos; |         ++pos; | ||||||
|       } else { |       } else { | ||||||
|         const size_t len = MIN_LEN + TCoderDecode(coderl, &br); |         const size_t len = MIN_LEN + TCoderDecode(coderl, br); | ||||||
|         size_t k; |         size_t k; | ||||||
|         if (pos + len > output_size || pos < dist) goto End; |         if (pos + len > output_size || pos < dist) goto End; | ||||||
|         for (k = 0; k < len; ++k) { |         for (k = 0; k < len; ++k) { | ||||||
| @@ -370,7 +321,7 @@ static int DecompressZlibTCoder(const uint8_t* data, size_t data_size, | |||||||
|         pos += len; |         pos += len; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     ok = !br.eof_; |     ok = !br->eof_; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  End: |  End: | ||||||
| @@ -386,51 +337,42 @@ int DecodeAlpha(const uint8_t* data, size_t data_size, | |||||||
|                 int width, int height, int stride, |                 int width, int height, int stride, | ||||||
|                 uint8_t* output) { |                 uint8_t* output) { | ||||||
|   uint8_t* decoded_data = NULL; |   uint8_t* decoded_data = NULL; | ||||||
|  |   const size_t decoded_size = height * width; | ||||||
|   int ok = 0; |   int ok = 0; | ||||||
|   int method; |   int method; | ||||||
|   size_t decoded_size = height * width; |  | ||||||
|  |  | ||||||
|   if (data == NULL || output == NULL) { |   assert(width > 0 && height > 0 && stride >= width); | ||||||
|     return 0; |   assert(data != NULL && output != NULL); | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (data_size <= ALPHA_HEADER_LEN) { |   if (data_size <= ALPHA_HEADER_LEN) { | ||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (width <= 0 || height <= 0 || stride < width) { |  | ||||||
|     return 0; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   method = data[0]; |   method = data[0]; | ||||||
|   if (method < 0 || method > 1) { |   ok = (data[1] == 0); | ||||||
|  |   if (method < 0 || method > 1 || !ok) { | ||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   if (method == 0) { | ||||||
|  |     ok = (data_size >= decoded_size); | ||||||
|  |     decoded_data = (uint8_t*)data + ALPHA_HEADER_LEN; | ||||||
|  |   } else if (method == 1) { | ||||||
|  |     VP8BitReader br; | ||||||
|     decoded_data = (uint8_t*)malloc(decoded_size); |     decoded_data = (uint8_t*)malloc(decoded_size); | ||||||
|     if (decoded_data == NULL) { |     if (decoded_data == NULL) { | ||||||
|       return 0; |       return 0; | ||||||
|     } |     } | ||||||
|  |     VP8InitBitReader(&br, data + ALPHA_HEADER_LEN, data + data_size); | ||||||
|   data_size -= ALPHA_HEADER_LEN; |     ok = DecompressZlibTCoder(&br, width, decoded_data, decoded_size); | ||||||
|   data += ALPHA_HEADER_LEN; |  | ||||||
|  |  | ||||||
|   if (method == 0) { |  | ||||||
|     ok = DecodeIdent(data, data_size, decoded_data); |  | ||||||
|   } else if (method == 1) { |  | ||||||
|     ok = DecompressZlibTCoder(data, data_size, width, height, |  | ||||||
|                               decoded_data, decoded_size); |  | ||||||
|   } |   } | ||||||
|  |   // Construct raw_data (height x stride) from alpha data (height x width). | ||||||
|   if (ok) { |   if (ok) { | ||||||
|     // Construct raw_data (HeightXStride) from the alpha data (HeightXWidth). |     CopyPlane(decoded_data, width, output, stride, width, height); | ||||||
|     int h; |  | ||||||
|     for (h = 0; h < height; ++h) { |  | ||||||
|       memcpy(output + h * stride, decoded_data + h * width, width); |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
|  |   if (method == 1) { | ||||||
|     free(decoded_data); |     free(decoded_data); | ||||||
|  |   } | ||||||
|   return ok; |   return ok; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user