2014-01-24 10:37:15 +00:00
|
|
|
/*****************************************************************************
|
|
|
|
* This file is part of Kvazaar HEVC encoder.
|
2014-02-21 13:00:20 +00:00
|
|
|
*
|
2015-02-23 11:18:48 +00:00
|
|
|
* Copyright (C) 2013-2015 Tampere University of Technology and others (see
|
2014-01-24 10:37:15 +00:00
|
|
|
* COPYING file).
|
|
|
|
*
|
2015-02-23 11:18:48 +00:00
|
|
|
* Kvazaar is free software: you can redistribute it and/or modify it under
|
|
|
|
* the terms of the GNU Lesser General Public License as published by the
|
|
|
|
* Free Software Foundation; either version 2.1 of the License, or (at your
|
|
|
|
* option) any later version.
|
2014-01-24 10:37:15 +00:00
|
|
|
*
|
2015-02-23 11:18:48 +00:00
|
|
|
* Kvazaar is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
|
|
|
* more details.
|
2014-01-24 10:37:15 +00:00
|
|
|
*
|
2015-02-23 11:18:48 +00:00
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with Kvazaar. If not, see <http://www.gnu.org/licenses/>.
|
2014-01-24 10:37:15 +00:00
|
|
|
****************************************************************************/
|
|
|
|
|
2012-06-11 15:43:29 +00:00
|
|
|
/**
|
2013-09-18 14:29:30 +00:00
|
|
|
* \file
|
|
|
|
* \brief Functions for handling intra frames.
|
2012-06-11 15:43:29 +00:00
|
|
|
*/
|
|
|
|
|
2013-09-18 09:16:03 +00:00
|
|
|
#include "intra.h"
|
|
|
|
|
2014-04-17 12:01:43 +00:00
|
|
|
#include <assert.h>
|
2013-03-07 15:42:00 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2013-09-18 09:16:03 +00:00
|
|
|
|
2013-03-07 15:42:00 +00:00
|
|
|
#include "config.h"
|
|
|
|
#include "encoder.h"
|
2014-04-09 12:56:10 +00:00
|
|
|
#include "transform.h"
|
|
|
|
#include "rdo.h"
|
2013-03-07 15:42:00 +00:00
|
|
|
|
2013-03-19 15:12:43 +00:00
|
|
|
|
2014-02-21 13:00:20 +00:00
|
|
|
/**
|
2013-09-19 13:21:45 +00:00
|
|
|
* \brief Function for deriving intra luma predictions
|
|
|
|
* \param pic picture to use
|
|
|
|
* \param x_cu x CU position (smallest CU)
|
|
|
|
* \param y_cu y CU position (smallest CU)
|
2014-02-21 13:00:20 +00:00
|
|
|
* \param preds output buffer for 3 predictions
|
2013-09-19 13:21:45 +00:00
|
|
|
* \returns (predictions are found)?1:0
|
|
|
|
*/
|
2015-08-26 08:50:27 +00:00
|
|
|
int8_t kvz_intra_get_dir_luma_predictor(const uint32_t x, const uint32_t y, int8_t* preds,
|
2015-03-04 11:15:45 +00:00
|
|
|
const cu_info_t * const cur_cu, const cu_info_t * const left_cu, const cu_info_t * const above_cu)
|
2013-03-07 15:42:00 +00:00
|
|
|
{
|
2014-02-25 12:16:32 +00:00
|
|
|
int y_cu = y>>3;
|
2014-01-17 15:06:24 +00:00
|
|
|
|
|
|
|
// The default mode if block is not coded yet is INTRA_DC.
|
2014-02-14 15:14:35 +00:00
|
|
|
int8_t left_intra_dir = 1;
|
|
|
|
int8_t above_intra_dir = 1;
|
2014-03-06 16:14:01 +00:00
|
|
|
|
2014-03-11 17:19:20 +00:00
|
|
|
if (x & 4) {
|
2014-01-21 18:48:59 +00:00
|
|
|
// If current CU is NxN and PU is on the right half, take mode from the
|
|
|
|
// left half of the same CU.
|
2014-03-11 17:19:20 +00:00
|
|
|
left_intra_dir = cur_cu->intra[PU_INDEX(0, y >> 2)].mode;
|
2014-01-17 15:06:24 +00:00
|
|
|
} else if (left_cu && left_cu->type == CU_INTRA) {
|
2014-01-21 18:48:59 +00:00
|
|
|
// Otherwise take the mode from the right side of the CU on the left.
|
2014-03-11 17:19:20 +00:00
|
|
|
left_intra_dir = left_cu->intra[PU_INDEX(1, y >> 2)].mode;
|
2014-01-17 15:06:24 +00:00
|
|
|
}
|
|
|
|
|
2014-03-11 17:19:20 +00:00
|
|
|
if (y & 4) {
|
2014-01-21 18:48:59 +00:00
|
|
|
// If current CU is NxN and PU is on the bottom half, take mode from the
|
|
|
|
// top half of the same CU.
|
2014-03-11 17:19:20 +00:00
|
|
|
above_intra_dir = cur_cu->intra[PU_INDEX(x >> 2, 0)].mode;
|
2014-01-21 18:48:59 +00:00
|
|
|
} else if (above_cu && above_cu->type == CU_INTRA &&
|
|
|
|
(y_cu * (LCU_WIDTH>>MAX_DEPTH)) % LCU_WIDTH != 0)
|
2014-01-17 15:06:24 +00:00
|
|
|
{
|
2014-01-21 18:48:59 +00:00
|
|
|
// Otherwise take the mode from the bottom half of the CU above.
|
2014-03-11 17:19:20 +00:00
|
|
|
above_intra_dir = above_cu->intra[PU_INDEX(x >> 2, 1)].mode;
|
2014-01-21 18:48:59 +00:00
|
|
|
}
|
|
|
|
|
2013-09-19 13:21:45 +00:00
|
|
|
// If the predictions are the same, add new predictions
|
2014-02-21 13:00:20 +00:00
|
|
|
if (left_intra_dir == above_intra_dir) {
|
2013-09-19 13:21:45 +00:00
|
|
|
if (left_intra_dir > 1) { // angular modes
|
|
|
|
preds[0] = left_intra_dir;
|
|
|
|
preds[1] = ((left_intra_dir + 29) % 32) + 2;
|
|
|
|
preds[2] = ((left_intra_dir - 1 ) % 32) + 2;
|
|
|
|
} else { //non-angular
|
2013-03-07 15:42:00 +00:00
|
|
|
preds[0] = 0;//PLANAR_IDX;
|
|
|
|
preds[1] = 1;//DC_IDX;
|
2014-02-21 13:00:20 +00:00
|
|
|
preds[2] = 26;//VER_IDX;
|
2013-03-07 15:42:00 +00:00
|
|
|
}
|
2013-09-19 13:21:45 +00:00
|
|
|
} else { // If we have two distinct predictions
|
|
|
|
preds[0] = left_intra_dir;
|
|
|
|
preds[1] = above_intra_dir;
|
2014-02-21 13:00:20 +00:00
|
|
|
|
2013-09-19 13:21:45 +00:00
|
|
|
// add planar mode if it's not yet present
|
|
|
|
if (left_intra_dir && above_intra_dir ) {
|
|
|
|
preds[2] = 0; // PLANAR_IDX;
|
2014-03-11 17:19:20 +00:00
|
|
|
} else { // Add DC mode if it's not present, otherwise 26.
|
2013-09-19 13:21:45 +00:00
|
|
|
preds[2] = (left_intra_dir+above_intra_dir)<2? 26 : 1;
|
2013-03-07 15:42:00 +00:00
|
|
|
}
|
2013-04-17 14:08:52 +00:00
|
|
|
}
|
2013-03-07 15:42:00 +00:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-04-09 12:56:10 +00:00
|
|
|
|
2015-10-03 00:36:58 +00:00
|
|
|
static void intra_filter_reference(int_fast8_t log2_width, kvz_intra_references *refs)
|
|
|
|
{
|
|
|
|
if (refs->filtered_initialized) {
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
refs->filtered_initialized = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const int_fast8_t ref_width = 2 * (1 << log2_width) + 1;
|
|
|
|
kvz_intra_ref *ref = &refs->ref;
|
|
|
|
kvz_intra_ref *filtered_ref = &refs->filtered_ref;
|
|
|
|
|
|
|
|
filtered_ref->left[0] = (ref->left[1] + 2 * ref->left[0] + ref->top[1] + 2) / 4;
|
|
|
|
filtered_ref->top[0] = filtered_ref->left[0];
|
|
|
|
|
|
|
|
for (int_fast8_t y = 1; y < ref_width - 1; ++y) {
|
|
|
|
kvz_pixel *p = &ref->left[y];
|
|
|
|
filtered_ref->left[y] = (p[-1] + 2 * p[0] + p[1] + 2) / 4;
|
|
|
|
}
|
|
|
|
filtered_ref->left[ref_width - 1] = ref->left[ref_width - 1];
|
|
|
|
|
|
|
|
for (int_fast8_t x = 1; x < ref_width - 1; ++x) {
|
|
|
|
kvz_pixel *p = &ref->top[x];
|
|
|
|
filtered_ref->top[x] = (p[-1] + 2 * p[0] + p[1] + 2) / 4;
|
|
|
|
}
|
|
|
|
filtered_ref->top[ref_width - 1] = ref->top[ref_width - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void post_process_intra_angular(
|
|
|
|
unsigned width,
|
|
|
|
unsigned stride,
|
|
|
|
const kvz_pixel *ref,
|
|
|
|
kvz_pixel *block)
|
|
|
|
{
|
|
|
|
kvz_pixel ref2 = ref[0];
|
|
|
|
for (unsigned i = 0; i < width; i++) {
|
|
|
|
kvz_pixel val = block[i * stride];
|
|
|
|
kvz_pixel ref1 = ref[i + 1];
|
|
|
|
block[i * stride] = CLIP_TO_PIXEL(val + ((ref1 - ref2) >> 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-10 08:27:15 +00:00
|
|
|
/**
|
2015-10-03 00:36:58 +00:00
|
|
|
* \brief Generage angular predictions.
|
|
|
|
* \param log2_width Log2 of width, range 2..5.
|
|
|
|
* \param intra_mode Angular mode in range 2..34.
|
|
|
|
* \param in_ref_above Pointer to -1 index of above reference, length=width*2+1.
|
|
|
|
* \param in_ref_left Pointer to -1 index of left reference, length=width*2+1.
|
|
|
|
* \param dst Buffer of size width*width.
|
2014-04-10 08:27:15 +00:00
|
|
|
*/
|
2015-10-03 00:36:58 +00:00
|
|
|
static void kvz_intra_pred_angular(
|
|
|
|
const int_fast8_t log2_width,
|
|
|
|
const int_fast8_t intra_mode,
|
|
|
|
const kvz_pixel *const in_ref_above,
|
|
|
|
const kvz_pixel *const in_ref_left,
|
|
|
|
kvz_pixel *const dst)
|
|
|
|
{
|
|
|
|
assert(log2_width >= 2 && log2_width <= 5);
|
|
|
|
assert(intra_mode >= 2 && intra_mode <= 34);
|
|
|
|
|
|
|
|
static const int8_t modedisp2sampledisp[9] = {0, 2, 5, 9, 13, 17, 21, 26, 32};
|
|
|
|
static const int16_t modedisp2invsampledisp[9] = {0, 4096, 1638, 910, 630, 482, 390, 315, 256}; // (256 * 32) / sampledisp
|
|
|
|
|
|
|
|
// Temporary buffer for modes 11-25.
|
|
|
|
// It only needs to be big enough to hold indices from -width to width-1.
|
|
|
|
kvz_pixel tmp_ref[2 * 32];
|
|
|
|
const int_fast8_t width = 1 << log2_width;
|
|
|
|
|
|
|
|
// Whether to swap references to always project on the left reference row.
|
|
|
|
const bool vertical_mode = intra_mode >= 18;
|
|
|
|
// Modes distance to horizontal or vertical mode.
|
|
|
|
const int_fast8_t mode_disp = vertical_mode ? intra_mode - 26 : 10 - intra_mode;
|
|
|
|
// Sample displacement per column in fractions of 32.
|
|
|
|
const int_fast8_t sample_disp = (mode_disp < 0 ? -1 : 1) * modedisp2sampledisp[abs(mode_disp)];
|
|
|
|
|
|
|
|
// Pointer for the reference we are interpolating from.
|
|
|
|
const kvz_pixel *ref_main;
|
|
|
|
// Pointer for the other reference.
|
|
|
|
const kvz_pixel *ref_side;
|
|
|
|
|
|
|
|
// Set ref_main and ref_side such that, when indexed with 0, they point to
|
|
|
|
// index 0 in block coordinates.
|
|
|
|
if (sample_disp < 0) {
|
|
|
|
// Negative sample_disp means, we need to use both references.
|
|
|
|
|
|
|
|
ref_side = (vertical_mode ? in_ref_left : in_ref_above) + 1;
|
|
|
|
ref_main = (vertical_mode ? in_ref_above : in_ref_left) + 1;
|
|
|
|
|
|
|
|
// Move the reference pixels to start from the middle to the later half of
|
|
|
|
// the tmp_ref, so there is room for negative indices.
|
|
|
|
for (int_fast8_t x = -1; x < width; ++x) {
|
|
|
|
tmp_ref[x + width] = ref_main[x];
|
|
|
|
}
|
|
|
|
// Get a pointer to block index 0 in tmp_ref.
|
|
|
|
ref_main = &tmp_ref[width];
|
|
|
|
|
|
|
|
// Extend the side reference to the negative indices of main reference.
|
|
|
|
int_fast32_t col_sample_disp = 128; // rounding for the ">> 8"
|
|
|
|
int_fast16_t inv_abs_sample_disp = modedisp2invsampledisp[abs(mode_disp)];
|
|
|
|
int_fast8_t most_negative_index = (width * sample_disp) >> 5;
|
|
|
|
for (int_fast8_t x = -2; x >= most_negative_index; --x) {
|
|
|
|
col_sample_disp += inv_abs_sample_disp;
|
|
|
|
int_fast8_t side_index = col_sample_disp >> 8;
|
|
|
|
tmp_ref[x + width] = ref_side[side_index - 1];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// sample_disp >= 0 means we don't need to refer to negative indices,
|
|
|
|
// which means we can just use the references as is.
|
|
|
|
ref_main = (vertical_mode ? in_ref_above : in_ref_left) + 1;
|
|
|
|
ref_side = (vertical_mode ? in_ref_left : in_ref_above) + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sample_disp != 0) {
|
|
|
|
// The mode is not horizontal or vertical, we have to do interpolation.
|
|
|
|
|
|
|
|
int_fast16_t delta_pos = 0;
|
|
|
|
for (int_fast8_t y = 0; y < width; ++y) {
|
|
|
|
delta_pos += sample_disp;
|
|
|
|
int_fast8_t delta_int = delta_pos >> 5;
|
|
|
|
int_fast8_t delta_fract = delta_pos & (32 - 1);
|
|
|
|
|
|
|
|
if (delta_fract) {
|
|
|
|
// Do linear filtering
|
|
|
|
for (int_fast8_t x = 0; x < width; ++x) {
|
|
|
|
kvz_pixel ref1 = ref_main[x + delta_int];
|
|
|
|
kvz_pixel ref2 = ref_main[x + delta_int + 1];
|
|
|
|
dst[y * width + x] = ((32 - delta_fract) * ref1 + delta_fract * ref2 + 16) >> 5;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Just copy the integer samples
|
|
|
|
for (int_fast8_t x = 0; x < width; x++) {
|
|
|
|
dst[y * width + x] = ref_main[x + delta_int];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Mode is horizontal or vertical, just copy the pixels.
|
|
|
|
|
|
|
|
for (int_fast8_t y = 0; y < width; ++y) {
|
|
|
|
for (int_fast8_t x = 0; x < width; ++x) {
|
|
|
|
dst[y * width + x] = ref_main[x];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Flip the block if this is was a horizontal mode.
|
|
|
|
if (!vertical_mode) {
|
|
|
|
for (int_fast8_t y = 0; y < width - 1; ++y) {
|
|
|
|
for (int_fast8_t x = y + 1; x < width; ++x) {
|
|
|
|
SWAP(dst[y * width + x], dst[x * width + y], kvz_pixel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Generage planar prediction.
|
|
|
|
* \param log2_width Log2 of width, range 2..5.
|
|
|
|
* \param in_ref_above Pointer to -1 index of above reference, length=width*2+1.
|
|
|
|
* \param in_ref_left Pointer to -1 index of left reference, length=width*2+1.
|
|
|
|
* \param dst Buffer of size width*width.
|
|
|
|
*/
|
|
|
|
static void kvz_intra_pred_planar(
|
|
|
|
const int_fast8_t log2_width,
|
|
|
|
const kvz_pixel *const ref_top,
|
|
|
|
const kvz_pixel *const ref_left,
|
|
|
|
kvz_pixel *const dst)
|
|
|
|
{
|
|
|
|
assert(log2_width >= 2 && log2_width <= 5);
|
|
|
|
|
|
|
|
const int_fast8_t width = 1 << log2_width;
|
|
|
|
const kvz_pixel top_right = ref_top[width + 1];
|
|
|
|
const kvz_pixel bottom_left = ref_left[width + 1];
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// Unoptimized version for reference.
|
|
|
|
for (int y = 0; y < width; ++y) {
|
|
|
|
for (int x = 0; x < width; ++x) {
|
|
|
|
int_fast16_t hor = (width - 1 - x) * ref_left[y + 1] + (x + 1) * top_right;
|
|
|
|
int_fast16_t ver = (width - 1 - y) * ref_top[x + 1] + (y + 1) * bottom_left;
|
|
|
|
dst[y * width + x] = (ver + hor + width) >> (log2_width + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
int_fast16_t top[32];
|
|
|
|
for (int i = 0; i < width; ++i) {
|
|
|
|
top[i] = ref_top[i + 1] << log2_width;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int y = 0; y < width; ++y) {
|
|
|
|
int_fast16_t hor = (ref_left[y + 1] << log2_width) + width;
|
|
|
|
for (int x = 0; x < width; ++x) {
|
|
|
|
hor += top_right - ref_left[y + 1];
|
|
|
|
top[x] += bottom_left - ref_top[x + 1];
|
|
|
|
dst[y * width + x] = (hor + top[x]) >> (log2_width + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Generage planar prediction.
|
|
|
|
* \param log2_width Log2 of width, range 2..5.
|
|
|
|
* \param in_ref_above Pointer to -1 index of above reference, length=width*2+1.
|
|
|
|
* \param in_ref_left Pointer to -1 index of left reference, length=width*2+1.
|
|
|
|
* \param dst Buffer of size width*width.
|
|
|
|
*/
|
|
|
|
static void kvz_intra_pred_dc(
|
|
|
|
const int_fast8_t log2_width,
|
|
|
|
const kvz_pixel *const ref_top,
|
|
|
|
const kvz_pixel *const ref_left,
|
|
|
|
kvz_pixel *const out_block)
|
|
|
|
{
|
|
|
|
int_fast8_t width = 1 << log2_width;
|
|
|
|
|
|
|
|
int_fast16_t sum = 0;
|
|
|
|
for (int_fast8_t i = 0; i < width; ++i) {
|
|
|
|
sum += ref_top[i + 1];
|
|
|
|
sum += ref_left[i + 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
const kvz_pixel dc_val = (sum + width) >> (log2_width + 1);
|
|
|
|
const int_fast16_t block_size = 1 << (log2_width * 2);
|
|
|
|
|
|
|
|
for (int_fast16_t i = 0; i < block_size; ++i) {
|
|
|
|
out_block[i] = dc_val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Generage intra DC prediction with post filtering applied.
|
|
|
|
* \param log2_width Log2 of width, range 2..5.
|
|
|
|
* \param in_ref_above Pointer to -1 index of above reference, length=width*2+1.
|
|
|
|
* \param in_ref_left Pointer to -1 index of left reference, length=width*2+1.
|
|
|
|
* \param dst Buffer of size width*width.
|
|
|
|
*/
|
|
|
|
static void kvz_intra_pred_filtered_dc(
|
|
|
|
const int_fast8_t log2_width,
|
|
|
|
const kvz_pixel *const ref_top,
|
|
|
|
const kvz_pixel *const ref_left,
|
|
|
|
kvz_pixel *const out_block)
|
|
|
|
{
|
|
|
|
assert(log2_width >= 2 && log2_width <= 5);
|
|
|
|
|
|
|
|
const int_fast8_t width = 1 << log2_width;
|
|
|
|
|
|
|
|
int_fast16_t sum = 0;
|
|
|
|
for (int_fast8_t i = 0; i < width; ++i) {
|
|
|
|
sum += ref_top[i + 1];
|
|
|
|
sum += ref_left[i + 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
const kvz_pixel dc_val = (sum + width) >> (log2_width + 1);
|
|
|
|
|
|
|
|
// Filter top-left with ([1 2 1] / 4)
|
|
|
|
out_block[0] = (ref_left[1] + 2 * dc_val + ref_top[1] + 2) / 4;
|
|
|
|
|
|
|
|
// Filter rest of the boundary with ([1 3] / 4)
|
|
|
|
for (int_fast8_t x = 1; x < width; ++x) {
|
|
|
|
out_block[x] = (ref_top[x + 1] + 3 * dc_val + 2) / 4;
|
|
|
|
}
|
|
|
|
for (int_fast8_t y = 1; y < width; ++y) {
|
|
|
|
out_block[y * width] = (ref_left[y + 1] + 3 * dc_val + 2) / 4;
|
|
|
|
for (int_fast8_t x = 1; x < width; ++x) {
|
|
|
|
out_block[y * width + x] = dc_val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void kvz_intra_get_pred_new(
|
|
|
|
kvz_intra_references *refs,
|
|
|
|
int_fast8_t log2_width,
|
|
|
|
int_fast8_t mode,
|
|
|
|
color_t color,
|
|
|
|
kvz_pixel *dst)
|
|
|
|
{
|
|
|
|
const int_fast8_t width = 1 << log2_width;
|
|
|
|
|
|
|
|
const kvz_intra_ref *used_ref = &refs->ref;
|
|
|
|
if (color != COLOR_Y || mode == 1 || width == 4) {
|
|
|
|
// For chroma, DC and 4x4 blocks, always use unfiltered reference.
|
|
|
|
} else if (mode == 0) {
|
|
|
|
// Otherwise, use filtered for planar.
|
|
|
|
used_ref = &refs->filtered_ref;
|
|
|
|
} else {
|
|
|
|
// Angular modes use smoothed reference pixels, unless the mode is close
|
|
|
|
// to being either vertical or horizontal.
|
2015-10-05 02:16:52 +00:00
|
|
|
static const int kvz_intra_hor_ver_dist_thres[5] = { 0, 7, 1, 0, 0 };
|
2015-10-03 00:36:58 +00:00
|
|
|
int filter_threshold = kvz_intra_hor_ver_dist_thres[g_to_bits[width]];
|
|
|
|
int dist_from_vert_or_hor = MIN(abs(mode - 26), abs(mode - 10));
|
|
|
|
if (dist_from_vert_or_hor > filter_threshold) {
|
|
|
|
used_ref = &refs->filtered_ref;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (used_ref == &refs->filtered_ref && !refs->filtered_initialized) {
|
|
|
|
intra_filter_reference(log2_width, refs);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mode == 0) {
|
|
|
|
kvz_intra_pred_planar(log2_width, used_ref->top, used_ref->left, dst);
|
|
|
|
} else if (mode == 1) {
|
|
|
|
// Do extra post filtering for edge pixels of luma DC mode.
|
|
|
|
if (color == COLOR_Y && width < 32) {
|
|
|
|
kvz_intra_pred_filtered_dc(log2_width, used_ref->top, used_ref->left, dst);
|
|
|
|
} else {
|
|
|
|
kvz_intra_pred_dc(log2_width, used_ref->top, used_ref->left, dst);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
kvz_intra_pred_angular(log2_width, mode, used_ref->top, used_ref->left, dst);
|
|
|
|
if (color == COLOR_Y && width < 32) {
|
|
|
|
if (mode == 10) {
|
|
|
|
post_process_intra_angular(width, 1, used_ref->top, dst);
|
|
|
|
} else if (mode == 26) {
|
|
|
|
post_process_intra_angular(width, width, used_ref->left, dst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-13 13:56:43 +00:00
|
|
|
|
2015-10-03 00:36:58 +00:00
|
|
|
void kvz_intra_build_reference(
|
|
|
|
const int_fast8_t log2_width,
|
|
|
|
const color_t color,
|
|
|
|
const vector2d_t *const luma_px,
|
|
|
|
const vector2d_t *const pic_px,
|
|
|
|
const lcu_t *const lcu,
|
|
|
|
kvz_intra_references *const refs)
|
|
|
|
{
|
|
|
|
assert(log2_width >= 2 && log2_width <= 5);
|
|
|
|
|
|
|
|
// Tables for looking up the number of intra reference pixels based on
|
|
|
|
// prediction units coordinate within an LCU.
|
|
|
|
// generated by "tools/generate_ref_pixel_tables.py".
|
|
|
|
static const uint8_t num_ref_pixels_top[16][16] = {
|
|
|
|
{ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 },
|
|
|
|
{ 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4 },
|
|
|
|
{ 16, 12, 8, 4, 16, 12, 8, 4, 16, 12, 8, 4, 16, 12, 8, 4 },
|
|
|
|
{ 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4 },
|
|
|
|
{ 32, 28, 24, 20, 16, 12, 8, 4, 32, 28, 24, 20, 16, 12, 8, 4 },
|
|
|
|
{ 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4 },
|
|
|
|
{ 16, 12, 8, 4, 16, 12, 8, 4, 16, 12, 8, 4, 16, 12, 8, 4 },
|
|
|
|
{ 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4 },
|
|
|
|
{ 64, 60, 56, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4 },
|
|
|
|
{ 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4 },
|
|
|
|
{ 16, 12, 8, 4, 16, 12, 8, 4, 16, 12, 8, 4, 16, 12, 8, 4 },
|
|
|
|
{ 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4 },
|
|
|
|
{ 32, 28, 24, 20, 16, 12, 8, 4, 32, 28, 24, 20, 16, 12, 8, 4 },
|
|
|
|
{ 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4 },
|
|
|
|
{ 16, 12, 8, 4, 16, 12, 8, 4, 16, 12, 8, 4, 16, 12, 8, 4 },
|
|
|
|
{ 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4 }
|
|
|
|
};
|
|
|
|
static const uint8_t num_ref_pixels_left[16][16] = {
|
|
|
|
{ 64, 4, 8, 4, 16, 4, 8, 4, 32, 4, 8, 4, 16, 4, 8, 4 },
|
|
|
|
{ 60, 4, 4, 4, 12, 4, 4, 4, 28, 4, 4, 4, 12, 4, 4, 4 },
|
|
|
|
{ 56, 4, 8, 4, 8, 4, 8, 4, 24, 4, 8, 4, 8, 4, 8, 4 },
|
|
|
|
{ 52, 4, 4, 4, 4, 4, 4, 4, 20, 4, 4, 4, 4, 4, 4, 4 },
|
|
|
|
{ 48, 4, 8, 4, 16, 4, 8, 4, 16, 4, 8, 4, 16, 4, 8, 4 },
|
|
|
|
{ 44, 4, 4, 4, 12, 4, 4, 4, 12, 4, 4, 4, 12, 4, 4, 4 },
|
|
|
|
{ 40, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4 },
|
|
|
|
{ 36, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 },
|
|
|
|
{ 32, 4, 8, 4, 16, 4, 8, 4, 32, 4, 8, 4, 16, 4, 8, 4 },
|
|
|
|
{ 28, 4, 4, 4, 12, 4, 4, 4, 28, 4, 4, 4, 12, 4, 4, 4 },
|
|
|
|
{ 24, 4, 8, 4, 8, 4, 8, 4, 24, 4, 8, 4, 8, 4, 8, 4 },
|
|
|
|
{ 20, 4, 4, 4, 4, 4, 4, 4, 20, 4, 4, 4, 4, 4, 4, 4 },
|
|
|
|
{ 16, 4, 8, 4, 16, 4, 8, 4, 16, 4, 8, 4, 16, 4, 8, 4 },
|
|
|
|
{ 12, 4, 4, 4, 12, 4, 4, 4, 12, 4, 4, 4, 12, 4, 4, 4 },
|
|
|
|
{ 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4 },
|
|
|
|
{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }
|
|
|
|
};
|
|
|
|
|
|
|
|
refs->filtered_initialized = false;
|
|
|
|
kvz_pixel *out_left_ref = &refs->ref.left[0];
|
|
|
|
kvz_pixel *out_top_ref = &refs->ref.top[0];
|
|
|
|
|
|
|
|
const kvz_pixel dc_val = 1 << (KVZ_BIT_DEPTH - 1);
|
|
|
|
const int is_chroma = color != COLOR_Y ? 1 : 0;
|
|
|
|
const int_fast8_t width = 1 << log2_width;
|
|
|
|
|
|
|
|
// Convert luma coordinates to chroma coordinates for chroma.
|
|
|
|
const vector2d_t lcu_px = {
|
|
|
|
luma_px->x % LCU_WIDTH,
|
|
|
|
luma_px->y % LCU_WIDTH
|
|
|
|
};
|
|
|
|
const vector2d_t px = {
|
|
|
|
lcu_px.x >> is_chroma,
|
|
|
|
lcu_px.y >> is_chroma,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Init pointers to LCUs reconstruction buffers, such that index 0 refers to block coordinate 0.
|
|
|
|
const kvz_pixel *left_ref = !color ? &lcu->left_ref.y[1] : (color == 1) ? &lcu->left_ref.u[1] : &lcu->left_ref.v[1];
|
|
|
|
const kvz_pixel *top_ref = !color ? &lcu->top_ref.y[1] : (color == 1) ? &lcu->top_ref.u[1] : &lcu->top_ref.v[1];
|
|
|
|
const kvz_pixel *rec_ref = !color ? lcu->rec.y : (color == 1) ? lcu->rec.u : lcu->rec.v;
|
|
|
|
|
|
|
|
// Init top borders pointer to point to the correct place in the correct reference array.
|
|
|
|
const kvz_pixel *top_border;
|
|
|
|
if (px.y) {
|
|
|
|
top_border = &rec_ref[px.x + (px.y - 1) * (LCU_WIDTH >> is_chroma)];
|
|
|
|
} else {
|
|
|
|
top_border = &top_ref[px.x];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Init left borders pointer to point to the correct place in the correct reference array.
|
|
|
|
const kvz_pixel *left_border;
|
|
|
|
int left_stride; // Distance between reference samples.
|
|
|
|
if (px.x) {
|
|
|
|
left_border = &rec_ref[px.x - 1 + px.y * (LCU_WIDTH >> is_chroma)];
|
|
|
|
left_stride = LCU_WIDTH >> is_chroma;
|
|
|
|
} else {
|
|
|
|
left_border = &left_ref[px.y];
|
|
|
|
left_stride = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate left reference.
|
|
|
|
if (luma_px->x > 0) {
|
|
|
|
// Get the number of reference pixels based on the PU coordinate within the LCU.
|
|
|
|
int px_available_left = num_ref_pixels_left[lcu_px.y / 4][lcu_px.x / 4] >> is_chroma;
|
|
|
|
|
|
|
|
// Limit the number of available pixels based on block size and dimensions
|
|
|
|
// of the picture.
|
|
|
|
px_available_left = MIN(px_available_left, width * 2);
|
|
|
|
px_available_left = MIN(px_available_left, (pic_px->y - luma_px->y) >> is_chroma);
|
|
|
|
|
|
|
|
// Copy pixels from coded CUs.
|
|
|
|
for (int i = 0; i < px_available_left; ++i) {
|
|
|
|
out_left_ref[i + 1] = left_border[i * left_stride];
|
|
|
|
}
|
|
|
|
// Extend the last pixel for the rest of the reference values.
|
|
|
|
kvz_pixel nearest_pixel = out_left_ref[px_available_left];
|
|
|
|
for (int i = px_available_left; i < width * 2; ++i) {
|
|
|
|
out_left_ref[i + 1] = nearest_pixel;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If we are on the left edge, extend the first pixel of the top row.
|
|
|
|
kvz_pixel nearest_pixel = luma_px->y > 0 ? top_border[0] : dc_val;
|
|
|
|
for (int i = 0; i < width * 2; i++) {
|
|
|
|
out_left_ref[i + 1] = nearest_pixel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate top-left reference.
|
|
|
|
if (luma_px->x > 0 && luma_px->y > 0) {
|
|
|
|
// If the block is at an LCU border, the top-left must be copied from
|
|
|
|
// the border that points to the LCUs 1D reference buffer.
|
|
|
|
if (px.x == 0) {
|
|
|
|
out_left_ref[0] = left_border[-1 * left_stride];
|
|
|
|
out_top_ref[0] = left_border[-1 * left_stride];
|
|
|
|
} else {
|
|
|
|
out_left_ref[0] = top_border[-1];
|
|
|
|
out_top_ref[0] = top_border[-1];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Copy reference clockwise.
|
|
|
|
out_left_ref[0] = out_left_ref[1];
|
|
|
|
out_top_ref[0] = out_left_ref[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate top reference.
|
|
|
|
if (luma_px->y > 0) {
|
|
|
|
// Get the number of reference pixels based on the PU coordinate within the LCU.
|
|
|
|
int px_available_top = num_ref_pixels_top[lcu_px.y / 4][lcu_px.x / 4] >> is_chroma;
|
|
|
|
|
|
|
|
// Limit the number of available pixels based on block size and dimensions
|
|
|
|
// of the picture.
|
|
|
|
px_available_top = MIN(px_available_top, width * 2);
|
|
|
|
px_available_top = MIN(px_available_top, (pic_px->x - luma_px->x) >> is_chroma);
|
|
|
|
|
|
|
|
// Copy all the pixels we can.
|
|
|
|
for (int i = 0; i < px_available_top; ++i) {
|
|
|
|
out_top_ref[i + 1] = top_border[i];
|
|
|
|
}
|
|
|
|
// Extend the last pixel for the rest of the reference values.
|
|
|
|
kvz_pixel nearest_pixel = top_border[px_available_top - 1];
|
|
|
|
for (int i = px_available_top; i < width * 2; ++i) {
|
|
|
|
out_top_ref[i + 1] = nearest_pixel;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Extend nearest pixel.
|
|
|
|
kvz_pixel nearest_pixel = luma_px->x > 0 ? left_border[0] : dc_val;
|
|
|
|
for (int i = 0; i < width * 2; i++) {
|
|
|
|
out_top_ref[i + 1] = nearest_pixel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-20 14:26:57 +00:00
|
|
|
|
2015-08-26 08:50:27 +00:00
|
|
|
void kvz_intra_recon_lcu_luma(encoder_state_t * const state, int x, int y, int depth, int8_t intra_mode, cu_info_t *cur_cu, lcu_t *lcu)
|
2014-02-25 10:08:07 +00:00
|
|
|
{
|
2015-03-04 11:32:11 +00:00
|
|
|
const vector2d_t lcu_px = { x & 0x3f, y & 0x3f };
|
2014-09-23 11:41:25 +00:00
|
|
|
if (cur_cu == NULL) {
|
|
|
|
cur_cu = &lcu->cu[LCU_CU_OFFSET + (lcu_px.x >> 3) + (lcu_px.y >> 3)*LCU_T_CU_WIDTH];
|
|
|
|
}
|
2014-05-15 14:37:17 +00:00
|
|
|
const int8_t width = LCU_WIDTH >> depth;
|
|
|
|
|
|
|
|
if (depth == 0 || cur_cu->tr_depth > depth) {
|
|
|
|
int offset = width / 2;
|
|
|
|
|
2015-08-26 08:50:27 +00:00
|
|
|
kvz_intra_recon_lcu_luma(state, x, y, depth+1, intra_mode, NULL, lcu);
|
|
|
|
kvz_intra_recon_lcu_luma(state, x + offset, y, depth+1, intra_mode, NULL, lcu);
|
|
|
|
kvz_intra_recon_lcu_luma(state, x, y + offset, depth+1, intra_mode, NULL, lcu);
|
|
|
|
kvz_intra_recon_lcu_luma(state, x + offset, y + offset, depth+1, intra_mode, NULL, lcu);
|
2014-05-15 14:37:17 +00:00
|
|
|
|
|
|
|
if (depth < MAX_DEPTH) {
|
2015-03-04 11:15:45 +00:00
|
|
|
cu_info_t *cu_a = &lcu->cu[LCU_CU_OFFSET + ((lcu_px.x + offset) >> 3) + (lcu_px.y >> 3) *LCU_T_CU_WIDTH];
|
|
|
|
cu_info_t *cu_b = &lcu->cu[LCU_CU_OFFSET + (lcu_px.x >> 3) + ((lcu_px.y + offset) >> 3)*LCU_T_CU_WIDTH];
|
|
|
|
cu_info_t *cu_c = &lcu->cu[LCU_CU_OFFSET + ((lcu_px.x + offset) >> 3) + ((lcu_px.y + offset) >> 3)*LCU_T_CU_WIDTH];
|
2014-05-15 14:37:17 +00:00
|
|
|
if (cbf_is_set(cu_a->cbf.y, depth+1) || cbf_is_set(cu_b->cbf.y, depth+1) || cbf_is_set(cu_c->cbf.y, depth+1)) {
|
|
|
|
cbf_set(&cur_cu->cbf.y, depth);
|
|
|
|
}
|
2014-05-19 08:08:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-10-05 02:41:38 +00:00
|
|
|
// Perform intra prediction and put the result in correct place lcu.
|
2015-10-04 21:30:49 +00:00
|
|
|
vector2d_t pic_px = { state->tile->frame->width, state->tile->frame->height };
|
|
|
|
vector2d_t luma_px = { x, y };
|
|
|
|
kvz_intra_references refs;
|
|
|
|
const int_fast8_t log2_width = kvz_g_convert_to_bit[width] + 2;
|
|
|
|
kvz_intra_build_reference(log2_width, COLOR_Y, &luma_px, &pic_px, lcu, &refs);
|
2014-05-19 08:08:40 +00:00
|
|
|
|
2015-10-05 02:41:38 +00:00
|
|
|
kvz_pixel pred[32 * 32];
|
|
|
|
kvz_intra_get_pred_new(&refs, log2_width, intra_mode, COLOR_Y, pred);
|
|
|
|
|
2015-10-04 21:30:49 +00:00
|
|
|
kvz_pixel *block_in_lcu = &lcu->rec.y[lcu_px.x + lcu_px.y * LCU_WIDTH];
|
2015-10-05 02:41:38 +00:00
|
|
|
kvz_pixels_blit(pred, block_in_lcu, width, width, width, LCU_WIDTH);
|
2014-05-19 08:08:40 +00:00
|
|
|
|
2015-10-04 21:30:49 +00:00
|
|
|
kvz_quantize_lcu_luma_residual(state, x, y, depth, cur_cu, lcu);
|
2014-05-19 08:08:40 +00:00
|
|
|
}
|
|
|
|
|
2015-08-26 08:50:27 +00:00
|
|
|
void kvz_intra_recon_lcu_chroma(encoder_state_t * const state, int x, int y, int depth, int8_t intra_mode, cu_info_t *cur_cu, lcu_t *lcu)
|
2014-05-19 08:08:40 +00:00
|
|
|
{
|
2015-03-04 11:32:11 +00:00
|
|
|
const vector2d_t lcu_px = { x & 0x3f, y & 0x3f };
|
2014-05-19 08:08:40 +00:00
|
|
|
const int8_t width = LCU_WIDTH >> depth;
|
|
|
|
const int8_t width_c = (depth == MAX_PU_DEPTH ? width : width / 2);
|
|
|
|
|
2014-09-23 11:41:25 +00:00
|
|
|
if (cur_cu == NULL) {
|
|
|
|
cur_cu = &lcu->cu[LCU_CU_OFFSET + (lcu_px.x >> 3) + (lcu_px.y >> 3)*LCU_T_CU_WIDTH];
|
|
|
|
}
|
|
|
|
|
2014-05-19 08:08:40 +00:00
|
|
|
if (depth == 0 || cur_cu->tr_depth > depth) {
|
|
|
|
int offset = width / 2;
|
|
|
|
|
2015-08-26 08:50:27 +00:00
|
|
|
kvz_intra_recon_lcu_chroma(state, x, y, depth+1, intra_mode, NULL, lcu);
|
|
|
|
kvz_intra_recon_lcu_chroma(state, x + offset, y, depth+1, intra_mode, NULL, lcu);
|
|
|
|
kvz_intra_recon_lcu_chroma(state, x, y + offset, depth+1, intra_mode, NULL, lcu);
|
|
|
|
kvz_intra_recon_lcu_chroma(state, x + offset, y + offset, depth+1, intra_mode, NULL, lcu);
|
2014-05-19 08:08:40 +00:00
|
|
|
|
|
|
|
if (depth < MAX_DEPTH) {
|
2015-03-04 11:15:45 +00:00
|
|
|
cu_info_t *cu_a = &lcu->cu[LCU_CU_OFFSET + ((lcu_px.x + offset) >> 3) + (lcu_px.y >> 3) *LCU_T_CU_WIDTH];
|
|
|
|
cu_info_t *cu_b = &lcu->cu[LCU_CU_OFFSET + (lcu_px.x >> 3) + ((lcu_px.y + offset) >> 3)*LCU_T_CU_WIDTH];
|
|
|
|
cu_info_t *cu_c = &lcu->cu[LCU_CU_OFFSET + ((lcu_px.x + offset) >> 3) + ((lcu_px.y + offset) >> 3)*LCU_T_CU_WIDTH];
|
2014-05-15 14:37:17 +00:00
|
|
|
if (cbf_is_set(cu_a->cbf.u, depth+1) || cbf_is_set(cu_b->cbf.u, depth+1) || cbf_is_set(cu_c->cbf.u, depth+1)) {
|
|
|
|
cbf_set(&cur_cu->cbf.u, depth);
|
|
|
|
}
|
|
|
|
if (cbf_is_set(cu_a->cbf.v, depth+1) || cbf_is_set(cu_b->cbf.v, depth+1) || cbf_is_set(cu_c->cbf.v, depth+1)) {
|
|
|
|
cbf_set(&cur_cu->cbf.v, depth);
|
|
|
|
}
|
|
|
|
}
|
2014-03-11 17:19:20 +00:00
|
|
|
|
2014-05-15 14:37:17 +00:00
|
|
|
return;
|
|
|
|
}
|
2014-05-19 08:08:40 +00:00
|
|
|
|
2015-10-04 22:29:40 +00:00
|
|
|
if (!(x & 4 || y & 4)) {
|
|
|
|
const int_fast8_t log2_width_c = kvz_g_convert_to_bit[width_c] + 2;
|
|
|
|
const vector2d_t luma_px = { x, y };
|
|
|
|
const vector2d_t pic_px = { state->tile->frame->width, state->tile->frame->height };
|
|
|
|
|
|
|
|
// Intra predict U-plane and put the result in lcu buffer.
|
|
|
|
{
|
|
|
|
kvz_intra_references refs;
|
|
|
|
kvz_intra_build_reference(log2_width_c, COLOR_U, &luma_px, &pic_px, lcu, &refs);
|
|
|
|
|
2015-10-05 02:41:38 +00:00
|
|
|
kvz_pixel pred[32 * 32];
|
|
|
|
kvz_intra_get_pred_new(&refs, log2_width_c, intra_mode, COLOR_U, pred);
|
|
|
|
|
|
|
|
kvz_pixel *pu_in_lcu = &lcu->rec.u[lcu_px.x / 2 + (lcu_px.y * LCU_WIDTH) / 4];
|
|
|
|
kvz_pixels_blit(pred, pu_in_lcu, width_c, width_c, width_c, LCU_WIDTH_C);
|
2014-10-15 13:01:58 +00:00
|
|
|
}
|
2015-10-04 22:29:40 +00:00
|
|
|
|
|
|
|
// Intra predict V-plane and put the result in lcu buffer.
|
|
|
|
{
|
|
|
|
kvz_intra_references refs;
|
|
|
|
kvz_intra_build_reference(log2_width_c, COLOR_V, &luma_px, &pic_px, lcu, &refs);
|
|
|
|
|
2015-10-05 02:41:38 +00:00
|
|
|
kvz_pixel pred[32 * 32];
|
|
|
|
kvz_intra_get_pred_new(&refs, log2_width_c, intra_mode, COLOR_V, pred);
|
|
|
|
|
|
|
|
kvz_pixel *pu_in_lcu = &lcu->rec.v[lcu_px.x / 2 + (lcu_px.y * LCU_WIDTH) / 4];
|
|
|
|
kvz_pixels_blit(pred, pu_in_lcu, width_c, width_c, width_c, LCU_WIDTH_C);
|
2015-10-04 22:29:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
kvz_quantize_lcu_chroma_residual(state, x, y, depth, cur_cu, lcu);
|
2014-02-26 13:45:00 +00:00
|
|
|
}
|
2014-05-19 10:18:06 +00:00
|
|
|
}
|