1a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Copyright 2010 Google Inc. All Rights Reserved.
29aea642eefa7a641ab8b89d953251939221d2719Eric Hassold//
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.
89aea642eefa7a641ab8b89d953251939221d2719Eric Hassold// -----------------------------------------------------------------------------
99aea642eefa7a641ab8b89d953251939221d2719Eric Hassold//
109aea642eefa7a641ab8b89d953251939221d2719Eric Hassold// Frame-reconstruction function. Memory allocation.
119aea642eefa7a641ab8b89d953251939221d2719Eric Hassold//
129aea642eefa7a641ab8b89d953251939221d2719Eric Hassold// Author: Skal (pascal.massimino@gmail.com)
139aea642eefa7a641ab8b89d953251939221d2719Eric Hassold
149aea642eefa7a641ab8b89d953251939221d2719Eric Hassold#include <stdlib.h>
15466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora#include "./vp8i.h"
16a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora#include "../utils/utils.h"
179aea642eefa7a641ab8b89d953251939221d2719Eric Hassold
189aea642eefa7a641ab8b89d953251939221d2719Eric Hassold#define ALIGN_MASK (32 - 1)
199aea642eefa7a641ab8b89d953251939221d2719Eric Hassold
208b720228d581a84fd173b6dcb2fa295b59db489aVikas Arorastatic void ReconstructRow(const VP8Decoder* const dec,
218b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                           const VP8ThreadContext* ctx);  // TODO(skal): remove
228b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
23466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//------------------------------------------------------------------------------
24a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Filtering
259aea642eefa7a641ab8b89d953251939221d2719Eric Hassold
26466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora// kFilterExtraRows[] = How many extra lines are needed on the MB boundary
27466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora// for caching, given a filtering level.
28466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora// Simple filter:  up to 2 luma samples are read and 1 is written.
29466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora// Complex filter: up to 4 luma samples are read and 3 are written. Same for
30466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//                 U/V, so it's 8 samples total (because of the 2x upsampling).
31466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arorastatic const uint8_t kFilterExtraRows[3] = { 0, 2, 8 };
329aea642eefa7a641ab8b89d953251939221d2719Eric Hassold
33466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arorastatic void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) {
34a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const VP8ThreadContext* const ctx = &dec->thread_ctx_;
358b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  const int cache_id = ctx->id_;
369aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  const int y_bps = dec->cache_y_stride_;
378b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  const VP8FInfo* const f_info = ctx->f_info_ + mb_x;
388b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  uint8_t* const y_dst = dec->cache_y_ + cache_id * 16 * y_bps + mb_x * 16;
39a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const int ilevel = f_info->f_ilevel_;
408b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  const int limit = f_info->f_limit_;
418b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (limit == 0) {
4203d5e34c70f174c16282b0efdc6bb9473df5f8f1Vikas Arora    return;
4303d5e34c70f174c16282b0efdc6bb9473df5f8f1Vikas Arora  }
448b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  assert(limit >= 3);
459aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  if (dec->filter_type_ == 1) {   // simple
469aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    if (mb_x > 0) {
479aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      VP8SimpleHFilter16(y_dst, y_bps, limit + 4);
489aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    }
49a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    if (f_info->f_inner_) {
509aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      VP8SimpleHFilter16i(y_dst, y_bps, limit);
519aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    }
529aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    if (mb_y > 0) {
539aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      VP8SimpleVFilter16(y_dst, y_bps, limit + 4);
549aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    }
55a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    if (f_info->f_inner_) {
569aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      VP8SimpleVFilter16i(y_dst, y_bps, limit);
579aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    }
589aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  } else {    // complex
599aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    const int uv_bps = dec->cache_uv_stride_;
608b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8;
618b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8;
628b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    const int hev_thresh = f_info->hev_thresh_;
639aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    if (mb_x > 0) {
649aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      VP8HFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh);
659aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      VP8HFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh);
669aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    }
67a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    if (f_info->f_inner_) {
689aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      VP8HFilter16i(y_dst, y_bps, limit, ilevel, hev_thresh);
699aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      VP8HFilter8i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh);
709aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    }
719aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    if (mb_y > 0) {
729aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      VP8VFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh);
739aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      VP8VFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh);
749aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    }
75a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    if (f_info->f_inner_) {
769aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      VP8VFilter16i(y_dst, y_bps, limit, ilevel, hev_thresh);
779aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      VP8VFilter8i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh);
789aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    }
799aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  }
809aea642eefa7a641ab8b89d953251939221d2719Eric Hassold}
819aea642eefa7a641ab8b89d953251939221d2719Eric Hassold
82a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Filter the decoded macroblock row (if needed)
83a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arorastatic void FilterRow(const VP8Decoder* const dec) {
84466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  int mb_x;
85a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const int mb_y = dec->thread_ctx_.mb_y_;
86a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(dec->thread_ctx_.filter_row_);
87466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) {
88a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    DoFilter(dec, mb_x, mb_y);
89466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  }
90466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora}
91466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
92466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//------------------------------------------------------------------------------
931e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora// Precompute the filtering strength for each segment and each i4x4/i16x16 mode.
94466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
951e7bf8805bd030c19924a5306837ecd72c295751Vikas Arorastatic void PrecomputeFilterStrengths(VP8Decoder* const dec) {
969aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  if (dec->filter_type_ > 0) {
971e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    int s;
981e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    const VP8FilterHeader* const hdr = &dec->filter_hdr_;
991e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
1001e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      int i4x4;
1011e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      // First, compute the initial level
1021e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      int base_level;
1031e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      if (dec->segment_hdr_.use_segment_) {
1041e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora        base_level = dec->segment_hdr_.filter_strength_[s];
1051e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora        if (!dec->segment_hdr_.absolute_delta_) {
1061e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora          base_level += hdr->level_;
1071e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora        }
1089aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      } else {
1091e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora        base_level = hdr->level_;
1109aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      }
1111e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      for (i4x4 = 0; i4x4 <= 1; ++i4x4) {
1121e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora        VP8FInfo* const info = &dec->fstrengths_[s][i4x4];
1131e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora        int level = base_level;
1141e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora        if (hdr->use_lf_delta_) {
1151e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora          // TODO(skal): only CURRENT is handled for now.
1161e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora          level += hdr->ref_lf_delta_[0];
1171e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora          if (i4x4) {
1181e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora            level += hdr->mode_lf_delta_[0];
1191e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora          }
1201e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora        }
1211e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora        level = (level < 0) ? 0 : (level > 63) ? 63 : level;
1228b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        if (level > 0) {
1238b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          int ilevel = level;
1248b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          if (hdr->sharpness_ > 0) {
1258b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora            if (hdr->sharpness_ > 4) {
1268b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora              ilevel >>= 2;
1278b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora            } else {
1288b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora              ilevel >>= 1;
1298b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora            }
1308b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora            if (ilevel > 9 - hdr->sharpness_) {
1318b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora              ilevel = 9 - hdr->sharpness_;
1328b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora            }
1331e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora          }
1348b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          if (ilevel < 1) ilevel = 1;
1358b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          info->f_ilevel_ = ilevel;
1368b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          info->f_limit_ = 2 * level + ilevel;
1378b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          info->hev_thresh_ = (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
1388b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        } else {
1398b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          info->f_limit_ = 0;  // no filtering
1401e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora        }
1418b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        info->f_inner_ = i4x4;
1429aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      }
1439aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    }
1449aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  }
1459aea642eefa7a641ab8b89d953251939221d2719Eric Hassold}
1469aea642eefa7a641ab8b89d953251939221d2719Eric Hassold
147466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//------------------------------------------------------------------------------
1488b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora// Dithering
1498b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
1508b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora#define DITHER_AMP_TAB_SIZE 12
1518b720228d581a84fd173b6dcb2fa295b59db489aVikas Arorastatic const int kQuantToDitherAmp[DITHER_AMP_TAB_SIZE] = {
1528b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  // roughly, it's dqm->uv_mat_[1]
1538b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  8, 7, 6, 4, 4, 2, 2, 2, 1, 1, 1, 1
1548b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora};
1558b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
1568b720228d581a84fd173b6dcb2fa295b59db489aVikas Aroravoid VP8InitDithering(const WebPDecoderOptions* const options,
1578b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                      VP8Decoder* const dec) {
1588b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  assert(dec != NULL);
1598b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (options != NULL) {
1608b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    const int d = options->dithering_strength;
1618b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    const int max_amp = (1 << VP8_RANDOM_DITHER_FIX) - 1;
1628b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    const int f = (d < 0) ? 0 : (d > 100) ? max_amp : (d * max_amp / 100);
1638b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    if (f > 0) {
1648b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      int s;
1658b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      int all_amp = 0;
1668b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
1678b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        VP8QuantMatrix* const dqm = &dec->dqm_[s];
1688b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        if (dqm->uv_quant_ < DITHER_AMP_TAB_SIZE) {
1698b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          // TODO(skal): should we specially dither more for uv_quant_ < 0?
1708b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          const int idx = (dqm->uv_quant_ < 0) ? 0 : dqm->uv_quant_;
1718b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          dqm->dither_ = (f * kQuantToDitherAmp[idx]) >> 3;
1728b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        }
1738b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        all_amp |= dqm->dither_;
1748b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      }
1758b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      if (all_amp != 0) {
1768b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        VP8InitRandom(&dec->dithering_rg_, 1.0f);
1778b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        dec->dither_ = 1;
1788b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      }
1798b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    }
1808c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora#if WEBP_DECODER_ABI_VERSION > 0x0204
18133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    // potentially allow alpha dithering
18233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    dec->alpha_dithering_ = options->alpha_dithering_strength;
18333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    if (dec->alpha_dithering_ > 100) {
18433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      dec->alpha_dithering_ = 100;
18533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    } else if (dec->alpha_dithering_ < 0) {
18633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      dec->alpha_dithering_ = 0;
18733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    }
18833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora#endif
1898b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  }
1908b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora}
1918b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
1928b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora// minimal amp that will provide a non-zero dithering effect
1938b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora#define MIN_DITHER_AMP 4
1948b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora#define DITHER_DESCALE 4
1958b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora#define DITHER_DESCALE_ROUNDER (1 << (DITHER_DESCALE - 1))
1968b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora#define DITHER_AMP_BITS 8
1978b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora#define DITHER_AMP_CENTER (1 << DITHER_AMP_BITS)
1988b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
1998b720228d581a84fd173b6dcb2fa295b59db489aVikas Arorastatic void Dither8x8(VP8Random* const rg, uint8_t* dst, int bps, int amp) {
2008b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  int i, j;
2018b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  for (j = 0; j < 8; ++j) {
2028b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    for (i = 0; i < 8; ++i) {
2038b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      // TODO: could be made faster with SSE2
2048b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      const int bits =
2058b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          VP8RandomBits2(rg, DITHER_AMP_BITS + 1, amp) - DITHER_AMP_CENTER;
2068b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      // Convert to range: [-2,2] for dither=50, [-4,4] for dither=100
2078b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      const int delta = (bits + DITHER_DESCALE_ROUNDER) >> DITHER_DESCALE;
2088b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      const int v = (int)dst[i] + delta;
2098b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      dst[i] = (v < 0) ? 0 : (v > 255) ? 255u : (uint8_t)v;
2108b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    }
2118b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    dst += bps;
2128b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  }
2138b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora}
2148b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
2158b720228d581a84fd173b6dcb2fa295b59db489aVikas Arorastatic void DitherRow(VP8Decoder* const dec) {
2168b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  int mb_x;
2178b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  assert(dec->dither_);
2188b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) {
2198b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    const VP8ThreadContext* const ctx = &dec->thread_ctx_;
2208b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    const VP8MBData* const data = ctx->mb_data_ + mb_x;
2218b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    const int cache_id = ctx->id_;
2228b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    const int uv_bps = dec->cache_uv_stride_;
2238b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    if (data->dither_ >= MIN_DITHER_AMP) {
2248b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8;
2258b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8;
2268b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      Dither8x8(&dec->dithering_rg_, u_dst, uv_bps, data->dither_);
2278b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      Dither8x8(&dec->dithering_rg_, v_dst, uv_bps, data->dither_);
2288b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    }
2298b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  }
2308b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora}
2318b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
2328b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora//------------------------------------------------------------------------------
233466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora// This function is called after a row of macroblocks is finished decoding.
234466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora// It also takes into account the following restrictions:
235466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//  * In case of in-loop filtering, we must hold off sending some of the bottom
236466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//    pixels as they are yet unfiltered. They will be when the next macroblock
237466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//    row is decoded. Meanwhile, we must preserve them by rotating them in the
238466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//    cache area. This doesn't hold for the very bottom row of the uncropped
239466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//    picture of course.
240466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//  * we must clip the remaining pixels against the cropping area. The VP8Io
241466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//    struct must have the following fields set correctly before calling put():
242466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
243466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora#define MACROBLOCK_VPOS(mb_y)  ((mb_y) * 16)    // vertical position of a MB
244466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
245a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Finalize and transmit a complete row. Return false in case of user-abort.
246a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arorastatic int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
247a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  int ok = 1;
248a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const VP8ThreadContext* const ctx = &dec->thread_ctx_;
2498b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  const int cache_id = ctx->id_;
2509aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  const int extra_y_rows = kFilterExtraRows[dec->filter_type_];
2519aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  const int ysize = extra_y_rows * dec->cache_y_stride_;
2529aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  const int uvsize = (extra_y_rows / 2) * dec->cache_uv_stride_;
2538b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  const int y_offset = cache_id * 16 * dec->cache_y_stride_;
2548b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  const int uv_offset = cache_id * 8 * dec->cache_uv_stride_;
255a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  uint8_t* const ydst = dec->cache_y_ - ysize + y_offset;
256a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  uint8_t* const udst = dec->cache_u_ - uvsize + uv_offset;
257a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  uint8_t* const vdst = dec->cache_v_ - uvsize + uv_offset;
2588b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  const int mb_y = ctx->mb_y_;
2598b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  const int is_first_row = (mb_y == 0);
2608b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  const int is_last_row = (mb_y >= dec->br_mb_y_ - 1);
2618b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
2628b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (dec->mt_method_ == 2) {
2638b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    ReconstructRow(dec, ctx);
2648b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  }
265a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
266a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (ctx->filter_row_) {
267a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    FilterRow(dec);
268a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
269a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
2708b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (dec->dither_) {
2718b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    DitherRow(dec);
2728b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  }
2738b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
2748b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (io->put != NULL) {
2758b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    int y_start = MACROBLOCK_VPOS(mb_y);
2768b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    int y_end = MACROBLOCK_VPOS(mb_y + 1);
2778b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    if (!is_first_row) {
2789aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      y_start -= extra_y_rows;
2799aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      io->y = ydst;
2809aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      io->u = udst;
2819aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      io->v = vdst;
2829aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    } else {
283a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      io->y = dec->cache_y_ + y_offset;
284a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      io->u = dec->cache_u_ + uv_offset;
285a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      io->v = dec->cache_v_ + uv_offset;
2869aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    }
287466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
2888b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    if (!is_last_row) {
2899aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      y_end -= extra_y_rows;
2909aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    }
291466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    if (y_end > io->crop_bottom) {
292466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      y_end = io->crop_bottom;    // make sure we don't overflow on last row.
2939aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    }
294466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    io->a = NULL;
295a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    if (dec->alpha_data_ != NULL && y_start < y_end) {
2968b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      // TODO(skal): testing presence of alpha with dec->alpha_data_ is not a
2978b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      // good idea.
298466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      io->a = VP8DecompressAlphaRows(dec, y_start, y_end - y_start);
299466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      if (io->a == NULL) {
300466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora        return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
301466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora                           "Could not decode alpha data.");
302466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      }
303466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    }
304466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    if (y_start < io->crop_top) {
305466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      const int delta_y = io->crop_top - y_start;
306466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      y_start = io->crop_top;
307466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      assert(!(delta_y & 1));
308466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      io->y += dec->cache_y_stride_ * delta_y;
309466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      io->u += dec->cache_uv_stride_ * (delta_y >> 1);
310466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      io->v += dec->cache_uv_stride_ * (delta_y >> 1);
311a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      if (io->a != NULL) {
312466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora        io->a += io->width * delta_y;
313466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      }
314466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    }
315466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    if (y_start < y_end) {
316466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      io->y += io->crop_left;
317466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      io->u += io->crop_left >> 1;
318466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      io->v += io->crop_left >> 1;
319a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      if (io->a != NULL) {
320466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora        io->a += io->crop_left;
321466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      }
322466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      io->mb_y = y_start - io->crop_top;
323466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      io->mb_w = io->crop_right - io->crop_left;
324466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      io->mb_h = y_end - y_start;
325a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      ok = io->put(io);
32603d5e34c70f174c16282b0efdc6bb9473df5f8f1Vikas Arora    }
3279aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  }
328a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  // rotate top samples if needed
3298b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (cache_id + 1 == dec->num_caches_) {
3308b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    if (!is_last_row) {
331a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      memcpy(dec->cache_y_ - ysize, ydst + 16 * dec->cache_y_stride_, ysize);
332a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      memcpy(dec->cache_u_ - uvsize, udst + 8 * dec->cache_uv_stride_, uvsize);
333a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      memcpy(dec->cache_v_ - uvsize, vdst + 8 * dec->cache_uv_stride_, uvsize);
334a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    }
3359aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  }
336a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
337a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  return ok;
3389aea642eefa7a641ab8b89d953251939221d2719Eric Hassold}
3399aea642eefa7a641ab8b89d953251939221d2719Eric Hassold
340466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora#undef MACROBLOCK_VPOS
341466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
342466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//------------------------------------------------------------------------------
343a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
344a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Aroraint VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) {
345a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  int ok = 1;
346a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  VP8ThreadContext* const ctx = &dec->thread_ctx_;
3478b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  const int filter_row =
3488b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      (dec->filter_type_ > 0) &&
3498b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      (dec->mb_y_ >= dec->tl_mb_y_) && (dec->mb_y_ <= dec->br_mb_y_);
3508b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (dec->mt_method_ == 0) {
351a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    // ctx->id_ and ctx->f_info_ are already set
352a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    ctx->mb_y_ = dec->mb_y_;
3538b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    ctx->filter_row_ = filter_row;
3548b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    ReconstructRow(dec, ctx);
355a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    ok = FinishRow(dec, io);
356a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  } else {
357a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    WebPWorker* const worker = &dec->worker_;
358a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    // Finish previous job *before* updating context
35933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    ok &= WebPGetWorkerInterface()->Sync(worker);
360a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    assert(worker->status_ == OK);
361a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    if (ok) {   // spawn a new deblocking/output job
362a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      ctx->io_ = *io;
363a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      ctx->id_ = dec->cache_id_;
364a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      ctx->mb_y_ = dec->mb_y_;
3658b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      ctx->filter_row_ = filter_row;
3668b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      if (dec->mt_method_ == 2) {  // swap macroblock data
3678b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        VP8MBData* const tmp = ctx->mb_data_;
3688b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        ctx->mb_data_ = dec->mb_data_;
3698b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        dec->mb_data_ = tmp;
3708b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      } else {
3718b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        // perform reconstruction directly in main thread
3728b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        ReconstructRow(dec, ctx);
3738b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      }
3748b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      if (filter_row) {            // swap filter info
375a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora        VP8FInfo* const tmp = ctx->f_info_;
376a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora        ctx->f_info_ = dec->f_info_;
377a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora        dec->f_info_ = tmp;
378a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      }
37933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      // (reconstruct)+filter in parallel
38033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      WebPGetWorkerInterface()->Launch(worker);
381a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      if (++dec->cache_id_ == dec->num_caches_) {
382a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora        dec->cache_id_ = 0;
383a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      }
384a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    }
385a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
386a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  return ok;
387a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora}
388a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
389a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//------------------------------------------------------------------------------
390466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora// Finish setting up the decoding parameter once user's setup() is called.
391466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
392a2415724fb3466168b2af5b08bd94ba732c0e753Vikas AroraVP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
393466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  // Call setup() first. This may trigger additional decoding features on 'io'.
3948b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  // Note: Afterward, we must call teardown() no matter what.
3958b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (io->setup != NULL && !io->setup(io)) {
396466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    VP8SetError(dec, VP8_STATUS_USER_ABORT, "Frame setup failed");
397466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    return dec->status_;
398466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  }
399466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
400466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  // Disable filtering per user request
401466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  if (io->bypass_filtering) {
402466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    dec->filter_type_ = 0;
403466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  }
404466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  // TODO(skal): filter type / strength / sharpness forcing
405466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
406466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  // Define the area where we can skip in-loop filtering, in case of cropping.
407466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  //
4088b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  // 'Simple' filter reads two luma samples outside of the macroblock
409466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  // and filters one. It doesn't filter the chroma samples. Hence, we can
410466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  // avoid doing the in-loop filtering before crop_top/crop_left position.
411466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  // For the 'Complex' filter, 3 samples are read and up to 3 are filtered.
412466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  // Means: there's a dependency chain that goes all the way up to the
413466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  // top-left corner of the picture (MB #0). We must filter all the previous
414466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  // macroblocks.
415466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  // TODO(skal): add an 'approximate_decoding' option, that won't produce
416466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  // a 1:1 bit-exactness for complex filtering?
417466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  {
418466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    const int extra_pixels = kFilterExtraRows[dec->filter_type_];
419466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    if (dec->filter_type_ == 2) {
420466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      // For complex filter, we need to preserve the dependency chain.
421466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      dec->tl_mb_x_ = 0;
422466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      dec->tl_mb_y_ = 0;
423466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    } else {
424466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      // For simple filter, we can filter only the cropped region.
425a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      // We include 'extra_pixels' on the other side of the boundary, since
426a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      // vertical or horizontal filtering of the previous macroblock can
427a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      // modify some abutting pixels.
428a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      dec->tl_mb_x_ = (io->crop_left - extra_pixels) >> 4;
429a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      dec->tl_mb_y_ = (io->crop_top - extra_pixels) >> 4;
430a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      if (dec->tl_mb_x_ < 0) dec->tl_mb_x_ = 0;
431a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      if (dec->tl_mb_y_ < 0) dec->tl_mb_y_ = 0;
432466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    }
433466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    // We need some 'extra' pixels on the right/bottom.
434466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    dec->br_mb_y_ = (io->crop_bottom + 15 + extra_pixels) >> 4;
435466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    dec->br_mb_x_ = (io->crop_right + 15 + extra_pixels) >> 4;
436466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    if (dec->br_mb_x_ > dec->mb_w_) {
437466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      dec->br_mb_x_ = dec->mb_w_;
438466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    }
439466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    if (dec->br_mb_y_ > dec->mb_h_) {
440466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora      dec->br_mb_y_ = dec->mb_h_;
441466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    }
442466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  }
4431e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  PrecomputeFilterStrengths(dec);
444466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  return VP8_STATUS_OK;
445466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora}
446466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
447a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Aroraint VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) {
448a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  int ok = 1;
4498b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (dec->mt_method_ > 0) {
45033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    ok = WebPGetWorkerInterface()->Sync(&dec->worker_);
451a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
452a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
4538b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (io->teardown != NULL) {
454a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    io->teardown(io);
455a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
456a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  return ok;
457a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora}
458a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
459a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//------------------------------------------------------------------------------
460a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// For multi-threaded decoding we need to use 3 rows of 16 pixels as delay line.
461a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//
462a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Reason is: the deblocking filter cannot deblock the bottom horizontal edges
463a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// immediately, and needs to wait for first few rows of the next macroblock to
464a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// be decoded. Hence, deblocking is lagging behind by 4 or 8 pixels (depending
465a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// on strength).
466a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// With two threads, the vertical positions of the rows being decoded are:
467a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Decode:  [ 0..15][16..31][32..47][48..63][64..79][...
468a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Deblock:         [ 0..11][12..27][28..43][44..59][...
469a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// If we use two threads and two caches of 16 pixels, the sequence would be:
470a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Decode:  [ 0..15][16..31][ 0..15!!][16..31][ 0..15][...
471a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Deblock:         [ 0..11][12..27!!][-4..11][12..27][...
472a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// The problem occurs during row [12..15!!] that both the decoding and
473a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// deblocking threads are writing simultaneously.
474a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// With 3 cache lines, one get a safe write pattern:
475a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Decode:  [ 0..15][16..31][32..47][ 0..15][16..31][32..47][0..
476a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Deblock:         [ 0..11][12..27][28..43][-4..11][12..27][28...
477a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Note that multi-threaded output _without_ deblocking can make use of two
478a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// cache lines of 16 pixels only, since there's no lagging behind. The decoding
479a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// and output process have non-concurrent writing:
480a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Decode:  [ 0..15][16..31][ 0..15][16..31][...
481a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// io->put:         [ 0..15][16..31][ 0..15][...
482a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
483a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora#define MT_CACHE_LINES 3
484a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora#define ST_CACHE_LINES 1   // 1 cache row only for single-threaded case
485a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
486a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Initialize multi/single-thread worker
487a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arorastatic int InitThreadContext(VP8Decoder* const dec) {
488a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  dec->cache_id_ = 0;
4898b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (dec->mt_method_ > 0) {
490a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    WebPWorker* const worker = &dec->worker_;
49133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    if (!WebPGetWorkerInterface()->Reset(worker)) {
492a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
493a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                         "thread initialization failed.");
494a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    }
495a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    worker->data1 = dec;
496a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    worker->data2 = (void*)&dec->thread_ctx_.io_;
497a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    worker->hook = (WebPWorkerHook)FinishRow;
498a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    dec->num_caches_ =
499a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      (dec->filter_type_ > 0) ? MT_CACHE_LINES : MT_CACHE_LINES - 1;
500a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  } else {
501a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    dec->num_caches_ = ST_CACHE_LINES;
502a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
503a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  return 1;
504a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora}
505a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
5068b720228d581a84fd173b6dcb2fa295b59db489aVikas Aroraint VP8GetThreadMethod(const WebPDecoderOptions* const options,
5078b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                       const WebPHeaderStructure* const headers,
5088b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                       int width, int height) {
5098b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (options == NULL || options->use_threads == 0) {
5108b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    return 0;
5118b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  }
5128b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  (void)headers;
5138b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  (void)width;
5148b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  (void)height;
51533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  assert(headers == NULL || !headers->is_lossless);
5168b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora#if defined(WEBP_USE_THREAD)
5178b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (width < MIN_WIDTH_FOR_THREADS) return 0;
5188b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  // TODO(skal): tune the heuristic further
5198b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora#if 0
5208b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (height < 2 * width) return 2;
5218b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora#endif
5228b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  return 2;
5238b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora#else   // !WEBP_USE_THREAD
5248b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  return 0;
5258b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora#endif
5268b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora}
5278b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
528a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora#undef MT_CACHE_LINES
529a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora#undef ST_CACHE_LINES
530a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
531a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//------------------------------------------------------------------------------
532a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Memory setup
533a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
534a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arorastatic int AllocateMemory(VP8Decoder* const dec) {
535a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const int num_caches = dec->num_caches_;
536a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const int mb_w = dec->mb_w_;
537a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  // Note: we use 'size_t' when there's no overflow risk, uint64_t otherwise.
538a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const size_t intra_pred_mode_size = 4 * mb_w * sizeof(uint8_t);
5398b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  const size_t top_size = sizeof(VP8TopSamples) * mb_w;
540a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const size_t mb_info_size = (mb_w + 1) * sizeof(VP8MB);
541a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const size_t f_info_size =
542a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      (dec->filter_type_ > 0) ?
5438b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          mb_w * (dec->mt_method_ > 0 ? 2 : 1) * sizeof(VP8FInfo)
544a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora        : 0;
545a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b_);
5468b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  const size_t mb_data_size =
5478b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      (dec->mt_method_ == 2 ? 2 : 1) * mb_w * sizeof(*dec->mb_data_);
548a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const size_t cache_height = (16 * num_caches
549a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                            + kFilterExtraRows[dec->filter_type_]) * 3 / 2;
550a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const size_t cache_size = top_size * cache_height;
551a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  // alpha_size is the only one that scales as width x height.
552a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const uint64_t alpha_size = (dec->alpha_data_ != NULL) ?
553a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      (uint64_t)dec->pic_hdr_.width_ * dec->pic_hdr_.height_ : 0ULL;
554a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const uint64_t needed = (uint64_t)intra_pred_mode_size
555a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                        + top_size + mb_info_size + f_info_size
5568b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                        + yuv_size + mb_data_size
557a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                        + cache_size + alpha_size + ALIGN_MASK;
558a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  uint8_t* mem;
559a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
560a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (needed != (size_t)needed) return 0;  // check for overflow
561a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (needed > dec->mem_size_) {
56233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    WebPSafeFree(dec->mem_);
563a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    dec->mem_size_ = 0;
564a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    dec->mem_ = WebPSafeMalloc(needed, sizeof(uint8_t));
565a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    if (dec->mem_ == NULL) {
566a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
567a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                         "no memory during frame initialization.");
568a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    }
569a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    // down-cast is ok, thanks to WebPSafeAlloc() above.
570a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    dec->mem_size_ = (size_t)needed;
571a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
572a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
573a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  mem = (uint8_t*)dec->mem_;
574a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  dec->intra_t_ = (uint8_t*)mem;
575a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  mem += intra_pred_mode_size;
576a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
5778b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  dec->yuv_t_ = (VP8TopSamples*)mem;
5788b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  mem += top_size;
579a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
580a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  dec->mb_info_ = ((VP8MB*)mem) + 1;
581a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  mem += mb_info_size;
582a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
583a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  dec->f_info_ = f_info_size ? (VP8FInfo*)mem : NULL;
584a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  mem += f_info_size;
585a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  dec->thread_ctx_.id_ = 0;
586a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  dec->thread_ctx_.f_info_ = dec->f_info_;
5878b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (dec->mt_method_ > 0) {
588a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    // secondary cache line. The deblocking process need to make use of the
589a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    // filtering strength from previous macroblock row, while the new ones
590a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    // are being decoded in parallel. We'll just swap the pointers.
591a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    dec->thread_ctx_.f_info_ += mb_w;
592a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
593a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
594a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  mem = (uint8_t*)((uintptr_t)(mem + ALIGN_MASK) & ~ALIGN_MASK);
595a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert((yuv_size & ALIGN_MASK) == 0);
596a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  dec->yuv_b_ = (uint8_t*)mem;
597a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  mem += yuv_size;
598a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
5998b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  dec->mb_data_ = (VP8MBData*)mem;
6008b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  dec->thread_ctx_.mb_data_ = (VP8MBData*)mem;
6018b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (dec->mt_method_ == 2) {
6028b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    dec->thread_ctx_.mb_data_ += mb_w;
6038b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  }
6048b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  mem += mb_data_size;
605a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
606a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  dec->cache_y_stride_ = 16 * mb_w;
607a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  dec->cache_uv_stride_ = 8 * mb_w;
608a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  {
609a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    const int extra_rows = kFilterExtraRows[dec->filter_type_];
610a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    const int extra_y = extra_rows * dec->cache_y_stride_;
611a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    const int extra_uv = (extra_rows / 2) * dec->cache_uv_stride_;
612a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    dec->cache_y_ = ((uint8_t*)mem) + extra_y;
613a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    dec->cache_u_ = dec->cache_y_
614a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                  + 16 * num_caches * dec->cache_y_stride_ + extra_uv;
615a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    dec->cache_v_ = dec->cache_u_
616a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                  + 8 * num_caches * dec->cache_uv_stride_ + extra_uv;
617a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    dec->cache_id_ = 0;
618a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
619a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  mem += cache_size;
620a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
621a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  // alpha plane
622a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  dec->alpha_plane_ = alpha_size ? (uint8_t*)mem : NULL;
623a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  mem += alpha_size;
6241e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  assert(mem <= (uint8_t*)dec->mem_ + dec->mem_size_);
625a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
6268b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  // note: left/top-info is initialized once for all.
627a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  memset(dec->mb_info_ - 1, 0, mb_info_size);
6288b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  VP8InitScanline(dec);   // initialize left too.
629a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
630a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  // initialize top
631a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  memset(dec->intra_t_, B_DC_PRED, intra_pred_mode_size);
632a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
633a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  return 1;
634a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora}
635a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
636a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arorastatic void InitIo(VP8Decoder* const dec, VP8Io* io) {
637a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  // prepare 'io'
638a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  io->mb_y = 0;
639a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  io->y = dec->cache_y_;
640a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  io->u = dec->cache_u_;
641a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  io->v = dec->cache_v_;
642a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  io->y_stride = dec->cache_y_stride_;
643a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  io->uv_stride = dec->cache_uv_stride_;
644a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  io->a = NULL;
645a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora}
646a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
647a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Aroraint VP8InitFrame(VP8Decoder* const dec, VP8Io* io) {
648a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (!InitThreadContext(dec)) return 0;  // call first. Sets dec->num_caches_.
649a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (!AllocateMemory(dec)) return 0;
650a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  InitIo(dec, io);
651a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  VP8DspInit();  // Init critical function pointers and look-up tables.
652a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  return 1;
653a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora}
654a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
655466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//------------------------------------------------------------------------------
6569aea642eefa7a641ab8b89d953251939221d2719Eric Hassold// Main reconstruction function.
6579aea642eefa7a641ab8b89d953251939221d2719Eric Hassold
6589aea642eefa7a641ab8b89d953251939221d2719Eric Hassoldstatic const int kScan[16] = {
6599aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  0 +  0 * BPS,  4 +  0 * BPS, 8 +  0 * BPS, 12 +  0 * BPS,
6609aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  0 +  4 * BPS,  4 +  4 * BPS, 8 +  4 * BPS, 12 +  4 * BPS,
6619aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  0 +  8 * BPS,  4 +  8 * BPS, 8 +  8 * BPS, 12 +  8 * BPS,
6629aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  0 + 12 * BPS,  4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS
6639aea642eefa7a641ab8b89d953251939221d2719Eric Hassold};
6649aea642eefa7a641ab8b89d953251939221d2719Eric Hassold
6658b720228d581a84fd173b6dcb2fa295b59db489aVikas Arorastatic int CheckMode(int mb_x, int mb_y, int mode) {
6669aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  if (mode == B_DC_PRED) {
6678b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    if (mb_x == 0) {
6688b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      return (mb_y == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT;
6699aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    } else {
6708b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      return (mb_y == 0) ? B_DC_PRED_NOTOP : B_DC_PRED;
6719aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    }
6729aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  }
6739aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  return mode;
6749aea642eefa7a641ab8b89d953251939221d2719Eric Hassold}
6759aea642eefa7a641ab8b89d953251939221d2719Eric Hassold
6768b720228d581a84fd173b6dcb2fa295b59db489aVikas Arorastatic void Copy32b(uint8_t* dst, uint8_t* src) {
6778b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  memcpy(dst, src, 4);
6788b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora}
6798b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
6808b720228d581a84fd173b6dcb2fa295b59db489aVikas Arorastatic WEBP_INLINE void DoTransform(uint32_t bits, const int16_t* const src,
6818b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                                    uint8_t* const dst) {
6828b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  switch (bits >> 30) {
6838b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    case 3:
6848b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      VP8Transform(src, dst, 0);
6858b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      break;
6868b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    case 2:
6878b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      VP8TransformAC3(src, dst);
6888b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      break;
6898b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    case 1:
6908b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      VP8TransformDC(src, dst);
6918b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      break;
6928b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    default:
6938b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      break;
6948b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  }
6959aea642eefa7a641ab8b89d953251939221d2719Eric Hassold}
6969aea642eefa7a641ab8b89d953251939221d2719Eric Hassold
6978b720228d581a84fd173b6dcb2fa295b59db489aVikas Arorastatic void DoUVTransform(uint32_t bits, const int16_t* const src,
6988b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                          uint8_t* const dst) {
6998b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  if (bits & 0xff) {    // any non-zero coeff at all?
7008b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    if (bits & 0xaa) {  // any non-zero AC coefficient?
7018b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      VP8TransformUV(src, dst);   // note we don't use the AC3 variant for U/V
7028b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    } else {
7038b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      VP8TransformDCUV(src, dst);
7048b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    }
7058b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  }
7068b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora}
7078b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
7088b720228d581a84fd173b6dcb2fa295b59db489aVikas Arorastatic void ReconstructRow(const VP8Decoder* const dec,
7098b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                           const VP8ThreadContext* ctx) {
7101e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  int j;
7118b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  int mb_x;
7128b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  const int mb_y = ctx->mb_y_;
7138b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  const int cache_id = ctx->id_;
7149aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  uint8_t* const y_dst = dec->yuv_b_ + Y_OFF;
7159aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  uint8_t* const u_dst = dec->yuv_b_ + U_OFF;
7169aea642eefa7a641ab8b89d953251939221d2719Eric Hassold  uint8_t* const v_dst = dec->yuv_b_ + V_OFF;
7178b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) {
7188b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    const VP8MBData* const block = ctx->mb_data_ + mb_x;
7199aea642eefa7a641ab8b89d953251939221d2719Eric Hassold
7208b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    // Rotate in the left samples from previously decoded block. We move four
7218b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    // pixels at a time for alignment reason, and because of in-loop filter.
7228b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    if (mb_x > 0) {
7238b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      for (j = -1; j < 16; ++j) {
7248b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        Copy32b(&y_dst[j * BPS - 4], &y_dst[j * BPS + 12]);
7258b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      }
7268b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      for (j = -1; j < 8; ++j) {
7278b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        Copy32b(&u_dst[j * BPS - 4], &u_dst[j * BPS + 4]);
7288b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        Copy32b(&v_dst[j * BPS - 4], &v_dst[j * BPS + 4]);
7298b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      }
7308b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    } else {
7318b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      for (j = 0; j < 16; ++j) {
7328b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        y_dst[j * BPS - 1] = 129;
7338b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      }
7348b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      for (j = 0; j < 8; ++j) {
7358b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        u_dst[j * BPS - 1] = 129;
7368b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        v_dst[j * BPS - 1] = 129;
7378b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      }
7388b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      // Init top-left sample on left column too
7398b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      if (mb_y > 0) {
7408b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        y_dst[-1 - BPS] = u_dst[-1 - BPS] = v_dst[-1 - BPS] = 129;
7418b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      }
7429aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    }
7438b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    {
7448b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      // bring top samples into the cache
7458b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      VP8TopSamples* const top_yuv = dec->yuv_t_ + mb_x;
7468b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      const int16_t* const coeffs = block->coeffs_;
7478b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      uint32_t bits = block->non_zero_y_;
7488b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      int n;
7498b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
7508b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      if (mb_y > 0) {
7518b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        memcpy(y_dst - BPS, top_yuv[0].y, 16);
7528b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        memcpy(u_dst - BPS, top_yuv[0].u, 8);
7538b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        memcpy(v_dst - BPS, top_yuv[0].v, 8);
7548b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      } else if (mb_x == 0) {
7558b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        // we only need to do this init once at block (0,0).
7568b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        // Afterward, it remains valid for the whole topmost row.
7578b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        memset(y_dst - BPS - 1, 127, 16 + 4 + 1);
7588b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        memset(u_dst - BPS - 1, 127, 8 + 1);
7598b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        memset(v_dst - BPS - 1, 127, 8 + 1);
7608b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      }
7619aea642eefa7a641ab8b89d953251939221d2719Eric Hassold
7628b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      // predict and add residuals
7638b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      if (block->is_i4x4_) {   // 4x4
7648b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        uint32_t* const top_right = (uint32_t*)(y_dst - BPS + 16);
7659aea642eefa7a641ab8b89d953251939221d2719Eric Hassold
7668b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        if (mb_y > 0) {
7678b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          if (mb_x >= dec->mb_w_ - 1) {    // on rightmost border
7688b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora            memset(top_right, top_yuv[0].y[15], sizeof(*top_right));
7698b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          } else {
7708b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora            memcpy(top_right, top_yuv[1].y, sizeof(*top_right));
7718b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          }
7729aea642eefa7a641ab8b89d953251939221d2719Eric Hassold        }
7738b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        // replicate the top-right pixels below
7748b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        top_right[BPS] = top_right[2 * BPS] = top_right[3 * BPS] = top_right[0];
7758b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora
7768b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        // predict and add residuals for all 4x4 blocks in turn.
7778b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        for (n = 0; n < 16; ++n, bits <<= 2) {
7789aea642eefa7a641ab8b89d953251939221d2719Eric Hassold          uint8_t* const dst = y_dst + kScan[n];
7798b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          VP8PredLuma4[block->imodes_[n]](dst);
7808b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          DoTransform(bits, coeffs + n * 16, dst);
7819aea642eefa7a641ab8b89d953251939221d2719Eric Hassold        }
7828b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      } else {    // 16x16
7838b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        const int pred_func = CheckMode(mb_x, mb_y,
7848b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora                                        block->imodes_[0]);
7858b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        VP8PredLuma16[pred_func](y_dst);
7868b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        if (bits != 0) {
7878b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          for (n = 0; n < 16; ++n, bits <<= 2) {
7888b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora            DoTransform(bits, coeffs + n * 16, y_dst + kScan[n]);
7898b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora          }
7909aea642eefa7a641ab8b89d953251939221d2719Eric Hassold        }
7919aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      }
7928b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      {
7938b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        // Chroma
7948b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        const uint32_t bits_uv = block->non_zero_uv_;
7958b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        const int pred_func = CheckMode(mb_x, mb_y, block->uvmode_);
7968b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        VP8PredChroma8[pred_func](u_dst);
7978b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        VP8PredChroma8[pred_func](v_dst);
7988b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        DoUVTransform(bits_uv >> 0, coeffs + 16 * 16, u_dst);
7998b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        DoUVTransform(bits_uv >> 8, coeffs + 20 * 16, v_dst);
8009aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      }
8019aea642eefa7a641ab8b89d953251939221d2719Eric Hassold
8029aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      // stash away top samples for next block
8038b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      if (mb_y < dec->mb_h_ - 1) {
8048b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        memcpy(top_yuv[0].y, y_dst + 15 * BPS, 16);
8058b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        memcpy(top_yuv[0].u, u_dst +  7 * BPS,  8);
8068b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        memcpy(top_yuv[0].v, v_dst +  7 * BPS,  8);
8079aea642eefa7a641ab8b89d953251939221d2719Eric Hassold      }
8089aea642eefa7a641ab8b89d953251939221d2719Eric Hassold    }
8098b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    // Transfer reconstructed samples from yuv_b_ cache to final destination.
8108b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora    {
8118b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      const int y_offset = cache_id * 16 * dec->cache_y_stride_;
8128b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      const int uv_offset = cache_id * 8 * dec->cache_uv_stride_;
8138b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      uint8_t* const y_out = dec->cache_y_ + mb_x * 16 + y_offset;
8148b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      uint8_t* const u_out = dec->cache_u_ + mb_x * 8 + uv_offset;
8158b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      uint8_t* const v_out = dec->cache_v_ + mb_x * 8 + uv_offset;
8168b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      for (j = 0; j < 16; ++j) {
8178b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        memcpy(y_out + j * dec->cache_y_stride_, y_dst + j * BPS, 16);
8188b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      }
8198b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      for (j = 0; j < 8; ++j) {
8208b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        memcpy(u_out + j * dec->cache_uv_stride_, u_dst + j * BPS, 8);
8218b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora        memcpy(v_out + j * dec->cache_uv_stride_, v_dst + j * BPS, 8);
8228b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora      }
8231e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    }
8241e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  }
8259aea642eefa7a641ab8b89d953251939221d2719Eric Hassold}
8269aea642eefa7a641ab8b89d953251939221d2719Eric Hassold
827466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//------------------------------------------------------------------------------
8289aea642eefa7a641ab8b89d953251939221d2719Eric Hassold
829