mirror of
				https://github.com/webmproject/libwebp.git
				synced 2025-10-31 10:25:46 +01:00 
			
		
		
		
	Introduce a 'fast' alpha mode
.. where only 2 filtering modes are potentially tried, instead of all of them. This is fast than the exhaustive 'best' mode, and not much worse. Options for cwebp are: -alpha_filter none -alpha_filter fast (<- default) -alpha_filter best (<- slow) Change-Id: I8cb90ee11b8f981811e013ea4ad5bf72ba3ea7d4
This commit is contained in:
		
							
								
								
									
										2
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								README
									
									
									
									
									
								
							| @@ -149,6 +149,8 @@ options: | ||||
|   -map <int> ............. print map of extra info. | ||||
|   -d <file.pgm> .......... dump the compressed output (PGM file). | ||||
|   -alpha_method <int> .... Transparency-compression method (0..1) | ||||
|   -alpha_filter <string> . predictive filtering for alpha plane. | ||||
|                            One of: none, fast (default) or best. | ||||
|   -noalpha ............... discard any transparency information. | ||||
|  | ||||
|   -short ................. condense printed message | ||||
|   | ||||
| @@ -700,7 +700,8 @@ static void HelpLong(void) { | ||||
|   printf("  -map <int> ............. print map of extra info.\n"); | ||||
|   printf("  -d <file.pgm> .......... dump the compressed output (PGM file).\n"); | ||||
|   printf("  -alpha_method <int> .... Transparency-compression method (0..1)\n"); | ||||
|   printf("  -alpha_filter <int> .... predictive filtering for Alpha (0..5)\n"); | ||||
|   printf("  -alpha_filter <string> . predictive filtering for alpha plane.\n"); | ||||
|   printf("                           One of: none, fast (default) or best.\n"); | ||||
|   printf("  -noalpha ............... discard any transparency information.\n"); | ||||
|  | ||||
|   printf("\n"); | ||||
| @@ -795,7 +796,17 @@ int main(int argc, const char *argv[]) { | ||||
|     } else if (!strcmp(argv[c], "-alpha_method") && c < argc - 1) { | ||||
|       config.alpha_compression = strtol(argv[++c], NULL, 0); | ||||
|     } else if (!strcmp(argv[c], "-alpha_filter") && c < argc - 1) { | ||||
|       config.alpha_filtering = strtol(argv[++c], NULL, 0); | ||||
|       ++c; | ||||
|       if (!strcmp(argv[c], "none")) { | ||||
|         config.alpha_filtering = 0; | ||||
|       } else if (!strcmp(argv[c], "fast")) { | ||||
|         config.alpha_filtering = 1; | ||||
|       } else if (!strcmp(argv[c], "best")) { | ||||
|         config.alpha_filtering = 2; | ||||
|       } else { | ||||
|         fprintf(stderr, "Error! Unrecognized alpha filter: %s\n", argv[c]); | ||||
|         goto Error; | ||||
|       } | ||||
|     } else if (!strcmp(argv[c], "-noalpha")) { | ||||
|       keep_alpha = 0; | ||||
|     } else if (!strcmp(argv[c], "-size") && c < argc - 1) { | ||||
|   | ||||
							
								
								
									
										12
									
								
								man/cwebp.1
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								man/cwebp.1
									
									
									
									
									
								
							| @@ -41,11 +41,13 @@ Specify the compression factor for alpha compression between 0 and 100. | ||||
| Lossless compression of alpha is achieved using a value of 100, while the lower | ||||
| values result in a lossy compression. The default is 100. | ||||
| .TP | ||||
| .B \-alpha_filter int | ||||
| Specify the predictive filtering method (between 0 and 5) for alpha plane. | ||||
| These correspond to prediction modes none, horizontal, vertical, gradient and | ||||
| paeth filters. The prediction mode 5 will try all the prediction modes (0 to 4) | ||||
| and pick the best prediction mode. The default value is 0 (no prediction). | ||||
| .B \-alpha_filter string | ||||
| Specify the predictive filtering method for alpha plane. One of 'none', 'fast' | ||||
| or 'best', in increasing complexity and slowness order. Default is 'fast'. | ||||
| Internally, alpha filtering is performed using four possible predictions (none, | ||||
| horizontal, vertical, gradient). The 'best' mode will try each modes in turn and | ||||
| pick the one which gives the smaller size. The 'fast' mode will just try to | ||||
| form an a-priori guess without testing all modes. | ||||
| .TP | ||||
| .B \-f int | ||||
| Specify the strength of the deblocking filter, between 0 (no filtering) | ||||
|   | ||||
| @@ -14,6 +14,7 @@ | ||||
|  | ||||
| #include "./vp8enci.h" | ||||
| #include "../utils/alpha.h" | ||||
| #include "../utils/filters.h" | ||||
|  | ||||
| #if defined(__cplusplus) || defined(c_plusplus) | ||||
| extern "C" { | ||||
| @@ -33,10 +34,15 @@ int VP8EncFinishAlpha(VP8Encoder* enc) { | ||||
|     const WebPPicture* pic = enc->pic_; | ||||
|     uint8_t* tmp_data = NULL; | ||||
|     size_t tmp_size = 0; | ||||
|     const WEBP_FILTER_TYPE filter = | ||||
|         (config->alpha_filtering == 0) ? WEBP_FILTER_NONE : | ||||
|         (config->alpha_filtering == 1) ? WEBP_FILTER_FAST : | ||||
|                                          WEBP_FILTER_BEST; | ||||
|  | ||||
|     assert(pic->a); | ||||
|     if (!EncodeAlpha(pic->a, pic->width, pic->height, pic->a_stride, | ||||
|                      config->alpha_quality, config->alpha_compression, | ||||
|                      config->alpha_filtering, &tmp_data, &tmp_size)) { | ||||
|                      filter, &tmp_data, &tmp_size)) { | ||||
|       return 0; | ||||
|     } | ||||
|     if (tmp_size != (uint32_t)tmp_size) {  // Sanity check. | ||||
|   | ||||
| @@ -42,7 +42,7 @@ int WebPConfigInitInternal(WebPConfig* const config, | ||||
|   config->autofilter = 0; | ||||
|   config->partition_limit = 0; | ||||
|   config->alpha_compression = 1; | ||||
|   config->alpha_filtering = 0; | ||||
|   config->alpha_filtering = 1; | ||||
|   config->alpha_quality = 100; | ||||
|  | ||||
|   // TODO(skal): tune. | ||||
|   | ||||
| @@ -203,14 +203,35 @@ static int EncodeZlibTCoder(const uint8_t* data, int width, int height, | ||||
|   return ok && !bw->error_; | ||||
| } | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
|  | ||||
| static int EncodeAlphaInternal(const uint8_t* data, int width, int height, | ||||
|                                int method, VP8BitWriter* const bw) { | ||||
|                                int method, int filter, size_t data_size, | ||||
|                                uint8_t* tmp_alpha, VP8BitWriter* const bw) { | ||||
|   int ok = 0; | ||||
|   const uint8_t* alpha_src; | ||||
|   WebPFilterFunc filter_func; | ||||
|   uint8_t header[ALPHA_HEADER_LEN]; | ||||
|   const size_t expected_size = (method == 0) ? | ||||
|           (ALPHA_HEADER_LEN + data_size) : (data_size >> 5); | ||||
|   header[0] = (filter << 4) | method; | ||||
|   header[1] = 0;                // reserved byte for later use | ||||
|   VP8BitWriterInit(bw, expected_size); | ||||
|   VP8BitWriterAppend(bw, header, sizeof(header)); | ||||
|  | ||||
|   filter_func = WebPFilters[filter]; | ||||
|   if (filter_func) { | ||||
|     filter_func(data, width, height, 1, width, tmp_alpha); | ||||
|     alpha_src = tmp_alpha; | ||||
|   }  else { | ||||
|     alpha_src = data; | ||||
|   } | ||||
|  | ||||
|   if (method == 0) { | ||||
|     ok = VP8BitWriterAppend(bw, data, width * height); | ||||
|     ok = VP8BitWriterAppend(bw, alpha_src, width * height); | ||||
|     ok = ok && !bw->error_; | ||||
|   } else if (method == 1) { | ||||
|     ok = EncodeZlibTCoder(data, width, height, bw); | ||||
|   } else { | ||||
|     ok = EncodeZlibTCoder(alpha_src, width, height, bw); | ||||
|     VP8BitWriterFinish(bw); | ||||
|   } | ||||
|   return ok; | ||||
| @@ -239,7 +260,7 @@ int EncodeAlpha(const uint8_t* data, int width, int height, int stride, | ||||
|   assert(data != NULL && output != NULL && output_size != NULL); | ||||
|   assert(width > 0 && height > 0); | ||||
|   assert(stride >= width); | ||||
|   assert(filter < WEBP_FILTER_LAST); | ||||
|   assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST); | ||||
|  | ||||
|   if (quality < 0 || quality > 100) { | ||||
|     return 0; | ||||
| @@ -267,62 +288,67 @@ int EncodeAlpha(const uint8_t* data, int width, int height, int stride, | ||||
|   } | ||||
|  | ||||
|   if (ok) { | ||||
|     WEBP_FILTER_TYPE this_filter; | ||||
|     size_t best_size = 1 << 30; | ||||
|     uint8_t* tmp_out = NULL; | ||||
|     uint8_t* const filtered_alpha = (uint8_t*)malloc(data_size); | ||||
|     if (filtered_alpha == NULL) { | ||||
|       free(quant_alpha); | ||||
|       return 0; | ||||
|     VP8BitWriter bw; | ||||
|     size_t best_score; | ||||
|     int test_filter; | ||||
|     uint8_t* filtered_alpha = NULL; | ||||
|  | ||||
|     // We always test WEBP_FILTER_NONE first. | ||||
|     ok = EncodeAlphaInternal(quant_alpha, width, height, method, | ||||
|                              WEBP_FILTER_NONE, data_size, NULL, &bw); | ||||
|     if (!ok) { | ||||
|       VP8BitWriterWipeOut(&bw); | ||||
|       goto End; | ||||
|     } | ||||
|     // Filtering. | ||||
|     for (this_filter = WEBP_FILTER_NONE; this_filter < WEBP_FILTER_LAST; | ||||
|          ++this_filter) { | ||||
|       uint8_t header[ALPHA_HEADER_LEN]; | ||||
|       VP8BitWriter bw; | ||||
|       WebPFilterFunc filter_func = NULL; | ||||
|       const size_t expected_size = (method == 0) ? | ||||
|           (ALPHA_HEADER_LEN + data_size) : (data_size >> 5); | ||||
|       if (this_filter == WEBP_FILTER_BEST) { | ||||
|         continue; | ||||
|       } else if (this_filter != filter && filter != WEBP_FILTER_BEST) { | ||||
|     best_score = VP8BitWriterSize(&bw); | ||||
|  | ||||
|     if (filter == WEBP_FILTER_FAST) {  // Quick estimate of a second candidate? | ||||
|       filter = EstimateBestFilter(quant_alpha, width, height, width); | ||||
|     } | ||||
|     // Stop? | ||||
|     if (filter == WEBP_FILTER_NONE) { | ||||
|       goto Ok; | ||||
|     } | ||||
|  | ||||
|     filtered_alpha = (uint8_t*)malloc(data_size); | ||||
|     ok = (filtered_alpha != NULL); | ||||
|     if (!ok) { | ||||
|       goto End; | ||||
|     } | ||||
|  | ||||
|     // Try the other mode(s). | ||||
|     for (test_filter = WEBP_FILTER_HORIZONTAL; | ||||
|          ok && (test_filter <= WEBP_FILTER_GRADIENT); | ||||
|          ++test_filter) { | ||||
|       VP8BitWriter tmp_bw; | ||||
|       if (filter != WEBP_FILTER_BEST && test_filter != filter) { | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       header[0] = ((this_filter & 0x0f) << 4) | (method & 0x0f); | ||||
|       header[1] = 0;                // reserved byte for later use | ||||
|       VP8BitWriterInit(&bw, expected_size); | ||||
|       VP8BitWriterAppend(&bw, header, sizeof(header)); | ||||
|  | ||||
|       filter_func = WebPFilters[this_filter]; | ||||
|       if (filter_func) { | ||||
|         filter_func(quant_alpha, width, height, 1, width, filtered_alpha); | ||||
|         ok = EncodeAlphaInternal(filtered_alpha, width, height, method, &bw); | ||||
|       } else { | ||||
|         ok = EncodeAlphaInternal(quant_alpha, width, height, method, &bw); | ||||
|       } | ||||
|       ok = EncodeAlphaInternal(quant_alpha, width, height, method, test_filter, | ||||
|                                data_size, filtered_alpha, &tmp_bw); | ||||
|       if (ok) { | ||||
|         const size_t this_size = VP8BitWriterSize(&bw); | ||||
|         if (this_size < best_size) { | ||||
|           free(tmp_out); | ||||
|           tmp_out = VP8BitWriterBuf(&bw); | ||||
|           best_size = this_size; | ||||
|         } else { | ||||
|           VP8BitWriterWipeOut(&bw); | ||||
|         const size_t score = VP8BitWriterSize(&tmp_bw); | ||||
|         if (score < best_score) { | ||||
|           // swap bitwriter objects. | ||||
|           VP8BitWriter tmp = tmp_bw; | ||||
|           tmp_bw = bw; | ||||
|           bw = tmp; | ||||
|           best_score = score; | ||||
|         } | ||||
|       } else { | ||||
|         free(tmp_out); | ||||
|         VP8BitWriterWipeOut(&bw); | ||||
|         break; | ||||
|       } | ||||
|       VP8BitWriterWipeOut(&tmp_bw); | ||||
|     } | ||||
|  Ok: | ||||
|     if (ok) { | ||||
|       *output_size = best_size; | ||||
|       *output = tmp_out; | ||||
|       *output_size = VP8BitWriterSize(&bw); | ||||
|       *output = VP8BitWriterBuf(&bw); | ||||
|     } | ||||
|     free(filtered_alpha); | ||||
|   } | ||||
|  | ||||
|  End: | ||||
|   free(quant_alpha); | ||||
|   return ok; | ||||
| } | ||||
| @@ -379,7 +405,7 @@ int DecodeAlpha(const uint8_t* data, size_t data_size, | ||||
|   uint8_t* decoded_data = NULL; | ||||
|   const size_t decoded_size = height * width; | ||||
|   uint8_t* unfiltered_data = NULL; | ||||
|   int filter; | ||||
|   WEBP_FILTER_TYPE filter; | ||||
|   int ok = 0; | ||||
|   int method; | ||||
|  | ||||
| @@ -394,7 +420,7 @@ int DecodeAlpha(const uint8_t* data, size_t data_size, | ||||
|   filter = data[0] >> 4; | ||||
|   ok = (data[1] == 0); | ||||
|   if (method < 0 || method > 1 || | ||||
|       filter < WEBP_FILTER_NONE || filter > WEBP_FILTER_PAETH || !ok) { | ||||
|       filter > WEBP_FILTER_GRADIENT || !ok) { | ||||
|     return 0; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -115,6 +115,11 @@ static void VerticalUnfilter(const uint8_t* data, int width, int height, | ||||
| //------------------------------------------------------------------------------ | ||||
| // Gradient filter. | ||||
|  | ||||
| static WEBP_INLINE uint8_t GradientPredictor(uint8_t a, uint8_t b, uint8_t c) { | ||||
|   const int g = a + b - c; | ||||
|   return (g < 0) ? 0 : (g > 255) ? 255 : (uint8_t)g; | ||||
| } | ||||
|  | ||||
| static void GradientFilter(const uint8_t* data, int width, int height, | ||||
|                            int bpp, int stride, uint8_t* filtered_data) { | ||||
|   int h; | ||||
| @@ -131,9 +136,9 @@ static void GradientFilter(const uint8_t* data, int width, int height, | ||||
|     const uint8_t* const prev_line = scan_line - stride; | ||||
|     memcpy((void*)out, (const void*)scan_line, bpp); | ||||
|     for (w = bpp; w < width * bpp; ++w) { | ||||
|       const uint8_t predictor = scan_line[w - bpp] + prev_line[w] - | ||||
|                                 prev_line[w - bpp]; | ||||
|       out[w] = scan_line[w] - predictor; | ||||
|       out[w] = scan_line[w] - GradientPredictor(scan_line[w - bpp], | ||||
|                                                 prev_line[w], | ||||
|                                                 prev_line[w - bpp]); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -154,110 +159,81 @@ static void GradientUnfilter(const uint8_t* data, int width, int height, | ||||
|     const uint8_t* const out_prev_line = out - stride; | ||||
|     memcpy((void*)out, (const void*)scan_line, bpp); | ||||
|     for (w = bpp; w < width * bpp; ++w) { | ||||
|       const uint8_t predictor = out[w - bpp] + out_prev_line[w] - | ||||
|                                 out_prev_line[w - bpp]; | ||||
|       out[w] = scan_line[w] + predictor; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // Paeth filter. | ||||
|  | ||||
| static WEBP_INLINE int AbsDiff(int a, int b) { | ||||
|   return (a > b) ? a - b : b - a; | ||||
| } | ||||
|  | ||||
| static WEBP_INLINE uint8_t PaethPredictor(uint8_t a, uint8_t b, uint8_t c) { | ||||
|   const int p = a + b - c;  // Base. | ||||
|   const int pa = AbsDiff(p, a); | ||||
|   const int pb = AbsDiff(p, b); | ||||
|   const int pc = AbsDiff(p, c); | ||||
|  | ||||
|   // Return nearest to base of a, b, c. | ||||
|   return (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c; | ||||
| } | ||||
|  | ||||
| static void PaethFilter(const uint8_t* data, int width, int height, | ||||
|                         int bpp, int stride, uint8_t* filtered_data) { | ||||
|   int w; | ||||
|   int h; | ||||
|   SANITY_CHECK(data, filtered_data); | ||||
|  | ||||
|   // Top scan line (special case). | ||||
|   memcpy((void*)filtered_data, (const void*)data, bpp); | ||||
|   for (w = bpp; w < width * bpp; ++w) { | ||||
|     // Note: PaethPredictor(scan_line[w - bpp], 0, 0) == scan_line[w - bpp]. | ||||
|     filtered_data[w] = data[w] - data[w - bpp]; | ||||
|   } | ||||
|  | ||||
|   // Filter line-by-line. | ||||
|   for (h = 1; h < height; ++h) { | ||||
|     int w; | ||||
|     const uint8_t* const scan_line = data + h * stride; | ||||
|     uint8_t* const out = filtered_data + h * stride; | ||||
|     const uint8_t* const prev_line = scan_line - stride; | ||||
|     for (w = 0; w < bpp; ++w) { | ||||
|       // Note: PaethPredictor(0, prev_line[w], 0) == prev_line[w]. | ||||
|       out[w] = scan_line[w] - prev_line[w]; | ||||
|     } | ||||
|     for (w = bpp; w < width * bpp; ++w) { | ||||
|       out[w] = scan_line[w] - PaethPredictor(scan_line[w - bpp], prev_line[w], | ||||
|                                              prev_line[w - bpp]); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void PaethUnfilter(const uint8_t* data, int width, int height, | ||||
|                           int bpp, int stride, uint8_t* recon_data) { | ||||
|   int w; | ||||
|   int h; | ||||
|   SANITY_CHECK(data, recon_data); | ||||
|  | ||||
|   // Top scan line (special case). | ||||
|   memcpy((void*)recon_data, (const void*)data, bpp); | ||||
|   for (w = bpp; w < width * bpp; ++w) { | ||||
|     // Note: PaethPredictor(out[w - bpp], 0, 0) == out[w - bpp]. | ||||
|     recon_data[w] = data[w] + recon_data[w - bpp]; | ||||
|   } | ||||
|  | ||||
|   // Unfilter line-by-line. | ||||
|   for (h = 1; h < height; ++h) { | ||||
|     int w; | ||||
|     const uint8_t* const scan_line = data + h * stride; | ||||
|     uint8_t* const out = recon_data + h * stride; | ||||
|     const uint8_t* const out_prev = out - stride; | ||||
|     for (w = 0; w < bpp; ++w) { | ||||
|       // Note: PaethPredictor(0, out_prev[w], 0) == out_prev[w]. | ||||
|       out[w] = scan_line[w] + out_prev[w]; | ||||
|     } | ||||
|     for (w = bpp; w < width * bpp; ++w) { | ||||
|       out[w] = scan_line[w] + PaethPredictor(out[w - bpp], out_prev[w], | ||||
|                                              out_prev[w - bpp]); | ||||
|       out[w] = scan_line[w] + GradientPredictor(out[w - bpp], | ||||
|                                                 out_prev_line[w], | ||||
|                                                 out_prev_line[w - bpp]); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| #undef SANITY_CHECK | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
| // Quick estimate of a potentially interesting filter mode to try, in addition | ||||
| // to the default NONE. | ||||
|  | ||||
| #define SMAX 16 | ||||
| #define SDIFF(a, b) (abs((a) - (b)) >> 4)   // Scoring diff, in [0..SMAX) | ||||
|  | ||||
| WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data, | ||||
|                                     int width, int height, int stride) { | ||||
|   int i, j; | ||||
|   int bins[WEBP_FILTER_LAST][SMAX]; | ||||
|   memset(bins, 0, sizeof(bins)); | ||||
|   // We only sample every other pixels. That's enough. | ||||
|   for (j = 2; j < height - 1; j += 2) { | ||||
|     const uint8_t* const p = data + j * stride; | ||||
|     int mean = p[0]; | ||||
|     for (i = 2; i < width - 1; i += 2) { | ||||
|       const int diff0 = SDIFF(p[i], mean); | ||||
|       const int diff1 = SDIFF(p[i], p[i - 1]); | ||||
|       const int diff2 = SDIFF(p[i], p[i - width]); | ||||
|       const int grad_pred = | ||||
|           GradientPredictor(p[i - 1], p[i - width], p[i - width - 1]); | ||||
|       const int diff3 = SDIFF(p[i], grad_pred); | ||||
|       bins[WEBP_FILTER_NONE][diff0] = 1; | ||||
|       bins[WEBP_FILTER_HORIZONTAL][diff1] = 1; | ||||
|       bins[WEBP_FILTER_VERTICAL][diff2] = 1; | ||||
|       bins[WEBP_FILTER_GRADIENT][diff3] = 1; | ||||
|       mean = (3 * mean + p[i] + 2) >> 2; | ||||
|     } | ||||
|   } | ||||
|   { | ||||
|     WEBP_FILTER_TYPE filter, best_filter = WEBP_FILTER_NONE; | ||||
|     int best_score = 0x7fffffff; | ||||
|     for (filter = WEBP_FILTER_NONE; filter < WEBP_FILTER_LAST; ++filter) { | ||||
|       int score = 0; | ||||
|       for (i = 0; i < SMAX; ++i) { | ||||
|         if (bins[filter][i] > 0) { | ||||
|           score += i; | ||||
|         } | ||||
|       } | ||||
|       if (score < best_score) { | ||||
|         best_score = score; | ||||
|         best_filter = filter; | ||||
|       } | ||||
|     } | ||||
|     return best_filter; | ||||
|   } | ||||
| } | ||||
|  | ||||
| #undef SMAX | ||||
| #undef SDIFF | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| const WebPFilterFunc WebPFilters[WEBP_FILTER_LAST] = { | ||||
|     NULL,              // WEBP_FILTER_NONE | ||||
|     HorizontalFilter,  // WEBP_FILTER_HORIZONTAL | ||||
|     VerticalFilter,    // WEBP_FILTER_VERTICAL | ||||
|     GradientFilter,    // WEBP_FILTER_GRADIENT | ||||
|     PaethFilter,       // WEBP_FILTER_PAETH | ||||
|     NULL               // WEBP_FILTER_BEST | ||||
|     GradientFilter     // WEBP_FILTER_GRADIENT | ||||
| }; | ||||
|  | ||||
| const WebPFilterFunc WebPUnfilters[WEBP_FILTER_LAST] = { | ||||
|     NULL,                // WEBP_FILTER_NONE | ||||
|     HorizontalUnfilter,  // WEBP_FILTER_HORIZONTAL | ||||
|     VerticalUnfilter,    // WEBP_FILTER_VERTICAL | ||||
|     GradientUnfilter,    // WEBP_FILTER_GRADIENT | ||||
|     PaethUnfilter,       // WEBP_FILTER_PAETH | ||||
|     NULL                 // WEBP_FILTER_BEST | ||||
|     GradientUnfilter     // WEBP_FILTER_GRADIENT | ||||
| }; | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
|   | ||||
| @@ -24,9 +24,9 @@ typedef enum { | ||||
|   WEBP_FILTER_HORIZONTAL, | ||||
|   WEBP_FILTER_VERTICAL, | ||||
|   WEBP_FILTER_GRADIENT, | ||||
|   WEBP_FILTER_PAETH, | ||||
|   WEBP_FILTER_LAST = WEBP_FILTER_GRADIENT + 1,  // end marker | ||||
|   WEBP_FILTER_BEST, | ||||
|   WEBP_FILTER_LAST, | ||||
|   WEBP_FILTER_FAST | ||||
| } WEBP_FILTER_TYPE; | ||||
|  | ||||
| typedef void (*WebPFilterFunc)(const uint8_t* in, int width, int height, | ||||
| @@ -38,10 +38,14 @@ typedef void (*WebPFilterFunc)(const uint8_t* in, int width, int height, | ||||
| // 'bpp' is number of bytes per pixel, and | ||||
| // 'stride' is number of bytes per scan line (with possible padding). | ||||
| // 'out' should be pre-allocated. | ||||
| extern const WebPFilterFunc WebPFilters[/*WEBP_FILTER_LAST*/]; | ||||
| extern const WebPFilterFunc WebPFilters[WEBP_FILTER_LAST]; | ||||
|  | ||||
| // Reconstruct the original data from the given filtered data. | ||||
| extern const WebPFilterFunc WebPUnfilters[/*WEBP_FILTER_LAST*/]; | ||||
| extern const WebPFilterFunc WebPUnfilters[WEBP_FILTER_LAST]; | ||||
|  | ||||
| // Fast estimate of a potentially good filter. | ||||
| extern WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data, | ||||
|                                            int width, int height, int stride); | ||||
|  | ||||
| #if defined(__cplusplus) || defined(c_plusplus) | ||||
| }    // extern "C" | ||||
|   | ||||
| @@ -75,8 +75,7 @@ typedef struct { | ||||
|                           // 1 = backward reference counts encoded with | ||||
|                           // arithmetic encoder). Default is 1. | ||||
|   int alpha_filtering;    // Predictive filtering method for alpha plane. | ||||
|                           // (0 = none, 1 = horizontal, 2 = vertical, 3 = grad, | ||||
|                           // 4 = Paeth and 5 = Best of (0 .. 4). | ||||
|                           //  0: none, 1: fast, 2: best. Default if 1. | ||||
|   int alpha_quality;      // Between 0 (smallest size) and 100 (lossless). | ||||
|                           // Default is 100. | ||||
| } WebPConfig; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user