1a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Copyright 2011 Google Inc. All Rights Reserved.
2466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//
30406ce1417f76f2034833414dcecc9f56253640cVikas Arora// Use of this source code is governed by a BSD-style license
40406ce1417f76f2034833414dcecc9f56253640cVikas Arora// that can be found in the COPYING file in the root of the source
50406ce1417f76f2034833414dcecc9f56253640cVikas Arora// tree. An additional intellectual property rights grant can be found
60406ce1417f76f2034833414dcecc9f56253640cVikas Arora// in the file PATENTS. All contributing project authors may
70406ce1417f76f2034833414dcecc9f56253640cVikas Arora// be found in the AUTHORS file in the root of the source tree.
8466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora// -----------------------------------------------------------------------------
9466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//
10466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora// Alpha-plane compression.
11466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//
12466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora// Author: Skal (pascal.massimino@gmail.com)
13466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
14466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora#include <assert.h>
15466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora#include <stdlib.h>
16466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
17a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora#include "./vp8enci.h"
18a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora#include "../utils/filters.h"
19a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora#include "../utils/quant_levels.h"
2033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora#include "../utils/utils.h"
21a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora#include "webp/format_constants.h"
22466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
23a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// -----------------------------------------------------------------------------
24a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Encodes the given alpha data via specified compression method 'method'.
25a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// The pre-processing (quantization) is performed if 'quality' is less than 100.
26a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// For such cases, the encoding is lossy. The valid range is [0, 100] for
27a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 'quality' and [0, 1] for 'method':
28a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//   'method = 0' - No compression;
29a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//   'method = 1' - Use lossless coder on the alpha plane only
30a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 'filter' values [0, 4] correspond to prediction modes none, horizontal,
31a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// vertical & gradient filters. The prediction mode 4 will try all the
32a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// prediction modes 0 to 3 and pick the best one.
33a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 'effort_level': specifies how much effort must be spent to try and reduce
34a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//  the compressed output size. In range 0 (quick) to 6 (slow).
35a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//
36a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 'output' corresponds to the buffer containing compressed alpha data.
37a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//          This buffer is allocated by this method and caller should call
3833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora//          WebPSafeFree(*output) when done.
39a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 'output_size' corresponds to size of this compressed alpha buffer.
40a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//
41a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Returns 1 on successfully encoding the alpha and
42a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//         0 if either:
43a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//           invalid quality or method, or
44a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//           memory allocation for the compressed data fails.
45a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
46a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora#include "../enc/vp8li.h"
47a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
48a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arorastatic int EncodeLossless(const uint8_t* const data, int width, int height,
49a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                          int effort_level,  // in [0..6] range
508c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora                          VP8LBitWriter* const bw,
51a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                          WebPAuxStats* const stats) {
52a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  int ok = 0;
53a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  WebPConfig config;
54a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  WebPPicture picture;
55a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
56a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  WebPPictureInit(&picture);
57a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  picture.width = width;
58a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  picture.height = height;
59a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  picture.use_argb = 1;
60a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  picture.stats = stats;
61a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (!WebPPictureAlloc(&picture)) return 0;
62a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
63a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  // Transfer the alpha values to the green channel.
64a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  {
65a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    int i, j;
66a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    uint32_t* dst = picture.argb;
67a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    const uint8_t* src = data;
68a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    for (j = 0; j < picture.height; ++j) {
69a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      for (i = 0; i < picture.width; ++i) {
708b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        dst[i] = src[i] << 8;  // we leave A/R/B channels zero'd.
71a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      }
72a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      src += width;
73a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      dst += picture.argb_stride;
74a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    }
75a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
76a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
77a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  WebPConfigInit(&config);
78a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  config.lossless = 1;
79a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  config.method = effort_level;  // impact is very small
808b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  // Set a low default quality for encoding alpha. Ensure that Alpha quality at
818b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  // lower methods (3 and below) is less than the threshold for triggering
828b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  // costly 'BackwardReferencesTraceBackwards'.
838b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  config.quality = 8.f * effort_level;
841e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  assert(config.quality >= 0 && config.quality <= 100.f);
85a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
868c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora  ok = (VP8LEncodeStream(&config, &picture, bw) == VP8_ENC_OK);
87a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  WebPPictureFree(&picture);
888c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora  ok = ok && !bw->error_;
898c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora  if (!ok) {
908c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora    VP8LBitWriterDestroy(bw);
918c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora    return 0;
92a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
938c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora  return 1;
948c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora
95a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora}
96a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
97a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// -----------------------------------------------------------------------------
98a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
998b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora// Small struct to hold the result of a filter mode compression attempt.
1008b720228d581a84fd173b6dcb2fa295b59db489aVikas Aroratypedef struct {
1018b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  size_t score;
1028b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  VP8BitWriter bw;
1038b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  WebPAuxStats stats;
1048b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora} FilterTrial;
1058b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
1068b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora// This function always returns an initialized 'bw' object, even upon error.
107a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arorastatic int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
108a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                               int method, int filter, int reduce_levels,
109a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                               int effort_level,  // in [0..6] range
110a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                               uint8_t* const tmp_alpha,
1118b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                               FilterTrial* result) {
112a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  int ok = 0;
113a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const uint8_t* alpha_src;
114a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  WebPFilterFunc filter_func;
115a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  uint8_t header;
116a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const size_t data_size = width * height;
1178c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora  const uint8_t* output = NULL;
1188c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora  size_t output_size = 0;
1198c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora  VP8LBitWriter tmp_bw;
120a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
121a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert((uint64_t)data_size == (uint64_t)width * height);  // as per spec
122a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(filter >= 0 && filter < WEBP_FILTER_LAST);
123a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(method >= ALPHA_NO_COMPRESSION);
124a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(method <= ALPHA_LOSSLESS_COMPRESSION);
125a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(sizeof(header) == ALPHA_HEADER_LEN);
126a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  // TODO(skal): have a common function and #define's to validate alpha params.
127a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
128a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  filter_func = WebPFilters[filter];
1290406ce1417f76f2034833414dcecc9f56253640cVikas Arora  if (filter_func != NULL) {
1300406ce1417f76f2034833414dcecc9f56253640cVikas Arora    filter_func(data, width, height, width, tmp_alpha);
131a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    alpha_src = tmp_alpha;
132a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }  else {
133a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    alpha_src = data;
134a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
135a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
1368c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora  if (method != ALPHA_NO_COMPRESSION) {
1378c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora    ok = VP8LBitWriterInit(&tmp_bw, data_size >> 3);
1388c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora    ok = ok && EncodeLossless(alpha_src, width, height, effort_level,
1398c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora                              &tmp_bw, &result->stats);
1408c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora    if (ok) {
1418c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora      output = VP8LBitWriterFinish(&tmp_bw);
1428c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora      output_size = VP8LBitWriterNumBytes(&tmp_bw);
1438c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora      if (output_size > data_size) {
1448c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora        // compressed size is larger than source! Revert to uncompressed mode.
1458c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora        method = ALPHA_NO_COMPRESSION;
1468c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora        VP8LBitWriterDestroy(&tmp_bw);
1478c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora      }
1488c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora    } else {
1498c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora      VP8LBitWriterDestroy(&tmp_bw);
1508c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora      return 0;
1518c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora    }
1528c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora  }
1538c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora
154a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (method == ALPHA_NO_COMPRESSION) {
1558c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora    output = alpha_src;
1568c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora    output_size = data_size;
1578c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora    ok = 1;
1588c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora  }
1598c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora
1608c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora  // Emit final result.
1618c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora  header = method | (filter << 2);
1628c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora  if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4;
1638c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora
1648c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora  VP8BitWriterInit(&result->bw, ALPHA_HEADER_LEN + output_size);
1658c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora  ok = ok && VP8BitWriterAppend(&result->bw, &header, ALPHA_HEADER_LEN);
1668c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora  ok = ok && VP8BitWriterAppend(&result->bw, output, output_size);
1678c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora
1688c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora  if (method != ALPHA_NO_COMPRESSION) {
1698c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora    VP8LBitWriterDestroy(&tmp_bw);
170a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
1718c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora  ok = ok && !result->bw.error_;
1728b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  result->score = VP8BitWriterSize(&result->bw);
173a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  return ok;
174a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora}
175a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
176a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// -----------------------------------------------------------------------------
177a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
178a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// TODO(skal): move to dsp/ ?
179a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arorastatic void CopyPlane(const uint8_t* src, int src_stride,
180a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                      uint8_t* dst, int dst_stride, int width, int height) {
181a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  while (height-- > 0) {
182a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    memcpy(dst, src, width);
183a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    src += src_stride;
184a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    dst += dst_stride;
185a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
186a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora}
187a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
1880406ce1417f76f2034833414dcecc9f56253640cVikas Arorastatic int GetNumColors(const uint8_t* data, int width, int height,
1890406ce1417f76f2034833414dcecc9f56253640cVikas Arora                        int stride) {
1900406ce1417f76f2034833414dcecc9f56253640cVikas Arora  int j;
1910406ce1417f76f2034833414dcecc9f56253640cVikas Arora  int colors = 0;
1920406ce1417f76f2034833414dcecc9f56253640cVikas Arora  uint8_t color[256] = { 0 };
1930406ce1417f76f2034833414dcecc9f56253640cVikas Arora
1940406ce1417f76f2034833414dcecc9f56253640cVikas Arora  for (j = 0; j < height; ++j) {
1950406ce1417f76f2034833414dcecc9f56253640cVikas Arora    int i;
1960406ce1417f76f2034833414dcecc9f56253640cVikas Arora    const uint8_t* const p = data + j * stride;
1970406ce1417f76f2034833414dcecc9f56253640cVikas Arora    for (i = 0; i < width; ++i) {
1980406ce1417f76f2034833414dcecc9f56253640cVikas Arora      color[p[i]] = 1;
1990406ce1417f76f2034833414dcecc9f56253640cVikas Arora    }
2000406ce1417f76f2034833414dcecc9f56253640cVikas Arora  }
2010406ce1417f76f2034833414dcecc9f56253640cVikas Arora  for (j = 0; j < 256; ++j) {
2020406ce1417f76f2034833414dcecc9f56253640cVikas Arora    if (color[j] > 0) ++colors;
2030406ce1417f76f2034833414dcecc9f56253640cVikas Arora  }
2040406ce1417f76f2034833414dcecc9f56253640cVikas Arora  return colors;
2050406ce1417f76f2034833414dcecc9f56253640cVikas Arora}
2060406ce1417f76f2034833414dcecc9f56253640cVikas Arora
2078b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora#define FILTER_TRY_NONE (1 << WEBP_FILTER_NONE)
2088b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora#define FILTER_TRY_ALL ((1 << WEBP_FILTER_LAST) - 1)
2098b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
2108b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora// Given the input 'filter' option, return an OR'd bit-set of filters to try.
2118b720228d581a84fd173b6dcb2fa295b59db489aVikas Arorastatic uint32_t GetFilterMap(const uint8_t* alpha, int width, int height,
2128b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                             int filter, int effort_level) {
2138b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  uint32_t bit_map = 0U;
2148b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (filter == WEBP_FILTER_FAST) {
2158b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    // Quick estimate of the best candidate.
2168b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    int try_filter_none = (effort_level > 3);
2178b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    const int kMinColorsForFilterNone = 16;
2188b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    const int kMaxColorsForFilterNone = 192;
2198b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    const int num_colors = GetNumColors(alpha, width, height, width);
2208b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    // For low number of colors, NONE yields better compression.
2218b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    filter = (num_colors <= kMinColorsForFilterNone) ? WEBP_FILTER_NONE :
2228b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora             EstimateBestFilter(alpha, width, height, width);
2238b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    bit_map |= 1 << filter;
2248b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    // For large number of colors, try FILTER_NONE in addition to the best
2258b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    // filter as well.
2268b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    if (try_filter_none || num_colors > kMaxColorsForFilterNone) {
2278b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      bit_map |= FILTER_TRY_NONE;
2288b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    }
2298b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  } else if (filter == WEBP_FILTER_NONE) {
2308b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    bit_map = FILTER_TRY_NONE;
2318b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  } else {  // WEBP_FILTER_BEST -> try all
2328b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    bit_map = FILTER_TRY_ALL;
2338b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  }
2348b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  return bit_map;
2358b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora}
2368b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
2378b720228d581a84fd173b6dcb2fa295b59db489aVikas Arorastatic void InitFilterTrial(FilterTrial* const score) {
2388b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  score->score = (size_t)~0U;
2398b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  VP8BitWriterInit(&score->bw, 0);
2408b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora}
2418b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
2428b720228d581a84fd173b6dcb2fa295b59db489aVikas Arorastatic int ApplyFiltersAndEncode(const uint8_t* alpha, int width, int height,
2438b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                                 size_t data_size, int method, int filter,
2448b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                                 int reduce_levels, int effort_level,
2458b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                                 uint8_t** const output,
2468b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                                 size_t* const output_size,
2478b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                                 WebPAuxStats* const stats) {
2488b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  int ok = 1;
2498b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  FilterTrial best;
2508b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  uint32_t try_map =
2518b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      GetFilterMap(alpha, width, height, filter, effort_level);
2528b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  InitFilterTrial(&best);
2538b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (try_map != FILTER_TRY_NONE) {
25433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    uint8_t* filtered_alpha =  (uint8_t*)WebPSafeMalloc(1ULL, data_size);
2558b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    if (filtered_alpha == NULL) return 0;
2568b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
2578b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    for (filter = WEBP_FILTER_NONE; ok && try_map; ++filter, try_map >>= 1) {
2588b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      if (try_map & 1) {
2598b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        FilterTrial trial;
2608b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        ok = EncodeAlphaInternal(alpha, width, height, method, filter,
2618b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                                 reduce_levels, effort_level, filtered_alpha,
2628b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                                 &trial);
2638b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        if (ok && trial.score < best.score) {
2648b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          VP8BitWriterWipeOut(&best.bw);
2658b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          best = trial;
2668b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        } else {
2678b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          VP8BitWriterWipeOut(&trial.bw);
2688b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        }
2698b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      }
2708b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    }
27133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    WebPSafeFree(filtered_alpha);
2728b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  } else {
2738b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    ok = EncodeAlphaInternal(alpha, width, height, method, WEBP_FILTER_NONE,
2748b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                             reduce_levels, effort_level, NULL, &best);
2758b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  }
2768b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (ok) {
2778b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    if (stats != NULL) *stats = best.stats;
2788b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    *output_size = VP8BitWriterSize(&best.bw);
2798b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    *output = VP8BitWriterBuf(&best.bw);
2808b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  } else {
2818b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    VP8BitWriterWipeOut(&best.bw);
2828b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  }
2838b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  return ok;
2848b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora}
2858b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
286a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arorastatic int EncodeAlpha(VP8Encoder* const enc,
287a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                       int quality, int method, int filter,
288a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                       int effort_level,
289a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                       uint8_t** const output, size_t* const output_size) {
290a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const WebPPicture* const pic = enc->pic_;
291a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const int width = pic->width;
292a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const int height = pic->height;
293466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
294a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  uint8_t* quant_alpha = NULL;
295a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const size_t data_size = width * height;
296a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  uint64_t sse = 0;
297a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  int ok = 1;
298a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const int reduce_levels = (quality < 100);
299466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
300a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  // quick sanity checks
301a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert((uint64_t)data_size == (uint64_t)width * height);  // as per spec
302a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(enc != NULL && pic != NULL && pic->a != NULL);
303a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(output != NULL && output_size != NULL);
304a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(width > 0 && height > 0);
305a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(pic->a_stride >= width);
306a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST);
307a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
308a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (quality < 0 || quality > 100) {
309a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    return 0;
310a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
311466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
312a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (method < ALPHA_NO_COMPRESSION || method > ALPHA_LOSSLESS_COMPRESSION) {
313a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    return 0;
314a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
315466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
3168b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (method == ALPHA_NO_COMPRESSION) {
3178b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    // Don't filter, as filtering will make no impact on compressed size.
3188b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    filter = WEBP_FILTER_NONE;
3198b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  }
3208b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
32133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  quant_alpha = (uint8_t*)WebPSafeMalloc(1ULL, data_size);
322a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (quant_alpha == NULL) {
323466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    return 0;
324466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  }
325a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
326a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  // Extract alpha data (width x height) from raw_data (stride x height).
327a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  CopyPlane(pic->a, pic->a_stride, quant_alpha, width, width, height);
328a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
329a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (reduce_levels) {  // No Quantization required for 'quality = 100'.
330a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    // 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence
331a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    // mapped to moderate quality 70. Hence Quality:[0, 70] -> Levels:[2, 16]
332a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    // and Quality:]70, 100] -> Levels:]16, 256].
333a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    const int alpha_levels = (quality <= 70) ? (2 + quality / 5)
334a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                                             : (16 + (quality - 70) * 8);
335a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, &sse);
336a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
337a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
338a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (ok) {
3398b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    ok = ApplyFiltersAndEncode(quant_alpha, width, height, data_size, method,
3408b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                               filter, reduce_levels, effort_level, output,
3418b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                               output_size, pic->stats);
3428b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    if (pic->stats != NULL) {  // need stats?
3438b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      pic->stats->coded_size += (int)(*output_size);
3448b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      enc->sse_[3] = sse;
345a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    }
346466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  }
3478b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
34833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  WebPSafeFree(quant_alpha);
349a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  return ok;
350466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora}
351466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
352a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//------------------------------------------------------------------------------
353a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Main calls
354a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
3551e7bf8805bd030c19924a5306837ecd72c295751Vikas Arorastatic int CompressAlphaJob(VP8Encoder* const enc, void* dummy) {
3561e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  const WebPConfig* config = enc->config_;
3571e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  uint8_t* alpha_data = NULL;
3581e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  size_t alpha_size = 0;
3591e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  const int effort_level = config->method;  // maps to [0..6]
3601e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  const WEBP_FILTER_TYPE filter =
3611e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      (config->alpha_filtering == 0) ? WEBP_FILTER_NONE :
3621e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      (config->alpha_filtering == 1) ? WEBP_FILTER_FAST :
3631e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora                                       WEBP_FILTER_BEST;
3641e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression,
3651e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora                   filter, effort_level, &alpha_data, &alpha_size)) {
3661e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    return 0;
3671e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  }
3681e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  if (alpha_size != (uint32_t)alpha_size) {  // Sanity check.
36933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    WebPSafeFree(alpha_data);
3701e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    return 0;
3711e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  }
3721e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  enc->alpha_data_size_ = (uint32_t)alpha_size;
3731e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  enc->alpha_data_ = alpha_data;
3741e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  (void)dummy;
3751e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  return 1;
3761e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora}
3771e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora
378a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Aroravoid VP8EncInitAlpha(VP8Encoder* const enc) {
379a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_);
380466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  enc->alpha_data_ = NULL;
381466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  enc->alpha_data_size_ = 0;
3821e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  if (enc->thread_level_ > 0) {
3831e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    WebPWorker* const worker = &enc->alpha_worker_;
38433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    WebPGetWorkerInterface()->Init(worker);
3851e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    worker->data1 = enc;
3861e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    worker->data2 = NULL;
3871e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    worker->hook = (WebPWorkerHook)CompressAlphaJob;
3881e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  }
389466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora}
390466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
3911e7bf8805bd030c19924a5306837ecd72c295751Vikas Aroraint VP8EncStartAlpha(VP8Encoder* const enc) {
392466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  if (enc->has_alpha_) {
3931e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    if (enc->thread_level_ > 0) {
3941e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      WebPWorker* const worker = &enc->alpha_worker_;
39533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      // Makes sure worker is good to go.
39633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      if (!WebPGetWorkerInterface()->Reset(worker)) {
3971e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora        return 0;
3981e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      }
39933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      WebPGetWorkerInterface()->Launch(worker);
4001e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      return 1;
4011e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    } else {
4021e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      return CompressAlphaJob(enc, NULL);   // just do the job right away
403466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    }
4041e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  }
4051e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  return 1;
4061e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora}
4071e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora
4081e7bf8805bd030c19924a5306837ecd72c295751Vikas Aroraint VP8EncFinishAlpha(VP8Encoder* const enc) {
4091e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  if (enc->has_alpha_) {
4101e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    if (enc->thread_level_ > 0) {
4111e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      WebPWorker* const worker = &enc->alpha_worker_;
41233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      if (!WebPGetWorkerInterface()->Sync(worker)) return 0;  // error
413a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    }
414466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  }
415a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
416466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora}
417466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
4181e7bf8805bd030c19924a5306837ecd72c295751Vikas Aroraint VP8EncDeleteAlpha(VP8Encoder* const enc) {
4191e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  int ok = 1;
4201e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  if (enc->thread_level_ > 0) {
4211e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    WebPWorker* const worker = &enc->alpha_worker_;
42233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    // finish anything left in flight
42333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    ok = WebPGetWorkerInterface()->Sync(worker);
42433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    // still need to end the worker, even if !ok
42533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    WebPGetWorkerInterface()->End(worker);
4261e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  }
42733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  WebPSafeFree(enc->alpha_data_);
428466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  enc->alpha_data_ = NULL;
429466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  enc->alpha_data_size_ = 0;
430466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  enc->has_alpha_ = 0;
4311e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  return ok;
432466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora}
433466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
434