// Copyright 2019 The libgav1 Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "src/dsp/film_grain.h" #include #include #include #include #include #include #include "src/dsp/common.h" #include "src/dsp/constants.h" #include "src/dsp/dsp.h" #include "src/dsp/film_grain_common.h" #include "src/utils/array_2d.h" #include "src/utils/common.h" #include "src/utils/compiler_attributes.h" #include "src/utils/logging.h" namespace libgav1 { namespace dsp { namespace film_grain { namespace { // Making this a template function prevents it from adding to code size when it // is not placed in the DSP table. Most functions in the dsp directory change // behavior by bitdepth, but because this one doesn't, it receives a dummy // parameter with one enforced value, ensuring only one copy is made. template void InitializeScalingLookupTable_C( int num_points, const uint8_t point_value[], const uint8_t point_scaling[], uint8_t scaling_lut[kScalingLookupTableSize]) { static_assert(singleton == 0, "Improper instantiation of InitializeScalingLookupTable_C. " "There should be only one copy of this function."); if (num_points == 0) { memset(scaling_lut, 0, sizeof(scaling_lut[0]) * kScalingLookupTableSize); return; } static_assert(sizeof(scaling_lut[0]) == 1, ""); memset(scaling_lut, point_scaling[0], point_value[0]); for (int i = 0; i < num_points - 1; ++i) { const int delta_y = point_scaling[i + 1] - point_scaling[i]; const int delta_x = point_value[i + 1] - point_value[i]; const int delta = delta_y * ((65536 + (delta_x >> 1)) / delta_x); for (int x = 0; x < delta_x; ++x) { const int v = point_scaling[i] + ((x * delta + 32768) >> 16); assert(v >= 0 && v <= UINT8_MAX); scaling_lut[point_value[i] + x] = v; } } const uint8_t last_point_value = point_value[num_points - 1]; memset(&scaling_lut[last_point_value], point_scaling[num_points - 1], kScalingLookupTableSize - last_point_value); } // Section 7.18.3.5. // Performs a piecewise linear interpolation into the scaling table. template int ScaleLut(const uint8_t scaling_lut[kScalingLookupTableSize], int index) { const int shift = bitdepth - 8; const int quotient = index >> shift; const int remainder = index - (quotient << shift); if (bitdepth == 8) { assert(quotient < kScalingLookupTableSize); return scaling_lut[quotient]; } assert(quotient + 1 < kScalingLookupTableSize); const int start = scaling_lut[quotient]; const int end = scaling_lut[quotient + 1]; return start + RightShiftWithRounding((end - start) * remainder, shift); } // Applies an auto-regressive filter to the white noise in luma_grain. template void ApplyAutoRegressiveFilterToLumaGrain_C(const FilmGrainParams& params, void* luma_grain_buffer) { auto* luma_grain = static_cast(luma_grain_buffer); const int grain_min = GetGrainMin(); const int grain_max = GetGrainMax(); const int auto_regression_coeff_lag = params.auto_regression_coeff_lag; assert(auto_regression_coeff_lag > 0 && auto_regression_coeff_lag <= 3); // A pictorial representation of the auto-regressive filter for various values // of auto_regression_coeff_lag. The letter 'O' represents the current sample. // (The filter always operates on the current sample with filter // coefficient 1.) The letters 'X' represent the neighboring samples that the // filter operates on. // // auto_regression_coeff_lag == 3: // X X X X X X X // X X X X X X X // X X X X X X X // X X X O // auto_regression_coeff_lag == 2: // X X X X X // X X X X X // X X O // auto_regression_coeff_lag == 1: // X X X // X O // auto_regression_coeff_lag == 0: // O // // Note that if auto_regression_coeff_lag is 0, the filter is the identity // filter and therefore can be skipped. This implementation assumes it is not // called in that case. const int shift = params.auto_regression_shift; for (int y = kAutoRegressionBorder; y < kLumaHeight; ++y) { for (int x = kAutoRegressionBorder; x < kLumaWidth - kAutoRegressionBorder; ++x) { int sum = 0; int pos = 0; int delta_row = -auto_regression_coeff_lag; // The last iteration (delta_row == 0) is shorter and is handled // separately. do { int delta_column = -auto_regression_coeff_lag; do { const int coeff = params.auto_regression_coeff_y[pos]; sum += luma_grain[(y + delta_row) * kLumaWidth + (x + delta_column)] * coeff; ++pos; } while (++delta_column <= auto_regression_coeff_lag); } while (++delta_row < 0); // Last iteration: delta_row == 0. { int delta_column = -auto_regression_coeff_lag; do { const int coeff = params.auto_regression_coeff_y[pos]; sum += luma_grain[y * kLumaWidth + (x + delta_column)] * coeff; ++pos; } while (++delta_column < 0); } luma_grain[y * kLumaWidth + x] = Clip3( luma_grain[y * kLumaWidth + x] + RightShiftWithRounding(sum, shift), grain_min, grain_max); } } } template void ApplyAutoRegressiveFilterToChromaGrains_C(const FilmGrainParams& params, const void* luma_grain_buffer, int subsampling_x, int subsampling_y, void* u_grain_buffer, void* v_grain_buffer) { static_assert( auto_regression_coeff_lag >= 0 && auto_regression_coeff_lag <= 3, "Unsupported autoregression lag for chroma."); const auto* luma_grain = static_cast(luma_grain_buffer); const int grain_min = GetGrainMin(); const int grain_max = GetGrainMax(); auto* u_grain = static_cast(u_grain_buffer); auto* v_grain = static_cast(v_grain_buffer); const int shift = params.auto_regression_shift; const int chroma_height = (subsampling_y == 0) ? kMaxChromaHeight : kMinChromaHeight; const int chroma_width = (subsampling_x == 0) ? kMaxChromaWidth : kMinChromaWidth; for (int y = kAutoRegressionBorder; y < chroma_height; ++y) { const int luma_y = ((y - kAutoRegressionBorder) << subsampling_y) + kAutoRegressionBorder; for (int x = kAutoRegressionBorder; x < chroma_width - kAutoRegressionBorder; ++x) { int sum_u = 0; int sum_v = 0; int pos = 0; int delta_row = -auto_regression_coeff_lag; do { int delta_column = -auto_regression_coeff_lag; do { if (delta_row == 0 && delta_column == 0) { break; } const int coeff_u = params.auto_regression_coeff_u[pos]; const int coeff_v = params.auto_regression_coeff_v[pos]; sum_u += u_grain[(y + delta_row) * chroma_width + (x + delta_column)] * coeff_u; sum_v += v_grain[(y + delta_row) * chroma_width + (x + delta_column)] * coeff_v; ++pos; } while (++delta_column <= auto_regression_coeff_lag); } while (++delta_row <= 0); if (use_luma) { int luma = 0; const int luma_x = ((x - kAutoRegressionBorder) << subsampling_x) + kAutoRegressionBorder; int i = 0; do { int j = 0; do { luma += luma_grain[(luma_y + i) * kLumaWidth + (luma_x + j)]; } while (++j <= subsampling_x); } while (++i <= subsampling_y); luma = SubsampledValue(luma, subsampling_x + subsampling_y); const int coeff_u = params.auto_regression_coeff_u[pos]; const int coeff_v = params.auto_regression_coeff_v[pos]; sum_u += luma * coeff_u; sum_v += luma * coeff_v; } u_grain[y * chroma_width + x] = Clip3( u_grain[y * chroma_width + x] + RightShiftWithRounding(sum_u, shift), grain_min, grain_max); v_grain[y * chroma_width + x] = Clip3( v_grain[y * chroma_width + x] + RightShiftWithRounding(sum_v, shift), grain_min, grain_max); } } } // This implementation is for the condition overlap_flag == false. template void ConstructNoiseStripes_C(const void* grain_buffer, int grain_seed, int width, int height, int subsampling_x, int subsampling_y, void* noise_stripes_buffer) { auto* noise_stripes = static_cast*>(noise_stripes_buffer); const auto* grain = static_cast(grain_buffer); const int half_width = DivideBy2(width + 1); const int half_height = DivideBy2(height + 1); assert(half_width > 0); assert(half_height > 0); static_assert(kLumaWidth == kMaxChromaWidth, "kLumaWidth width should be equal to kMaxChromaWidth"); const int grain_width = (subsampling_x == 0) ? kMaxChromaWidth : kMinChromaWidth; const int plane_width = (width + subsampling_x) >> subsampling_x; constexpr int kNoiseStripeHeight = 34; int luma_num = 0; int y = 0; do { GrainType* const noise_stripe = (*noise_stripes)[luma_num]; uint16_t seed = grain_seed; seed ^= ((luma_num * 37 + 178) & 255) << 8; seed ^= ((luma_num * 173 + 105) & 255); int x = 0; do { const int rand = GetFilmGrainRandomNumber(8, &seed); const int offset_x = rand >> 4; const int offset_y = rand & 15; const int plane_offset_x = (subsampling_x != 0) ? 6 + offset_x : 9 + offset_x * 2; const int plane_offset_y = (subsampling_y != 0) ? 6 + offset_y : 9 + offset_y * 2; int i = 0; do { // Section 7.18.3.5 says: // noiseStripe[ lumaNum ][ 0 ] is 34 samples high and w samples // wide (a few additional samples across are actually written to // the array, but these are never read) ... // // Note: The warning in the parentheses also applies to // noiseStripe[ lumaNum ][ 1 ] and noiseStripe[ lumaNum ][ 2 ]. // // Writes beyond the width of each row could happen below. To // prevent those writes, we clip the number of pixels to copy against // the remaining width. // TODO(petersonab): Allocate aligned stripes with extra width to cover // the size of the final stripe block, then remove this call to min. const int copy_size = std::min(kNoiseStripeHeight >> subsampling_x, plane_width - (x << (1 - subsampling_x))); memcpy(&noise_stripe[i * plane_width + (x << (1 - subsampling_x))], &grain[(plane_offset_y + i) * grain_width + plane_offset_x], copy_size * sizeof(noise_stripe[0])); } while (++i < (kNoiseStripeHeight >> subsampling_y)); x += 16; } while (x < half_width); ++luma_num; y += 16; } while (y < half_height); } // This implementation is for the condition overlap_flag == true. template void ConstructNoiseStripesWithOverlap_C(const void* grain_buffer, int grain_seed, int width, int height, int subsampling_x, int subsampling_y, void* noise_stripes_buffer) { auto* noise_stripes = static_cast*>(noise_stripes_buffer); const auto* grain = static_cast(grain_buffer); const int half_width = DivideBy2(width + 1); const int half_height = DivideBy2(height + 1); assert(half_width > 0); assert(half_height > 0); static_assert(kLumaWidth == kMaxChromaWidth, "kLumaWidth width should be equal to kMaxChromaWidth"); const int grain_width = (subsampling_x == 0) ? kMaxChromaWidth : kMinChromaWidth; const int plane_width = (width + subsampling_x) >> subsampling_x; constexpr int kNoiseStripeHeight = 34; int luma_num = 0; int y = 0; do { GrainType* const noise_stripe = (*noise_stripes)[luma_num]; uint16_t seed = grain_seed; seed ^= ((luma_num * 37 + 178) & 255) << 8; seed ^= ((luma_num * 173 + 105) & 255); // Begin special iteration for x == 0. const int rand = GetFilmGrainRandomNumber(8, &seed); const int offset_x = rand >> 4; const int offset_y = rand & 15; const int plane_offset_x = (subsampling_x != 0) ? 6 + offset_x : 9 + offset_x * 2; const int plane_offset_y = (subsampling_y != 0) ? 6 + offset_y : 9 + offset_y * 2; // The overlap computation only occurs when x > 0, so it is omitted here. int i = 0; do { // TODO(petersonab): Allocate aligned stripes with extra width to cover // the size of the final stripe block, then remove this call to min. const int copy_size = std::min(kNoiseStripeHeight >> subsampling_x, plane_width); memcpy(&noise_stripe[i * plane_width], &grain[(plane_offset_y + i) * grain_width + plane_offset_x], copy_size * sizeof(noise_stripe[0])); } while (++i < (kNoiseStripeHeight >> subsampling_y)); // End special iteration for x == 0. for (int x = 16; x < half_width; x += 16) { const int rand = GetFilmGrainRandomNumber(8, &seed); const int offset_x = rand >> 4; const int offset_y = rand & 15; const int plane_offset_x = (subsampling_x != 0) ? 6 + offset_x : 9 + offset_x * 2; const int plane_offset_y = (subsampling_y != 0) ? 6 + offset_y : 9 + offset_y * 2; int i = 0; do { int j = 0; int grain_sample = grain[(plane_offset_y + i) * grain_width + plane_offset_x]; // The first pixel(s) of each segment of the noise_stripe are subject to // the "overlap" computation. if (subsampling_x == 0) { // Corresponds to the line in the spec: // if (j < 2 && x > 0) // j = 0 int old = noise_stripe[i * plane_width + x * 2]; grain_sample = old * 27 + grain_sample * 17; grain_sample = Clip3(RightShiftWithRounding(grain_sample, 5), GetGrainMin(), GetGrainMax()); noise_stripe[i * plane_width + x * 2] = grain_sample; // This check prevents overwriting for the iteration j = 1. The // continue applies to the i-loop. if (x * 2 + 1 >= plane_width) continue; // j = 1 grain_sample = grain[(plane_offset_y + i) * grain_width + plane_offset_x + 1]; old = noise_stripe[i * plane_width + x * 2 + 1]; grain_sample = old * 17 + grain_sample * 27; grain_sample = Clip3(RightShiftWithRounding(grain_sample, 5), GetGrainMin(), GetGrainMax()); noise_stripe[i * plane_width + x * 2 + 1] = grain_sample; j = 2; } else { // Corresponds to the line in the spec: // if (j == 0 && x > 0) const int old = noise_stripe[i * plane_width + x]; grain_sample = old * 23 + grain_sample * 22; grain_sample = Clip3(RightShiftWithRounding(grain_sample, 5), GetGrainMin(), GetGrainMax()); noise_stripe[i * plane_width + x] = grain_sample; j = 1; } // The following covers the rest of the loop over j as described in the // spec. // // Section 7.18.3.5 says: // noiseStripe[ lumaNum ][ 0 ] is 34 samples high and w samples // wide (a few additional samples across are actually written to // the array, but these are never read) ... // // Note: The warning in the parentheses also applies to // noiseStripe[ lumaNum ][ 1 ] and noiseStripe[ lumaNum ][ 2 ]. // // Writes beyond the width of each row could happen below. To // prevent those writes, we clip the number of pixels to copy against // the remaining width. // TODO(petersonab): Allocate aligned stripes with extra width to cover // the size of the final stripe block, then remove this call to min. const int copy_size = std::min(kNoiseStripeHeight >> subsampling_x, plane_width - (x << (1 - subsampling_x))) - j; memcpy(&noise_stripe[i * plane_width + (x << (1 - subsampling_x)) + j], &grain[(plane_offset_y + i) * grain_width + plane_offset_x + j], copy_size * sizeof(noise_stripe[0])); } while (++i < (kNoiseStripeHeight >> subsampling_y)); } ++luma_num; y += 16; } while (y < half_height); } template inline void WriteOverlapLine_C(const GrainType* noise_stripe_row, const GrainType* noise_stripe_row_prev, int plane_width, int grain_coeff, int old_coeff, GrainType* noise_image_row) { int x = 0; do { int grain = noise_stripe_row[x]; const int old = noise_stripe_row_prev[x]; grain = old * old_coeff + grain * grain_coeff; grain = Clip3(RightShiftWithRounding(grain, 5), GetGrainMin(), GetGrainMax()); noise_image_row[x] = grain; } while (++x < plane_width); } template void ConstructNoiseImageOverlap_C(const void* noise_stripes_buffer, int width, int height, int subsampling_x, int subsampling_y, void* noise_image_buffer) { const auto* noise_stripes = static_cast*>(noise_stripes_buffer); auto* noise_image = static_cast*>(noise_image_buffer); const int plane_width = (width + subsampling_x) >> subsampling_x; const int plane_height = (height + subsampling_y) >> subsampling_y; const int stripe_height = 32 >> subsampling_y; const int stripe_mask = stripe_height - 1; int y = stripe_height; int luma_num = 1; if (subsampling_y == 0) { // Begin complete stripes section. This is when we are guaranteed to have // two overlap rows in each stripe. for (; y < (plane_height & ~stripe_mask); ++luma_num, y += stripe_height) { const GrainType* noise_stripe = (*noise_stripes)[luma_num]; const GrainType* noise_stripe_prev = (*noise_stripes)[luma_num - 1]; // First overlap row. WriteOverlapLine_C(noise_stripe, &noise_stripe_prev[32 * plane_width], plane_width, 17, 27, (*noise_image)[y]); // Second overlap row. WriteOverlapLine_C(&noise_stripe[plane_width], &noise_stripe_prev[(32 + 1) * plane_width], plane_width, 27, 17, (*noise_image)[y + 1]); } // End complete stripes section. const int remaining_height = plane_height - y; // Either one partial stripe remains (remaining_height > 0), // OR image is less than one stripe high (remaining_height < 0), // OR all stripes are completed (remaining_height == 0). if (remaining_height <= 0) { return; } const GrainType* noise_stripe = (*noise_stripes)[luma_num]; const GrainType* noise_stripe_prev = (*noise_stripes)[luma_num - 1]; WriteOverlapLine_C(noise_stripe, &noise_stripe_prev[32 * plane_width], plane_width, 17, 27, (*noise_image)[y]); // Check if second overlap row is in the image. if (remaining_height > 1) { WriteOverlapLine_C(&noise_stripe[plane_width], &noise_stripe_prev[(32 + 1) * plane_width], plane_width, 27, 17, (*noise_image)[y + 1]); } } else { // |subsampling_y| == 1 // No special checks needed for partial stripes, because if one exists, the // first and only overlap row is guaranteed to exist. for (; y < plane_height; ++luma_num, y += stripe_height) { const GrainType* noise_stripe = (*noise_stripes)[luma_num]; const GrainType* noise_stripe_prev = (*noise_stripes)[luma_num - 1]; WriteOverlapLine_C(noise_stripe, &noise_stripe_prev[16 * plane_width], plane_width, 22, 23, (*noise_image)[y]); } } } template void BlendNoiseWithImageLuma_C( const void* noise_image_ptr, int min_value, int max_luma, int scaling_shift, int width, int height, int start_height, const uint8_t scaling_lut_y[kScalingLookupTableSize], const void* source_plane_y, ptrdiff_t source_stride_y, void* dest_plane_y, ptrdiff_t dest_stride_y) { const auto* noise_image = static_cast*>(noise_image_ptr); const auto* in_y = static_cast(source_plane_y); source_stride_y /= sizeof(Pixel); auto* out_y = static_cast(dest_plane_y); dest_stride_y /= sizeof(Pixel); int y = 0; do { int x = 0; do { const int orig = in_y[y * source_stride_y + x]; int noise = noise_image[kPlaneY][y + start_height][x]; noise = RightShiftWithRounding( ScaleLut(scaling_lut_y, orig) * noise, scaling_shift); out_y[y * dest_stride_y + x] = Clip3(orig + noise, min_value, max_luma); } while (++x < width); } while (++y < height); } // This function is for the case params_.chroma_scaling_from_luma == false. template void BlendNoiseWithImageChroma_C( Plane plane, const FilmGrainParams& params, const void* noise_image_ptr, int min_value, int max_chroma, int width, int height, int start_height, int subsampling_x, int subsampling_y, const uint8_t scaling_lut_uv[kScalingLookupTableSize], const void* source_plane_y, ptrdiff_t source_stride_y, const void* source_plane_uv, ptrdiff_t source_stride_uv, void* dest_plane_uv, ptrdiff_t dest_stride_uv) { const auto* noise_image = static_cast*>(noise_image_ptr); const int chroma_width = (width + subsampling_x) >> subsampling_x; const int chroma_height = (height + subsampling_y) >> subsampling_y; const auto* in_y = static_cast(source_plane_y); source_stride_y /= sizeof(Pixel); const auto* in_uv = static_cast(source_plane_uv); source_stride_uv /= sizeof(Pixel); auto* out_uv = static_cast(dest_plane_uv); dest_stride_uv /= sizeof(Pixel); const int offset = (plane == kPlaneU) ? params.u_offset : params.v_offset; const int luma_multiplier = (plane == kPlaneU) ? params.u_luma_multiplier : params.v_luma_multiplier; const int multiplier = (plane == kPlaneU) ? params.u_multiplier : params.v_multiplier; const int scaling_shift = params.chroma_scaling; start_height >>= subsampling_y; int y = 0; do { int x = 0; do { const int luma_x = x << subsampling_x; const int luma_y = y << subsampling_y; const int luma_next_x = std::min(luma_x + 1, width - 1); int average_luma; if (subsampling_x != 0) { average_luma = RightShiftWithRounding( in_y[luma_y * source_stride_y + luma_x] + in_y[luma_y * source_stride_y + luma_next_x], 1); } else { average_luma = in_y[luma_y * source_stride_y + luma_x]; } const int orig = in_uv[y * source_stride_uv + x]; const int combined = average_luma * luma_multiplier + orig * multiplier; const int merged = Clip3((combined >> 6) + LeftShift(offset, bitdepth - 8), 0, (1 << bitdepth) - 1); int noise = noise_image[plane][y + start_height][x]; noise = RightShiftWithRounding( ScaleLut(scaling_lut_uv, merged) * noise, scaling_shift); out_uv[y * dest_stride_uv + x] = Clip3(orig + noise, min_value, max_chroma); } while (++x < chroma_width); } while (++y < chroma_height); } // This function is for the case params_.chroma_scaling_from_luma == true. // This further implies that scaling_lut_u == scaling_lut_v == scaling_lut_y. template void BlendNoiseWithImageChromaWithCfl_C( Plane plane, const FilmGrainParams& params, const void* noise_image_ptr, int min_value, int max_chroma, int width, int height, int start_height, int subsampling_x, int subsampling_y, const uint8_t scaling_lut[kScalingLookupTableSize], const void* source_plane_y, ptrdiff_t source_stride_y, const void* source_plane_uv, ptrdiff_t source_stride_uv, void* dest_plane_uv, ptrdiff_t dest_stride_uv) { const auto* noise_image = static_cast*>(noise_image_ptr); const auto* in_y = static_cast(source_plane_y); source_stride_y /= sizeof(Pixel); const auto* in_uv = static_cast(source_plane_uv); source_stride_uv /= sizeof(Pixel); auto* out_uv = static_cast(dest_plane_uv); dest_stride_uv /= sizeof(Pixel); const int chroma_width = (width + subsampling_x) >> subsampling_x; const int chroma_height = (height + subsampling_y) >> subsampling_y; const int scaling_shift = params.chroma_scaling; start_height >>= subsampling_y; int y = 0; do { int x = 0; do { const int luma_x = x << subsampling_x; const int luma_y = y << subsampling_y; const int luma_next_x = std::min(luma_x + 1, width - 1); int average_luma; if (subsampling_x != 0) { average_luma = RightShiftWithRounding( in_y[luma_y * source_stride_y + luma_x] + in_y[luma_y * source_stride_y + luma_next_x], 1); } else { average_luma = in_y[luma_y * source_stride_y + luma_x]; } const int orig_uv = in_uv[y * source_stride_uv + x]; int noise_uv = noise_image[plane][y + start_height][x]; noise_uv = RightShiftWithRounding( ScaleLut(scaling_lut, average_luma) * noise_uv, scaling_shift); out_uv[y * dest_stride_uv + x] = Clip3(orig_uv + noise_uv, min_value, max_chroma); } while (++x < chroma_width); } while (++y < chroma_height); } void Init8bpp() { Dsp* const dsp = dsp_internal::GetWritableDspTable(8); assert(dsp != nullptr); #if LIBGAV1_ENABLE_ALL_DSP_FUNCTIONS // LumaAutoRegressionFunc dsp->film_grain.luma_auto_regression[0] = ApplyAutoRegressiveFilterToLumaGrain_C<8, int8_t>; dsp->film_grain.luma_auto_regression[1] = ApplyAutoRegressiveFilterToLumaGrain_C<8, int8_t>; dsp->film_grain.luma_auto_regression[2] = ApplyAutoRegressiveFilterToLumaGrain_C<8, int8_t>; // ChromaAutoRegressionFunc // Chroma autoregression should never be called when lag is 0 and use_luma is // false. dsp->film_grain.chroma_auto_regression[0][0] = nullptr; dsp->film_grain.chroma_auto_regression[0][1] = ApplyAutoRegressiveFilterToChromaGrains_C<8, int8_t, 1, false>; dsp->film_grain.chroma_auto_regression[0][2] = ApplyAutoRegressiveFilterToChromaGrains_C<8, int8_t, 2, false>; dsp->film_grain.chroma_auto_regression[0][3] = ApplyAutoRegressiveFilterToChromaGrains_C<8, int8_t, 3, false>; dsp->film_grain.chroma_auto_regression[1][0] = ApplyAutoRegressiveFilterToChromaGrains_C<8, int8_t, 0, true>; dsp->film_grain.chroma_auto_regression[1][1] = ApplyAutoRegressiveFilterToChromaGrains_C<8, int8_t, 1, true>; dsp->film_grain.chroma_auto_regression[1][2] = ApplyAutoRegressiveFilterToChromaGrains_C<8, int8_t, 2, true>; dsp->film_grain.chroma_auto_regression[1][3] = ApplyAutoRegressiveFilterToChromaGrains_C<8, int8_t, 3, true>; // ConstructNoiseStripesFunc dsp->film_grain.construct_noise_stripes[0] = ConstructNoiseStripes_C<8, int8_t>; dsp->film_grain.construct_noise_stripes[1] = ConstructNoiseStripesWithOverlap_C<8, int8_t>; // ConstructNoiseImageOverlapFunc dsp->film_grain.construct_noise_image_overlap = ConstructNoiseImageOverlap_C<8, int8_t>; // InitializeScalingLutFunc dsp->film_grain.initialize_scaling_lut = InitializeScalingLookupTable_C<0>; // BlendNoiseWithImageLumaFunc dsp->film_grain.blend_noise_luma = BlendNoiseWithImageLuma_C<8, int8_t, uint8_t>; // BlendNoiseWithImageChromaFunc dsp->film_grain.blend_noise_chroma[0] = BlendNoiseWithImageChroma_C<8, int8_t, uint8_t>; dsp->film_grain.blend_noise_chroma[1] = BlendNoiseWithImageChromaWithCfl_C<8, int8_t, uint8_t>; #else // !LIBGAV1_ENABLE_ALL_DSP_FUNCTIONS static_cast(dsp); #ifndef LIBGAV1_Dsp8bpp_FilmGrainAutoregressionLuma dsp->film_grain.luma_auto_regression[0] = ApplyAutoRegressiveFilterToLumaGrain_C<8, int8_t>; dsp->film_grain.luma_auto_regression[1] = ApplyAutoRegressiveFilterToLumaGrain_C<8, int8_t>; dsp->film_grain.luma_auto_regression[2] = ApplyAutoRegressiveFilterToLumaGrain_C<8, int8_t>; #endif #ifndef LIBGAV1_Dsp8bpp_FilmGrainAutoregressionChroma // Chroma autoregression should never be called when lag is 0 and use_luma is // false. dsp->film_grain.chroma_auto_regression[0][0] = nullptr; dsp->film_grain.chroma_auto_regression[0][1] = ApplyAutoRegressiveFilterToChromaGrains_C<8, int8_t, 1, false>; dsp->film_grain.chroma_auto_regression[0][2] = ApplyAutoRegressiveFilterToChromaGrains_C<8, int8_t, 2, false>; dsp->film_grain.chroma_auto_regression[0][3] = ApplyAutoRegressiveFilterToChromaGrains_C<8, int8_t, 3, false>; dsp->film_grain.chroma_auto_regression[1][0] = ApplyAutoRegressiveFilterToChromaGrains_C<8, int8_t, 0, true>; dsp->film_grain.chroma_auto_regression[1][1] = ApplyAutoRegressiveFilterToChromaGrains_C<8, int8_t, 1, true>; dsp->film_grain.chroma_auto_regression[1][2] = ApplyAutoRegressiveFilterToChromaGrains_C<8, int8_t, 2, true>; dsp->film_grain.chroma_auto_regression[1][3] = ApplyAutoRegressiveFilterToChromaGrains_C<8, int8_t, 3, true>; #endif #ifndef LIBGAV1_Dsp8bpp_FilmGrainConstructNoiseStripes dsp->film_grain.construct_noise_stripes[0] = ConstructNoiseStripes_C<8, int8_t>; dsp->film_grain.construct_noise_stripes[1] = ConstructNoiseStripesWithOverlap_C<8, int8_t>; #endif #ifndef LIBGAV1_Dsp8bpp_FilmGrainConstructNoiseImageOverlap dsp->film_grain.construct_noise_image_overlap = ConstructNoiseImageOverlap_C<8, int8_t>; #endif #ifndef LIBGAV1_Dsp8bpp_FilmGrainInitializeScalingLutFunc dsp->film_grain.initialize_scaling_lut = InitializeScalingLookupTable_C<0>; #endif #ifndef LIBGAV1_Dsp8bpp_FilmGrainBlendNoiseLuma dsp->film_grain.blend_noise_luma = BlendNoiseWithImageLuma_C<8, int8_t, uint8_t>; #endif #ifndef LIBGAV1_Dsp8bpp_FilmGrainBlendNoiseChroma dsp->film_grain.blend_noise_chroma[0] = BlendNoiseWithImageChroma_C<8, int8_t, uint8_t>; #endif #ifndef LIBGAV1_Dsp8bpp_FilmGrainBlendNoiseChromaWithCfl dsp->film_grain.blend_noise_chroma[1] = BlendNoiseWithImageChromaWithCfl_C<8, int8_t, uint8_t>; #endif #endif // LIBGAV1_ENABLE_ALL_DSP_FUNCTIONS } #if LIBGAV1_MAX_BITDEPTH >= 10 void Init10bpp() { Dsp* const dsp = dsp_internal::GetWritableDspTable(10); assert(dsp != nullptr); #if LIBGAV1_ENABLE_ALL_DSP_FUNCTIONS // LumaAutoRegressionFunc dsp->film_grain.luma_auto_regression[0] = ApplyAutoRegressiveFilterToLumaGrain_C<10, int16_t>; dsp->film_grain.luma_auto_regression[1] = ApplyAutoRegressiveFilterToLumaGrain_C<10, int16_t>; dsp->film_grain.luma_auto_regression[2] = ApplyAutoRegressiveFilterToLumaGrain_C<10, int16_t>; // ChromaAutoRegressionFunc // Chroma autoregression should never be called when lag is 0 and use_luma is // false. dsp->film_grain.chroma_auto_regression[0][0] = nullptr; dsp->film_grain.chroma_auto_regression[0][1] = ApplyAutoRegressiveFilterToChromaGrains_C<10, int16_t, 1, false>; dsp->film_grain.chroma_auto_regression[0][2] = ApplyAutoRegressiveFilterToChromaGrains_C<10, int16_t, 2, false>; dsp->film_grain.chroma_auto_regression[0][3] = ApplyAutoRegressiveFilterToChromaGrains_C<10, int16_t, 3, false>; dsp->film_grain.chroma_auto_regression[1][0] = ApplyAutoRegressiveFilterToChromaGrains_C<10, int16_t, 0, true>; dsp->film_grain.chroma_auto_regression[1][1] = ApplyAutoRegressiveFilterToChromaGrains_C<10, int16_t, 1, true>; dsp->film_grain.chroma_auto_regression[1][2] = ApplyAutoRegressiveFilterToChromaGrains_C<10, int16_t, 2, true>; dsp->film_grain.chroma_auto_regression[1][3] = ApplyAutoRegressiveFilterToChromaGrains_C<10, int16_t, 3, true>; // ConstructNoiseStripesFunc dsp->film_grain.construct_noise_stripes[0] = ConstructNoiseStripes_C<10, int16_t>; dsp->film_grain.construct_noise_stripes[1] = ConstructNoiseStripesWithOverlap_C<10, int16_t>; // ConstructNoiseImageOverlapFunc dsp->film_grain.construct_noise_image_overlap = ConstructNoiseImageOverlap_C<10, int16_t>; // InitializeScalingLutFunc dsp->film_grain.initialize_scaling_lut = InitializeScalingLookupTable_C<0>; // BlendNoiseWithImageLumaFunc dsp->film_grain.blend_noise_luma = BlendNoiseWithImageLuma_C<10, int16_t, uint16_t>; // BlendNoiseWithImageChromaFunc dsp->film_grain.blend_noise_chroma[0] = BlendNoiseWithImageChroma_C<10, int16_t, uint16_t>; dsp->film_grain.blend_noise_chroma[1] = BlendNoiseWithImageChromaWithCfl_C<10, int16_t, uint16_t>; #else // !LIBGAV1_ENABLE_ALL_DSP_FUNCTIONS static_cast(dsp); #ifndef LIBGAV1_Dsp10bpp_FilmGrainAutoregressionLuma dsp->film_grain.luma_auto_regression[0] = ApplyAutoRegressiveFilterToLumaGrain_C<10, int16_t>; dsp->film_grain.luma_auto_regression[1] = ApplyAutoRegressiveFilterToLumaGrain_C<10, int16_t>; dsp->film_grain.luma_auto_regression[2] = ApplyAutoRegressiveFilterToLumaGrain_C<10, int16_t>; #endif #ifndef LIBGAV1_Dsp10bpp_FilmGrainAutoregressionChroma // Chroma autoregression should never be called when lag is 0 and use_luma is // false. dsp->film_grain.chroma_auto_regression[0][0] = nullptr; dsp->film_grain.chroma_auto_regression[0][1] = ApplyAutoRegressiveFilterToChromaGrains_C<10, int16_t, 1, false>; dsp->film_grain.chroma_auto_regression[0][2] = ApplyAutoRegressiveFilterToChromaGrains_C<10, int16_t, 2, false>; dsp->film_grain.chroma_auto_regression[0][3] = ApplyAutoRegressiveFilterToChromaGrains_C<10, int16_t, 3, false>; dsp->film_grain.chroma_auto_regression[1][0] = ApplyAutoRegressiveFilterToChromaGrains_C<10, int16_t, 0, true>; dsp->film_grain.chroma_auto_regression[1][1] = ApplyAutoRegressiveFilterToChromaGrains_C<10, int16_t, 1, true>; dsp->film_grain.chroma_auto_regression[1][2] = ApplyAutoRegressiveFilterToChromaGrains_C<10, int16_t, 2, true>; dsp->film_grain.chroma_auto_regression[1][3] = ApplyAutoRegressiveFilterToChromaGrains_C<10, int16_t, 3, true>; #endif #ifndef LIBGAV1_Dsp10bpp_FilmGrainConstructNoiseStripes dsp->film_grain.construct_noise_stripes[0] = ConstructNoiseStripes_C<10, int16_t>; dsp->film_grain.construct_noise_stripes[1] = ConstructNoiseStripesWithOverlap_C<10, int16_t>; #endif #ifndef LIBGAV1_Dsp10bpp_FilmGrainConstructNoiseImageOverlap dsp->film_grain.construct_noise_image_overlap = ConstructNoiseImageOverlap_C<10, int16_t>; #endif #ifndef LIBGAV1_Dsp10bpp_FilmGrainInitializeScalingLutFunc dsp->film_grain.initialize_scaling_lut = InitializeScalingLookupTable_C<0>; #endif #ifndef LIBGAV1_Dsp10bpp_FilmGrainBlendNoiseLuma dsp->film_grain.blend_noise_luma = BlendNoiseWithImageLuma_C<10, int16_t, uint16_t>; #endif #ifndef LIBGAV1_Dsp10bpp_FilmGrainBlendNoiseChroma dsp->film_grain.blend_noise_chroma[0] = BlendNoiseWithImageChroma_C<10, int16_t, uint16_t>; #endif #ifndef LIBGAV1_Dsp10bpp_FilmGrainBlendNoiseChromaWithCfl dsp->film_grain.blend_noise_chroma[1] = BlendNoiseWithImageChromaWithCfl_C<10, int16_t, uint16_t>; #endif #endif // LIBGAV1_ENABLE_ALL_DSP_FUNCTIONS } #endif // LIBGAV1_MAX_BITDEPTH >= 10 } // namespace } // namespace film_grain void FilmGrainInit_C() { film_grain::Init8bpp(); #if LIBGAV1_MAX_BITDEPTH >= 10 film_grain::Init10bpp(); #endif } } // namespace dsp } // namespace libgav1