注:问号以及未注释部分 会在x265-1.9版本内更新
/*****************************************************************************
* Copyright (C) 2013 x265 project
*
* Authors: Min Chen <chenm003@163.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
*
* This program is also available under a commercial proprietary license.
* For more information, contact us at license @ x265.com.
*****************************************************************************/
#include "common.h"
#include "primitives.h"
using namespace X265_NS;
namespace {
/** 函数功能 : 对帧内参考像素进行滤波
/* 调用范围 : 只在Predict::initAdiPattern、Predict::initAdiPatternChroma和LookaheadTLD::lowresIntraEstimate函数中被调用
* \参数tuSize : 当前的块大小
* \参数samples : 参考像素
* \参数filtered : 返回滤波后的参考像素
* \返回值 : null**/
template<int tuSize>
void intraFilter(const pixel* samples, pixel* filtered) /* 1:2:1 filtering of left and top reference samples */
{
const int tuSize2 = tuSize << 1; //用于直接获取topLast数据,因为存储方式是先存储左上角像素, top,top-right,left, left-bottom格式
pixel topLeft = samples[0], topLast = samples[tuSize2], leftLast = samples[tuSize2 + tuSize2]; //获取相应位置数据
// filtering top
for (int i = 1; i < tuSize2; i++)
filtered[i] = ((samples[i] << 1) + samples[i - 1] + samples[i + 1] + 2) >> 2; //对所有top行数据进行滤波,除第一个数据与最后一个数据
filtered[tuSize2] = topLast; //top行最后一个数据不进行滤波,直接进行copy
// filtering top-left
filtered[0] = ((topLeft << 1) + samples[1] + samples[tuSize2 + 1] + 2) >> 2; //对左上角像素进行滤波: 采用top第一个数据与left第一个数据
// filtering left
filtered[tuSize2 + 1] = ((samples[tuSize2 + 1] << 1) + topLeft + samples[tuSize2 + 2] + 2) >> 2;
for (int i = tuSize2 + 2; i < tuSize2 + tuSize2; i++)
filtered[i] = ((samples[i] << 1) + samples[i - 1] + samples[i + 1] + 2) >> 2; //对left列进行滤波
filtered[tuSize2 + tuSize2] = leftLast; //left列最后一个数据不进行滤波,直接进行copy
}
/** 函数功能 : DC预测块滤波
* \参数above : 上边参考像素点
* \参数left : 左边参考像素点
* \参数dst : 目标存储地址
* \参数dststride : 目标存储地址步长
* \参数size : 当前的块大小
* \返回值 : null**/
static void dcPredFilter(const pixel* above, const pixel* left, pixel* dst, intptr_t dststride, int size)
{
// boundary pixels processing
dst[0] = (pixel)((above[0] + left[0] + 2 * dst[0] + 2) >> 2);//滤波预测块左上角像素点
for (int x = 1; x < size; x++)
dst[x] = (pixel)((above[x] + 3 * dst[x] + 2) >> 2);//滤波预测块第一行
dst += dststride;
for (int y = 1; y < size; y++)
{
*dst = (pixel)((left[y] + 3 * *dst + 2) >> 2);//滤波预测块第一列
dst += dststride;
}
}
/** 函数功能 : DC预测
* \参数width : 当前的块大小
* \参数dst : 目标存储地址
* \参数dstStride : 目标存储地址步长
* \参数src : 周边参考像素点
* \参数dirMode : 此处用不到 为0
* \参数filtered : 是否滤波(16x16以下需要滤波)
* \返回值 : null**/
template<int width>
void intra_pred_dc_c(pixel* dst, intptr_t dstStride, const pixel* srcPix, int /*dirMode*/, int bFilter)
{
int k, l;
int dcVal = width;//DC计算 +N
for (int i = 0; i < width; i++)
dcVal += srcPix[1 + i] + srcPix[2 * width + 1 + i];//累加 上边行参考像素点和左边行参考像素点
dcVal = dcVal / (width + width);//获取预测像素值
for (k = 0; k < width; k++)
for (l = 0; l < width; l++)
dst[k * dstStride + l] = (pixel)dcVal;//将预测DC值平铺到预测块中
if (bFilter)//如果需要滤波
dcPredFilter(srcPix + 1, srcPix + (2 * width + 1), dst, dstStride, width);//DC预测块滤波
}
/** 函数功能 : Planar预测
* \参数log2Size : 当前的块大小
* \参数dst : 目标存储地址
* \参数dstStride : 目标存储地址步长
* \参数src : 周边参考像素点
* \返回值 : null**/
template<int log2Size>
void planar_pred_c(pixel* dst, intptr_t dstStride, const pixel* srcPix, int /*dirMode*/, int /*bFilter*/)
{
const int blkSize = 1 << log2Size;//获取当前的块大小
const pixel* above = srcPix + 1;//上边块参考像素
const pixel* left = srcPix + (2 * blkSize + 1);//左边边参考像素
pixel topRight = above[blkSize];//上边块最右像素点
pixel bottomLeft = left[blkSize];//左边块最做像素点
for (int y = 0; y < blkSize; y++)
for (int x = 0; x < blkSize; x++)
dst[y * dstStride + x] = (pixel) (((blkSize - 1 - x) * left[y] + (blkSize - 1 -y) * above[x] + (x + 1) * topRight + (y + 1) * bottomLeft + blkSize) >> (log2Size + 1));//预测值
}
/** 函数功能 :按照预测方向的标号计算当前块大小的预测值
* \参数 width :待计算的块的宽度
* \参数 dst :预测块地址
* \参数 dstStride:步长
* \参数 srcPix0 :周边像素地址
* \参数 dirMode :模式
* \参数 bFilter :是否进行滤波
* \返回值 :null
*/
template<int width>
void intra_pred_ang_c(pixel* dst, intptr_t dstStride, const pixel *srcPix0, int dirMode, int bFilter)
{
int width2 = width << 1;//用于快速指向另一边 例如上边占2width
// Flip the neighbours in the horizontal case.
int horMode = dirMode < 18;//是否属于水平分类 (2,17)水平分类 (18~34) 垂直分类
pixel neighbourBuf[129];//暂存左上 左边 左下数据
const pixel *srcPix = srcPix0;//获取周边参考像素地址
if (horMode)//如果是水平模式 获取左边行数据 便于快速计算
{
neighbourBuf[0] = srcPix[0];//copy左上角参考像素
for (int i = 0; i < width << 1; i++) //将左边、左下 与上边、右上 的存储位置互换
{
neighbourBuf[1 + i] = srcPix[width2 + 1 + i];//获取左边数据、左下数据
neighbourBuf[width2 + 1 + i] = srcPix[1 + i];//获取上边、右上数据
}
srcPix = neighbourBuf;//更新参考数据位置
}
// Intra prediction angle and inverse angle tables.
const int8_t angleTable[17] = { -32, -26, -21, -17, -13, -9, -5, -2, 0, 2, 5, 9, 13, 17, 21, 26, 32 };//18~34角度对应的偏移值
const int16_t invAngleTable[8] = { 4096, 1638, 910, 630, 482, 390, 315, 256 };//水平模式:相对于10号的偏移位置 垂直模式:相对于26号的偏移位置
// Get the prediction angle.
int angleOffset = horMode ? 10 - dirMode : dirMode - 26;//获取offset
int angle = angleTable[8 + angleOffset];//获取当前标号对应的偏移值
// Vertical Prediction.
if (!angle)//标号10 或者 26进入 表示 水平或者垂直预测
{
for (int y = 0; y < width; y++)
for (int x = 0; x < width; x++)
dst[y * dstStride + x] = srcPix[1 + x];//获取垂直或者水平参考像素点
if (bFilter)//如果需要滤波
{
int topLeft = srcPix[0], top = srcPix[1];//左上角像素点 上边行第一个像素点(或者左边列第一个像素点)
for (int y = 0; y < width; y++)
dst[y * dstStride] = x265_clip((int16_t)(top + ((srcPix[width2 + 1 + y] - topLeft) >> 1)));//滤波左边列:垂直预测(左上角像素点+(右上块[y]-上边行第一个像素点)/2)水平预测(左上角像素点+(左下块[y]-左边列第一个像素点)/2)
}
}
else //角度预测 Angular prediction.
{
// Get the reference pixels. The reference base is the first pixel to the top (neighbourBuf[1]).
pixel refBuf[64];//用于存储参考像素
const pixel *ref;//指针 指示参考像素具体位置
// Use the projected left neighbours and the top neighbours.
if (angle < 0)//如果偏移值小于0 标号(11~25) 处于(左边上部分 上边左部分)
{
// Number of neighbours projected.
int nbProjected = -((width * angle) >> 5) - 1;//需要拿其它块补的个数
pixel *ref_pix = refBuf + nbProjected + 1;//如果是水平方向 则指向左边第一个参考像素 如果是垂直方向 则指向上边第一个像素 空出来 添补个数 加上 左上角一点
// Project the neighbours.
int invAngle = invAngleTable[- angleOffset - 1];//当前角度每次的增加量
int invAngleSum = 128;//用于四舍五入
for (int i = 0; i < nbProjected; i++)//添补
{
invAngleSum += invAngle;//每次增加角度偏移量
ref_pix[- 2 - i] = srcPix[width2 + (invAngleSum >> 8)];//获取参考像素
}
// Copy the top-left and top pixels.
for (int i = 0; i < width + 1; i++)//水平模式:获取左上角一点、左边值 垂直模式:获取左上角一点、上边值
ref_pix[-1 + i] = srcPix[i];
ref = ref_pix;//参考点位置
}
else // Use the top and top-right neighbours.
ref = srcPix + 1;//直接获取上边或者左边的参考点
// Pass every row.
int angleSum = 0;//记录当前的像素点 属于的角度位置
for (int y = 0; y < width; y++)
{
angleSum += angle;//获取当前角度位置
int offset = angleSum >> 5;//获取当前像素对应参考像素在ref中的位置
int fraction = angleSum & 31;//计算当前像素对应参考像素的加权因子
if (fraction) // Interpolate
for (int x = 0; x < width; x++)
dst[y * dstStride + x] = (pixel)(((32 - fraction) * ref[offset + x] + fraction * ref[offset + x + 1] + 16) >> 5);//计算当前位置的预测值
else // Copy.
for (int x = 0; x < width; x++)
dst[y * dstStride + x] = ref[offset + x];//计算当前位置的预测值
}
}
// Flip for horizontal.
if (horMode)//如果水平模式 上部分是按照垂直模式计算的需要对其转置
{
for (int y = 0; y < width - 1; y++)//转置
{
for (int x = y + 1; x < width; x++)
{
pixel tmp = dst[y * dstStride + x];
dst[y * dstStride + x] = dst[x * dstStride + y];
dst[x * dstStride + y] = tmp;
}
}
}
}
/** 函数功能 :计算所有角度模式的预测值
* \参数 log2Size :待计算的块的宽度
* \参数 dest :预测块地址
* \参数 refPix :未滤波的参考像素
* \参数 filtPix :滤波后的参考像素
* \参数 bLuma :是否进行滤波(不是亮度)
* \返回值 :null
*/
template<int log2Size>
void all_angs_pred_c(pixel *dest, pixel *refPix, pixel *filtPix, int bLuma)
{
const int size = 1 << log2Size;//获取当前的块大小
for (int mode = 2; mode <= 34; mode++)//遍历所有角度模式
{
pixel *srcPix = (g_intraFilterFlags[mode] & size ? filtPix : refPix);//获取滤波或则非滤波参考像素
pixel *out = dest + ((mode - 2) << (log2Size * 2));//获取对应角度的预测值存储地址
intra_pred_ang_c<size>(out, size, srcPix, mode, bLuma);//进行预测
// Optimize code don't flip buffer
bool modeHor = (mode < 18);
// transpose the block if this is a horizontal mode
/*这里可能需要转置
陈敏这样回复我
It is model only, so there have reduce matrix transpose
The output is not exact match to HM's (miss transpose on Horizon mode) since this function for intra decide only
**/
if (modeHor)//如果水平模式 上部分是按照垂直模式计算的需要对其转置 (注意水平方式需要置换两次 因为transpose将原始块置换了)
{
for (int k = 0; k < size - 1; k++)
{
for (int l = k + 1; l < size; l++)
{
pixel tmp = out[k * size + l];
out[k * size + l] = out[l * size + k];
out[l * size + k] = tmp;
}
}
}
}
}
}
namespace X265_NS {
// x265 private namespace
void setupIntraPrimitives_c(EncoderPrimitives& p)
{
p.cu[BLOCK_4x4].intra_filter = intraFilter<4>;
p.cu[BLOCK_8x8].intra_filter = intraFilter<8>;
p.cu[BLOCK_16x16].intra_filter = intraFilter<16>;
p.cu[BLOCK_32x32].intra_filter = intraFilter<32>;
p.cu[BLOCK_4x4].intra_pred[PLANAR_IDX] = planar_pred_c<2>;
p.cu[BLOCK_8x8].intra_pred[PLANAR_IDX] = planar_pred_c<3>;
p.cu[BLOCK_16x16].intra_pred[PLANAR_IDX] = planar_pred_c<4>;
p.cu[BLOCK_32x32].intra_pred[PLANAR_IDX] = planar_pred_c<5>;
p.cu[BLOCK_4x4].intra_pred[DC_IDX] = intra_pred_dc_c<4>;
p.cu[BLOCK_8x8].intra_pred[DC_IDX] = intra_pred_dc_c<8>;
p.cu[BLOCK_16x16].intra_pred[DC_IDX] = intra_pred_dc_c<16>;
p.cu[BLOCK_32x32].intra_pred[DC_IDX] = intra_pred_dc_c<32>;
for (int i = 2; i < NUM_INTRA_MODE; i++)
{
p.cu[BLOCK_4x4].intra_pred[i] = intra_pred_ang_c<4>;
p.cu[BLOCK_8x8].intra_pred[i] = intra_pred_ang_c<8>;
p.cu[BLOCK_16x16].intra_pred[i] = intra_pred_ang_c<16>;
p.cu[BLOCK_32x32].intra_pred[i] = intra_pred_ang_c<32>;
}
p.cu[BLOCK_4x4].intra_pred_allangs = all_angs_pred_c<2>;
p.cu[BLOCK_8x8].intra_pred_allangs = all_angs_pred_c<3>;
p.cu[BLOCK_16x16].intra_pred_allangs = all_angs_pred_c<4>;
p.cu[BLOCK_32x32].intra_pred_allangs = all_angs_pred_c<5>;
}
}