Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / third_party / libwebp / enc / alpha.c
1 // Copyright 2011 Google Inc. All Rights Reserved.
2 //
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the COPYING file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS. All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 // -----------------------------------------------------------------------------
9 //
10 // Alpha-plane compression.
11 //
12 // Author: Skal (pascal.massimino@gmail.com)
13
14 #include <assert.h>
15 #include <stdlib.h>
16
17 #include "./vp8enci.h"
18 #include "../utils/filters.h"
19 #include "../utils/quant_levels.h"
20 #include "../webp/format_constants.h"
21
22 // -----------------------------------------------------------------------------
23 // Encodes the given alpha data via specified compression method 'method'.
24 // The pre-processing (quantization) is performed if 'quality' is less than 100.
25 // For such cases, the encoding is lossy. The valid range is [0, 100] for
26 // 'quality' and [0, 1] for 'method':
27 //   'method = 0' - No compression;
28 //   'method = 1' - Use lossless coder on the alpha plane only
29 // 'filter' values [0, 4] correspond to prediction modes none, horizontal,
30 // vertical & gradient filters. The prediction mode 4 will try all the
31 // prediction modes 0 to 3 and pick the best one.
32 // 'effort_level': specifies how much effort must be spent to try and reduce
33 //  the compressed output size. In range 0 (quick) to 6 (slow).
34 //
35 // 'output' corresponds to the buffer containing compressed alpha data.
36 //          This buffer is allocated by this method and caller should call
37 //          free(*output) when done.
38 // 'output_size' corresponds to size of this compressed alpha buffer.
39 //
40 // Returns 1 on successfully encoding the alpha and
41 //         0 if either:
42 //           invalid quality or method, or
43 //           memory allocation for the compressed data fails.
44
45 #include "../enc/vp8li.h"
46
47 static int EncodeLossless(const uint8_t* const data, int width, int height,
48                           int effort_level,  // in [0..6] range
49                           VP8BitWriter* const bw,
50                           WebPAuxStats* const stats) {
51   int ok = 0;
52   WebPConfig config;
53   WebPPicture picture;
54   VP8LBitWriter tmp_bw;
55
56   WebPPictureInit(&picture);
57   picture.width = width;
58   picture.height = height;
59   picture.use_argb = 1;
60   picture.stats = stats;
61   if (!WebPPictureAlloc(&picture)) return 0;
62
63   // Transfer the alpha values to the green channel.
64   {
65     int i, j;
66     uint32_t* dst = picture.argb;
67     const uint8_t* src = data;
68     for (j = 0; j < picture.height; ++j) {
69       for (i = 0; i < picture.width; ++i) {
70         dst[i] = src[i] << 8;  // we leave A/R/B channels zero'd.
71       }
72       src += width;
73       dst += picture.argb_stride;
74     }
75   }
76
77   WebPConfigInit(&config);
78   config.lossless = 1;
79   config.method = effort_level;  // impact is very small
80   // Set a low default quality for encoding alpha. Ensure that Alpha quality at
81   // lower methods (3 and below) is less than the threshold for triggering
82   // costly 'BackwardReferencesTraceBackwards'.
83   config.quality = 8.f * effort_level;
84   assert(config.quality >= 0 && config.quality <= 100.f);
85
86   ok = VP8LBitWriterInit(&tmp_bw, (width * height) >> 3);
87   ok = ok && (VP8LEncodeStream(&config, &picture, &tmp_bw) == VP8_ENC_OK);
88   WebPPictureFree(&picture);
89   if (ok) {
90     const uint8_t* const buffer = VP8LBitWriterFinish(&tmp_bw);
91     const size_t buffer_size = VP8LBitWriterNumBytes(&tmp_bw);
92     VP8BitWriterAppend(bw, buffer, buffer_size);
93   }
94   VP8LBitWriterDestroy(&tmp_bw);
95   return ok && !bw->error_;
96 }
97
98 // -----------------------------------------------------------------------------
99
100 // Small struct to hold the result of a filter mode compression attempt.
101 typedef struct {
102   size_t score;
103   VP8BitWriter bw;
104   WebPAuxStats stats;
105 } FilterTrial;
106
107 // This function always returns an initialized 'bw' object, even upon error.
108 static int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
109                                int method, int filter, int reduce_levels,
110                                int effort_level,  // in [0..6] range
111                                uint8_t* const tmp_alpha,
112                                FilterTrial* result) {
113   int ok = 0;
114   const uint8_t* alpha_src;
115   WebPFilterFunc filter_func;
116   uint8_t header;
117   size_t expected_size;
118   const size_t data_size = width * height;
119
120   assert((uint64_t)data_size == (uint64_t)width * height);  // as per spec
121   assert(filter >= 0 && filter < WEBP_FILTER_LAST);
122   assert(method >= ALPHA_NO_COMPRESSION);
123   assert(method <= ALPHA_LOSSLESS_COMPRESSION);
124   assert(sizeof(header) == ALPHA_HEADER_LEN);
125   // TODO(skal): have a common function and #define's to validate alpha params.
126
127   expected_size =
128       (method == ALPHA_NO_COMPRESSION) ? (ALPHA_HEADER_LEN + data_size)
129                                        : (data_size >> 5);
130   header = method | (filter << 2);
131   if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4;
132
133   VP8BitWriterInit(&result->bw, expected_size);
134   VP8BitWriterAppend(&result->bw, &header, ALPHA_HEADER_LEN);
135
136   filter_func = WebPFilters[filter];
137   if (filter_func != NULL) {
138     filter_func(data, width, height, width, tmp_alpha);
139     alpha_src = tmp_alpha;
140   }  else {
141     alpha_src = data;
142   }
143
144   if (method == ALPHA_NO_COMPRESSION) {
145     ok = VP8BitWriterAppend(&result->bw, alpha_src, width * height);
146     ok = ok && !result->bw.error_;
147   } else {
148     ok = EncodeLossless(alpha_src, width, height, effort_level,
149                         &result->bw, &result->stats);
150     VP8BitWriterFinish(&result->bw);
151   }
152   result->score = VP8BitWriterSize(&result->bw);
153   return ok;
154 }
155
156 // -----------------------------------------------------------------------------
157
158 // TODO(skal): move to dsp/ ?
159 static void CopyPlane(const uint8_t* src, int src_stride,
160                       uint8_t* dst, int dst_stride, int width, int height) {
161   while (height-- > 0) {
162     memcpy(dst, src, width);
163     src += src_stride;
164     dst += dst_stride;
165   }
166 }
167
168 static int GetNumColors(const uint8_t* data, int width, int height,
169                         int stride) {
170   int j;
171   int colors = 0;
172   uint8_t color[256] = { 0 };
173
174   for (j = 0; j < height; ++j) {
175     int i;
176     const uint8_t* const p = data + j * stride;
177     for (i = 0; i < width; ++i) {
178       color[p[i]] = 1;
179     }
180   }
181   for (j = 0; j < 256; ++j) {
182     if (color[j] > 0) ++colors;
183   }
184   return colors;
185 }
186
187 #define FILTER_TRY_NONE (1 << WEBP_FILTER_NONE)
188 #define FILTER_TRY_ALL ((1 << WEBP_FILTER_LAST) - 1)
189
190 // Given the input 'filter' option, return an OR'd bit-set of filters to try.
191 static uint32_t GetFilterMap(const uint8_t* alpha, int width, int height,
192                              int filter, int effort_level) {
193   uint32_t bit_map = 0U;
194   if (filter == WEBP_FILTER_FAST) {
195     // Quick estimate of the best candidate.
196     int try_filter_none = (effort_level > 3);
197     const int kMinColorsForFilterNone = 16;
198     const int kMaxColorsForFilterNone = 192;
199     const int num_colors = GetNumColors(alpha, width, height, width);
200     // For low number of colors, NONE yields better compression.
201     filter = (num_colors <= kMinColorsForFilterNone) ? WEBP_FILTER_NONE :
202              EstimateBestFilter(alpha, width, height, width);
203     bit_map |= 1 << filter;
204     // For large number of colors, try FILTER_NONE in addition to the best
205     // filter as well.
206     if (try_filter_none || num_colors > kMaxColorsForFilterNone) {
207       bit_map |= FILTER_TRY_NONE;
208     }
209   } else if (filter == WEBP_FILTER_NONE) {
210     bit_map = FILTER_TRY_NONE;
211   } else {  // WEBP_FILTER_BEST -> try all
212     bit_map = FILTER_TRY_ALL;
213   }
214   return bit_map;
215 }
216
217 static void InitFilterTrial(FilterTrial* const score) {
218   score->score = (size_t)~0U;
219   VP8BitWriterInit(&score->bw, 0);
220 }
221
222 static int ApplyFiltersAndEncode(const uint8_t* alpha, int width, int height,
223                                  size_t data_size, int method, int filter,
224                                  int reduce_levels, int effort_level,
225                                  uint8_t** const output,
226                                  size_t* const output_size,
227                                  WebPAuxStats* const stats) {
228   int ok = 1;
229   FilterTrial best;
230   uint32_t try_map =
231       GetFilterMap(alpha, width, height, filter, effort_level);
232   InitFilterTrial(&best);
233   if (try_map != FILTER_TRY_NONE) {
234     uint8_t* filtered_alpha =  (uint8_t*)malloc(data_size);
235     if (filtered_alpha == NULL) return 0;
236
237     for (filter = WEBP_FILTER_NONE; ok && try_map; ++filter, try_map >>= 1) {
238       if (try_map & 1) {
239         FilterTrial trial;
240         ok = EncodeAlphaInternal(alpha, width, height, method, filter,
241                                  reduce_levels, effort_level, filtered_alpha,
242                                  &trial);
243         if (ok && trial.score < best.score) {
244           VP8BitWriterWipeOut(&best.bw);
245           best = trial;
246         } else {
247           VP8BitWriterWipeOut(&trial.bw);
248         }
249       }
250     }
251     free(filtered_alpha);
252   } else {
253     ok = EncodeAlphaInternal(alpha, width, height, method, WEBP_FILTER_NONE,
254                              reduce_levels, effort_level, NULL, &best);
255   }
256   if (ok) {
257     if (stats != NULL) *stats = best.stats;
258     *output_size = VP8BitWriterSize(&best.bw);
259     *output = VP8BitWriterBuf(&best.bw);
260   } else {
261     VP8BitWriterWipeOut(&best.bw);
262   }
263   return ok;
264 }
265
266 static int EncodeAlpha(VP8Encoder* const enc,
267                        int quality, int method, int filter,
268                        int effort_level,
269                        uint8_t** const output, size_t* const output_size) {
270   const WebPPicture* const pic = enc->pic_;
271   const int width = pic->width;
272   const int height = pic->height;
273
274   uint8_t* quant_alpha = NULL;
275   const size_t data_size = width * height;
276   uint64_t sse = 0;
277   int ok = 1;
278   const int reduce_levels = (quality < 100);
279
280   // quick sanity checks
281   assert((uint64_t)data_size == (uint64_t)width * height);  // as per spec
282   assert(enc != NULL && pic != NULL && pic->a != NULL);
283   assert(output != NULL && output_size != NULL);
284   assert(width > 0 && height > 0);
285   assert(pic->a_stride >= width);
286   assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST);
287
288   if (quality < 0 || quality > 100) {
289     return 0;
290   }
291
292   if (method < ALPHA_NO_COMPRESSION || method > ALPHA_LOSSLESS_COMPRESSION) {
293     return 0;
294   }
295
296   if (method == ALPHA_NO_COMPRESSION) {
297     // Don't filter, as filtering will make no impact on compressed size.
298     filter = WEBP_FILTER_NONE;
299   }
300
301   quant_alpha = (uint8_t*)malloc(data_size);
302   if (quant_alpha == NULL) {
303     return 0;
304   }
305
306   // Extract alpha data (width x height) from raw_data (stride x height).
307   CopyPlane(pic->a, pic->a_stride, quant_alpha, width, width, height);
308
309   if (reduce_levels) {  // No Quantization required for 'quality = 100'.
310     // 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence
311     // mapped to moderate quality 70. Hence Quality:[0, 70] -> Levels:[2, 16]
312     // and Quality:]70, 100] -> Levels:]16, 256].
313     const int alpha_levels = (quality <= 70) ? (2 + quality / 5)
314                                              : (16 + (quality - 70) * 8);
315     ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, &sse);
316   }
317
318   if (ok) {
319     ok = ApplyFiltersAndEncode(quant_alpha, width, height, data_size, method,
320                                filter, reduce_levels, effort_level, output,
321                                output_size, pic->stats);
322     if (pic->stats != NULL) {  // need stats?
323       pic->stats->coded_size += (int)(*output_size);
324       enc->sse_[3] = sse;
325     }
326   }
327
328   free(quant_alpha);
329   return ok;
330 }
331
332 //------------------------------------------------------------------------------
333 // Main calls
334
335 static int CompressAlphaJob(VP8Encoder* const enc, void* dummy) {
336   const WebPConfig* config = enc->config_;
337   uint8_t* alpha_data = NULL;
338   size_t alpha_size = 0;
339   const int effort_level = config->method;  // maps to [0..6]
340   const WEBP_FILTER_TYPE filter =
341       (config->alpha_filtering == 0) ? WEBP_FILTER_NONE :
342       (config->alpha_filtering == 1) ? WEBP_FILTER_FAST :
343                                        WEBP_FILTER_BEST;
344   if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression,
345                    filter, effort_level, &alpha_data, &alpha_size)) {
346     return 0;
347   }
348   if (alpha_size != (uint32_t)alpha_size) {  // Sanity check.
349     free(alpha_data);
350     return 0;
351   }
352   enc->alpha_data_size_ = (uint32_t)alpha_size;
353   enc->alpha_data_ = alpha_data;
354   (void)dummy;
355   return 1;
356 }
357
358 void VP8EncInitAlpha(VP8Encoder* const enc) {
359   enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_);
360   enc->alpha_data_ = NULL;
361   enc->alpha_data_size_ = 0;
362   if (enc->thread_level_ > 0) {
363     WebPWorker* const worker = &enc->alpha_worker_;
364     WebPWorkerInit(worker);
365     worker->data1 = enc;
366     worker->data2 = NULL;
367     worker->hook = (WebPWorkerHook)CompressAlphaJob;
368   }
369 }
370
371 int VP8EncStartAlpha(VP8Encoder* const enc) {
372   if (enc->has_alpha_) {
373     if (enc->thread_level_ > 0) {
374       WebPWorker* const worker = &enc->alpha_worker_;
375       if (!WebPWorkerReset(worker)) {    // Makes sure worker is good to go.
376         return 0;
377       }
378       WebPWorkerLaunch(worker);
379       return 1;
380     } else {
381       return CompressAlphaJob(enc, NULL);   // just do the job right away
382     }
383   }
384   return 1;
385 }
386
387 int VP8EncFinishAlpha(VP8Encoder* const enc) {
388   if (enc->has_alpha_) {
389     if (enc->thread_level_ > 0) {
390       WebPWorker* const worker = &enc->alpha_worker_;
391       if (!WebPWorkerSync(worker)) return 0;  // error
392     }
393   }
394   return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
395 }
396
397 int VP8EncDeleteAlpha(VP8Encoder* const enc) {
398   int ok = 1;
399   if (enc->thread_level_ > 0) {
400     WebPWorker* const worker = &enc->alpha_worker_;
401     ok = WebPWorkerSync(worker);  // finish anything left in flight
402     WebPWorkerEnd(worker);  // still need to end the worker, even if !ok
403   }
404   free(enc->alpha_data_);
405   enc->alpha_data_ = NULL;
406   enc->alpha_data_size_ = 0;
407   enc->has_alpha_ = 0;
408   return ok;
409 }
410