Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  grain_synthesis.c   Sprache: C

 
/*
 * Copyright (c) 2016, Alliance for Open Media. All rights reserved.
 *
 * This source code is subject to the terms of the BSD 2 Clause License and
 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
 * was not distributed with this source code in the LICENSE file, you can
 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
 * Media Patent License 1.0 was not distributed with this source code in the
 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
 */


/*!\file
 * \brief Describes film grain parameters and film grain synthesis
 *
 */


#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "aom_dsp/aom_dsp_common.h"
#include "aom_mem/aom_mem.h"
#include "av1/decoder/grain_synthesis.h"

// Samples with Gaussian distribution in the range of [-2048, 2047] (12 bits)
// with zero mean and standard deviation of about 512.
// should be divided by 4 for 10-bit range and 16 for 8-bit range.
static const int gaussian_sequence[2048] = {
  56,    568,   -180,  172,   124,   -84,   172,   -64,   -900,  24,   820,
  224,   1248,  996,   272,   -8,    -916,  -388,  -732,  -104,  -188, 800,
  112,   -652,  -320,  -376,  140,   -252,  492,   -168,  44,    -788, 588,
  -584,  500,   -228,  12,    680,   272,   -476,  972,   -100,  652,  368,
  432,   -196,  -720,  -192,  1000,  -332,  652,   -136,  -552,  -604, -4,
  192,   -220,  -136,  1000,  -52,   372,   -96,   -624,  124,   -24,  396,
  540,   -12,   -104,  640,   464,   244,   -208,  -84,   368,   -528, -740,
  248,   -968,  -848,  608,   376,   -60,   -292,  -40,   -156,  252,  -292,
  248,   224,   -280,  400,   -244,  244,   -60,   76,    -80,   212,  532,
  340,   128,   -36,   824,   -352,  -60,   -264,  -96,   -612,  416,  -704,
  220,   -204,  640,   -160,  1220,  -408,  900,   336,   20,    -336, -96,
  -792,  304,   48,    -28,   -1232, -1172, -448,  104,   -292,  -520, 244,
  60,    -948,  0,     -708,  268,   108,   356,   -548,  488,   -344, -136,
  488,   -196,  -224,  656,   -236,  -1128, 60,    4,     140,   276,  -676,
  -376,  168,   -108,  464,   8,     564,   64,    240,   308,   -300, -400,
  -456,  -136,  56,    120,   -408,  -116,  436,   504,   -232,  328,  844,
  -164,  -84,   784,   -168,  232,   -224,  348,   -376,  128,   568,  96,
  -1244, -288,  276,   848,   832,   -360,  656,   464,   -384,  -332, -356,
  728,   -388,  160,   -192,  468,   296,   224,   140,   -776,  -100, 280,
  4,     196,   44,    -36,   -648,  932,   16,    1428,  28,    528,  808,
  772,   20,    268,   88,    -332,  -284,  124,   -384,  -448,  208,  -228,
  -1044, -328,  660,   380,   -148,  -300,  588,   240,   540,   28,   136,
  -88,   -436,  256,   296,   -1000, 1400,  0,     -48,   1056,  -136, 264,
  -528,  -1108, 632,   -484,  -592,  -344,  796,   124,   -668,  -768, 388,
  1296,  -232,  -188,  -200,  -288,  -4,    308,   100,   -168,  256,  -500,
  204,   -508,  648,   -136,  372,   -272,  -120,  -1004, -552,  -548, -384,
  548,   -296,  428,   -108,  -8,    -912,  -324,  -224,  -88,   -112, -220,
  -100,  996,   -796,  548,   360,   -216,  180,   428,   -200,  -212, 148,
  96,    148,   284,   216,   -412,  -320,  120,   -300,  -384,  -604, -572,
  -332,  -8,    -180,  -176,  696,   116,   -88,   628,   76,    44,   -516,
  240,   -208,  -40,   100,   -592,  344,   -308,  -452,  -228,  20,   916,
  -1752, -136,  -340,  -804,  140,   40,    512,   340,   248,   184,  -492,
  896,   -156,  932,   -628,  328,   -688,  -448,  -616,  -752,  -100, 560,
  -1020, 180,   -800,  -64,   76,    576,   1068,  396,   660,   552,  -108,
  -28,   320,   -628,  312,   -92,   -92,   -472,  268,   16,    560,  516,
  -672,  -52,   492,   -100,  260,   384,   284,   292,   304,   -148, 88,
  -152,  1012,  1064,  -228,  164,   -376,  -684,  592,   -392,  156,  196,
  -524,  -64,   -884,  160,   -176,  636,   648,   404,   -396,  -436, 864,
  424,   -728,  988,   -604,  904,   -592,  296,   -224,  536,   -176, -920,
  436,   -48,   1176,  -884,  416,   -776,  -824,  -884,  524,   -548, -564,
  -68,   -164,  -96,   692,   364,   -692,  -1012, -68,   260,   -480, 876,
  -1116, 452,   -332,  -352,  892,   -1088, 1220,  -676,  12,    -292, 244,
  496,   372,   -32,   280,   200,   112,   -440,  -96,   24,    -644, -184,
  56,    -432,  224,   -980,  272,   -260,  144,   -436,  420,   356,  364,
  -528,  76,    172,   -744,  -368,  404,   -752,  -416,  684,   -688, 72,
  540,   416,   92,    444,   480,   -72,   -1416, 164,   -1172, -68,  24,
  424,   264,   1040,  128,   -912,  -524,  -356,  64,    876,   -12,  4,
  -88,   532,   272,   -524,  320,   276,   -508,  940,   24,    -400, -120,
  756,   60,    236,   -412,  100,   376,   -484,  400,   -100,  -740, -108,
  -260,  328,   -268,  224,   -200,  -416,  184,   -604,  -564,  -20,  296,
  60,    892,   -888,  60,    164,   68,    -760,  216,   -296,  904,  -336,
  -28,   404,   -356,  -568,  -208,  -1480, -512,  296,   328,   -360, -164,
  -1560, -776,  1156,  -428,  164,   -504,  -112,  120,   -216,  -148, -264,
  308,   32,    64,    -72,   72,    116,   176,   -64,   -272,  460,  -536,
  -784,  -280,  348,   108,   -752,  -132,  524,   -540,  -776,  116,  -296,
  -1196, -288,  -560,  1040,  -472,  116,   -848,  -1116, 116,   636,  696,
  284,   -176,  1016,  204,   -864,  -648,  -248,  356,   972,   -584, -204,
  264,   880,   528,   -24,   -184,  116,   448,   -144,  828,   524,  212,
  -212,  52,    12,    200,   268,   -488,  -404,  -880,  824,   -672, -40,
  908,   -248,  500,   716,   -576,  492,   -576,  16,    720,   -108, 384,
  124,   344,   280,   576,   -500,  252,   104,   -308,  196,   -188, -8,
  1268,  296,   1032,  -1196, 436,   316,   372,   -432,  -200,  -660, 704,
  -224,  596,   -132,  268,   32,    -452,  884,   104,   -1008, 424,  -1348,
  -280,  4,     -1168, 368,   476,   696,   300,   -8,    24,    180,  -592,
  -196,  388,   304,   500,   724,   -160,  244,   -84,   272,   -256, -420,
  320,   208,   -144,  -156,  156,   364,   452,   28,    540,   316,  220,
  -644,  -248,  464,   72,    360,   32,    -388,  496,   -680,  -48,  208,
  -116,  -408,  60,    -604,  -392,  548,   -840,  784,   -460,  656,  -544,
  -388,  -264,  908,   -800,  -628,  -612,  -568,  572,   -220,  164,  288,
  -16,   -308,  308,   -112,  -636,  -760,  280,   -668,  432,   364,  240,
  -196,  604,   340,   384,   196,   592,   -44,   -500,  432,   -580, -132,
  636,   -76,   392,   4,     -412,  540,   508,   328,   -356,  -36,  16,
  -220,  -64,   -248,  -60,   24,    -192,  368,   1040,  92,    -24,  -1044,
  -32,   40,    104,   148,   192,   -136,  -520,  56,    -816,  -224, 732,
  392,   356,   212,   -80,   -424,  -1008, -324,  588,   -1496, 576,  460,
  -816,  -848,  56,    -580,  -92,   -1372, -112,  -496,  200,   364,  52,
  -140,  48,    -48,   -60,   84,    72,    40,    132,   -356,  -268, -104,
  -284,  -404,  732,   -520,  164,   -304,  -540,  120,   328,   -76,  -460,
  756,   388,   588,   236,   -436,  -72,   -176,  -404,  -316,  -148, 716,
  -604,  404,   -72,   -88,   -888,  -68,   944,   88,    -220,  -344, 960,
  472,   460,   -232,  704,   120,   832,   -228,  692,   -508,  132,  -476,
  844,   -748,  -364,  -44,   1116,  -1104, -1056, 76,    428,   552,  -692,
  60,    356,   96,    -384,  -188,  -612,  -576,  736,   508,   892,  352,
  -1132, 504,   -24,   -352,  324,   332,   -600,  -312,  292,   508,  -144,
  -8,    484,   48,    284,   -260,  -240,  256,   -100,  -292,  -204, -44,
  472,   -204,  908,   -188,  -1000, -256,  92,    1164,  -392,  564,  356,
  652,   -28,   -884,  256,   484,   -192,  760,   -176,  376,   -524, -452,
  -436,  860,   -736,  212,   124,   504,   -476,  468,   76,    -472, 552,
  -692,  -944,  -620,  740,   -240,  400,   132,   20,    192,   -196, 264,
  -668,  -1012, -60,   296,   -316,  -828,  76,    -156,  284,   -768, -448,
  -832,  148,   248,   652,   616,   1236,  288,   -328,  -400,  -124, 588,
  220,   520,   -696,  1032,  768,   -740,  -92,   -272,  296,   448,  -464,
  412,   -200,  392,   440,   -200,  264,   -152,  -260,  320,   1032, 216,
  320,   -8,    -64,   156,   -1016, 1084,  1172,  536,   484,   -432, 132,
  372,   -52,   -256,  84,    116,   -352,  48,    116,   304,   -384, 412,
  924,   -300,  528,   628,   180,   648,   44,    -980,  -220,  1320, 48,
  332,   748,   524,   -268,  -720,  540,   -276,  564,   -344,  -208, -196,
  436,   896,   88,    -392,  132,   80,    -964,  -288,  568,   56,   -48,
  -456,  888,   8,     552,   -156,  -292,  948,   288,   128,   -716, -292,
  1192,  -152,  876,   352,   -600,  -260,  -812,  -468,  -28,   -120, -32,
  -44,   1284,  496,   192,   464,   312,   -76,   -516,  -380,  -456, -1012,
  -48,   308,   -156,  36,    492,   -156,  -808,  188,   1652,  68,   -120,
  -116,  316,   160,   -140,  352,   808,   -416,  592,   316,   -480, 56,
  528,   -204,  -568,  372,   -232,  752,   -344,  744,   -4,    324,  -416,
  -600,  768,   268,   -248,  -88,   -132,  -420,  -432,  80,    -288, 404,
  -316,  -1216, -588,  520,   -108,  92,    -320,  368,   -480,  -216, -92,
  1688,  -300,  180,   1020,  -176,  820,   -68,   -228,  -260,  436,  -904,
  20,    40,    -508,  440,   -736,  312,   332,   204,   760,   -372, 728,
  96,    -20,   -632,  -520,  -560,  336,   1076,  -64,   -532,  776,  584,
  192,   396,   -728,  -520,  276,   -188,  80,    -52,   -612,  -252, -48,
  648,   212,   -688,  228,   -52,   -260,  428,   -412,  -272,  -404, 180,
  816,   -796,  48,    152,   484,   -88,   -216,  988,   696,   188,  -528,
  648,   -116,  -180,  316,   476,   12,    -564,  96,    476,   -252, -364,
  -376,  -392,  556,   -256,  -576,  260,   -352,  120,   -16,   -136, -260,
  -492,  72,    556,   660,   580,   616,   772,   436,   424,   -32,  -324,
  -1268, 416,   -324,  -80,   920,   160,   228,   724,   32,    -516, 64,
  384,   68,    -128,  136,   240,   248,   -204,  -68,   252,   -932, -120,
  -480,  -628,  -84,   192,   852,   -404,  -288,  -132,  204,   100,  168,
  -68,   -196,  -868,  460,   1080,  380,   -80,   244,   0,     484,  -888,
  64,    184,   352,   600,   460,   164,   604,   -196,  320,   -64,  588,
  -184,  228,   12,    372,   48,    -848,  -344,  224,   208,   -200, 484,
  128,   -20,   272,   -468,  -840,  384,   256,   -720,  -520,  -464, -580,
  112,   -120,  644,   -356,  -208,  -608,  -528,  704,   560,   -424, 392,
  828,   40,    84,    200,   -152,  0,     -144,  584,   280,   -120, 80,
  -556,  -972,  -196,  -472,  724,   80,    168,   -32,   88,    160,  -688,
  0,     160,   356,   372,   -776,  740,   -128,  676,   -248,  -480, 4,
  -364,  96,    544,   232,   -1032, 956,   236,   356,   20,    -40,  300,
  24,    -676,  -596,  132,   1120,  -104,  532,   -1096, 568,   648,  444,
  508,   380,   188,   -376,  -604,  1488,  424,   24,    756,   -220, -192,
  716,   120,   920,   688,   168,   44,    -460,  568,   284,   1144, 1160,
  600,   424,   888,   656,   -356,  -320,  220,   316,   -176,  -724, -188,
  -816,  -628,  -348,  -228,  -380,  1012,  -452,  -660,  736,   928,  404,
  -696,  -72,   -268,  -892,  128,   184,   -344,  -780,  360,   336,  400,
  344,   428,   548,   -112,  136,   -228,  -216,  -820,  -516,  340,  92,
  -136,  116,   -300,  376,   -244,  100,   -316,  -520,  -284,  -12,  824,
  164,   -548,  -180,  -128,  116,   -924,  -828,  268,   -368,  -580, 620,
  192,   160,   0,     -1676, 1068,  424,   -56,   -360,  468,   -156, 720,
  288,   -528,  556,   -364,  548,   -148,  504,   316,   152,   -648, -620,
  -684,  -24,   -376,  -384,  -108,  -920,  -1032, 768,   180,   -264, -508,
  -1268, -260,  -60,   300,   -240,  988,   724,   -376,  -576,  -212, -736,
  556,   192,   1092,  -620,  -880,  376,   -56,   -4,    -216,  -32,  836,
  268,   396,   1332,  864,   -600,  100,   56,    -412,  -92,   356,  180,
  884,   -468,  -436,  292,   -388,  -804,  -704,  -840,  368,   -348, 140,
  -724,  1536,  940,   372,   112,   -372,  436,   -480,  1136,  296,  -32,
  -228,  132,   -48,   -220,  868,   -1016, -60,   -1044, -464,  328,  916,
  244,   12,    -736,  -296,  360,   468,   -376,  -108,  -92,   788,  368,
  -56,   544,   400,   -672,  -420,  728,   16,    320,   44,    -284, -380,
  -796,  488,   132,   204,   -596,  -372,  88,    -152,  -908,  -636, -572,
  -624,  -116,  -692,  -200,  -56,   276,   -88,   484,   -324,  948,  864,
  1000,  -456,  -184,  -276,  292,   -296,  156,   676,   320,   160,  908,
  -84,   -1236, -288,  -116,  260,   -372,  -644,  732,   -756,  -96,  84,
  344,   -520,  348,   -688,  240,   -84,   216,   -1044, -136,  -676, -396,
  -1500, 960,   -40,   176,   168,   1516,  420,   -504,  -344,  -364, -360,
  1216,  -940,  -380,  -212,  252,   -660,  -708,  484,   -444,  -152, 928,
  -120,  1112,  476,   -260,  560,   -148,  -344,  108,   -196,  228,  -288,
  504,   560,   -328,  -88,   288,   -1008, 460,   -228,  468,   -836, -196,
  76,    388,   232,   412,   -1168, -716,  -644,  756,   -172,  -356, -504,
  116,   432,   528,   48,    476,   -168,  -608,  448,   160,   -532, -272,
  28,    -676,  -12,   828,   980,   456,   520,   104,   -104,  256,  -344,
  -4,    -28,   -368,  -52,   -524,  -572,  -556,  -200,  768,   1124, -208,
  -512,  176,   232,   248,   -148,  -888,  604,   -600,  -304,  804,  -156,
  -212,  488,   -192,  -804,  -256,  368,   -360,  -916,  -328,  228,  -240,
  -448,  -472,  856,   -556,  -364,  572,   -12,   -156,  -368,  -340, 432,
  252,   -752,  -152,  288,   268,   -580,  -848,  -592,  108,   -76,  244,
  312,   -716,  592,   -80,   436,   360,   4,     -248,  160,   516,  584,
  732,   44,    -468,  -280,  -292,  -156,  -588,  28,    308,   912,  24,
  124,   156,   180,   -252,  944,   -924,  -772,  -520,  -428,  -624, 300,
  -212,  -1144, 32,    -724,  800,   -1128, -212,  -1288, -848,  180,  -416,
  440,   192,   -576,  -792,  -76,   -1080, 80,    -532,  -352,  -132, 380,
  -820,  148,   1112,  128,   164,   456,   700,   -924,  144,   -668, -384,
  648,   -832,  508,   552,   -52,   -100,  -656,  208,   -568,  748,  -88,
  680,   232,   300,   192,   -408,  -1012, -152,  -252,  -268,  272,  -876,
  -664,  -648,  -332,  -136,  16,    12,    1152,  -28,   332,   -536, 320,
  -672,  -460,  -316,  532,   -260,  228,   -40,   1052,  -816,  180,  88,
  -496,  -556,  -672,  -368,  428,   92,    356,   404,   -408,  252,  196,
  -176,  -556,  792,   268,   32,    372,   40,    96,    -332,  328,  120,
  372,   -900,  -40,   472,   -264,  -592,  952,   128,   656,   112,  664,
  -232,  420,   4,     -344,  -464,  556,   244,   -416,  -32,   252,  0,
  -412,  188,   -696,  508,   -476,  324,   -1096, 656,   -312,  560,  264,
  -136,  304,   160,   -64,   -580,  248,   336,   -720,  560,   -348, -288,
  -276,  -196,  -500,  852,   -544,  -236,  -1128, -992,  -776,  116,  56,
  52,    860,   884,   212,   -12,   168,   1020,  512,   -552,  924,  -148,
  716,   188,   164,   -340,  -520,  -184,  880,   -152,  -680,  -208, -1156,
  -300,  -528,  -472,  364,   100,   -744,  -1056, -32,   540,   280,  144,
  -676,  -32,   -232,  -280,  -224,  96,    568,   -76,   172,   148,  148,
  104,   32,    -296,  -32,   788,   -80,   32,    -16,   280,   288,  944,
  428,   -484
};

static const int gauss_bits = 11;

static int luma_subblock_size_y = 32;
static int luma_subblock_size_x = 32;

static int chroma_subblock_size_y = 16;
static int chroma_subblock_size_x = 16;

static const int min_luma_legal_range = 16;
static const int max_luma_legal_range = 235;

static const int min_chroma_legal_range = 16;
static const int max_chroma_legal_range = 240;

static int scaling_lut_y[256];
static int scaling_lut_cb[256];
static int scaling_lut_cr[256];

static int grain_min;
static int grain_max;

static uint16_t random_register = 0;  // random number generator register

static void dealloc_arrays(const aom_film_grain_t *params, int ***pred_pos_luma,
                           int ***pred_pos_chroma, int **luma_grain_block,
                           int **cb_grain_block, int **cr_grain_block,
                           int **y_line_buf, int **cb_line_buf,
                           int **cr_line_buf, int **y_col_buf, int **cb_col_buf,
                           int **cr_col_buf) {
  int num_pos_luma = 2 * params->ar_coeff_lag * (params->ar_coeff_lag + 1);
  int num_pos_chroma = num_pos_luma;
  if (params->num_y_points > 0) ++num_pos_chroma;

  if (*pred_pos_luma) {
    for (int row = 0; row < num_pos_luma; row++) {
      aom_free((*pred_pos_luma)[row]);
    }
    aom_free(*pred_pos_luma);
    *pred_pos_luma = NULL;
  }

  if (*pred_pos_chroma) {
    for (int row = 0; row < num_pos_chroma; row++) {
      aom_free((*pred_pos_chroma)[row]);
    }
    aom_free(*pred_pos_chroma);
    *pred_pos_chroma = NULL;
  }

  aom_free(*y_line_buf);
  *y_line_buf = NULL;

  aom_free(*cb_line_buf);
  *cb_line_buf = NULL;

  aom_free(*cr_line_buf);
  *cr_line_buf = NULL;

  aom_free(*y_col_buf);
  *y_col_buf = NULL;

  aom_free(*cb_col_buf);
  *cb_col_buf = NULL;

  aom_free(*cr_col_buf);
  *cr_col_buf = NULL;

  aom_free(*luma_grain_block);
  *luma_grain_block = NULL;

  aom_free(*cb_grain_block);
  *cb_grain_block = NULL;

  aom_free(*cr_grain_block);
  *cr_grain_block = NULL;
}

static bool init_arrays(const aom_film_grain_t *params, int luma_stride,
                        int chroma_stride, int ***pred_pos_luma_p,
                        int ***pred_pos_chroma_p, int **luma_grain_block,
                        int **cb_grain_block, int **cr_grain_block,
                        int **y_line_buf, int **cb_line_buf, int **cr_line_buf,
                        int **y_col_buf, int **cb_col_buf, int **cr_col_buf,
                        int luma_grain_samples, int chroma_grain_samples,
                        int chroma_subsamp_y, int chroma_subsamp_x) {
  *pred_pos_luma_p = NULL;
  *pred_pos_chroma_p = NULL;
  *luma_grain_block = NULL;
  *cb_grain_block = NULL;
  *cr_grain_block = NULL;
  *y_line_buf = NULL;
  *cb_line_buf = NULL;
  *cr_line_buf = NULL;
  *y_col_buf = NULL;
  *cb_col_buf = NULL;
  *cr_col_buf = NULL;

  memset(scaling_lut_y, 0, sizeof(*scaling_lut_y) * 256);
  memset(scaling_lut_cb, 0, sizeof(*scaling_lut_cb) * 256);
  memset(scaling_lut_cr, 0, sizeof(*scaling_lut_cr) * 256);

  int num_pos_luma = 2 * params->ar_coeff_lag * (params->ar_coeff_lag + 1);
  int num_pos_chroma = num_pos_luma;
  if (params->num_y_points > 0) ++num_pos_chroma;

  int **pred_pos_luma;
  int **pred_pos_chroma;

  pred_pos_luma = (int **)aom_calloc(num_pos_luma, sizeof(*pred_pos_luma));
  if (!pred_pos_luma) return false;

  for (int row = 0; row < num_pos_luma; row++) {
    pred_pos_luma[row] = (int *)aom_malloc(sizeof(**pred_pos_luma) * 3);
    if (!pred_pos_luma[row]) {
      dealloc_arrays(params, pred_pos_luma_p, pred_pos_chroma_p,
                     luma_grain_block, cb_grain_block, cr_grain_block,
                     y_line_buf, cb_line_buf, cr_line_buf, y_col_buf,
                     cb_col_buf, cr_col_buf);
      return false;
    }
  }

  pred_pos_chroma =
      (int **)aom_calloc(num_pos_chroma, sizeof(*pred_pos_chroma));
  if (!pred_pos_chroma) {
    dealloc_arrays(params, pred_pos_luma_p, pred_pos_chroma_p, luma_grain_block,
                   cb_grain_block, cr_grain_block, y_line_buf, cb_line_buf,
                   cr_line_buf, y_col_buf, cb_col_buf, cr_col_buf);
    return false;
  }

  for (int row = 0; row < num_pos_chroma; row++) {
    pred_pos_chroma[row] = (int *)aom_malloc(sizeof(**pred_pos_chroma) * 3);
    if (!pred_pos_chroma[row]) {
      dealloc_arrays(params, pred_pos_luma_p, pred_pos_chroma_p,
                     luma_grain_block, cb_grain_block, cr_grain_block,
                     y_line_buf, cb_line_buf, cr_line_buf, y_col_buf,
                     cb_col_buf, cr_col_buf);
      return false;
    }
  }

  int pos_ar_index = 0;

  for (int row = -params->ar_coeff_lag; row < 0; row++) {
    for (int col = -params->ar_coeff_lag; col < params->ar_coeff_lag + 1;
         col++) {
      pred_pos_luma[pos_ar_index][0] = row;
      pred_pos_luma[pos_ar_index][1] = col;
      pred_pos_luma[pos_ar_index][2] = 0;

      pred_pos_chroma[pos_ar_index][0] = row;
      pred_pos_chroma[pos_ar_index][1] = col;
      pred_pos_chroma[pos_ar_index][2] = 0;
      ++pos_ar_index;
    }
  }

  for (int col = -params->ar_coeff_lag; col < 0; col++) {
    pred_pos_luma[pos_ar_index][0] = 0;
    pred_pos_luma[pos_ar_index][1] = col;
    pred_pos_luma[pos_ar_index][2] = 0;

    pred_pos_chroma[pos_ar_index][0] = 0;
    pred_pos_chroma[pos_ar_index][1] = col;
    pred_pos_chroma[pos_ar_index][2] = 0;

    ++pos_ar_index;
  }

  if (params->num_y_points > 0) {
    pred_pos_chroma[pos_ar_index][0] = 0;
    pred_pos_chroma[pos_ar_index][1] = 0;
    pred_pos_chroma[pos_ar_index][2] = 1;
  }

  *pred_pos_luma_p = pred_pos_luma;
  *pred_pos_chroma_p = pred_pos_chroma;

  *y_line_buf = (int *)aom_malloc(sizeof(**y_line_buf) * luma_stride * 2);
  *cb_line_buf = (int *)aom_malloc(sizeof(**cb_line_buf) * chroma_stride *
                                   (2 >> chroma_subsamp_y));
  *cr_line_buf = (int *)aom_malloc(sizeof(**cr_line_buf) * chroma_stride *
                                   (2 >> chroma_subsamp_y));

  *y_col_buf =
      (int *)aom_malloc(sizeof(**y_col_buf) * (luma_subblock_size_y + 2) * 2);
  *cb_col_buf =
      (int *)aom_malloc(sizeof(**cb_col_buf) *
                        (chroma_subblock_size_y + (2 >> chroma_subsamp_y)) *
                        (2 >> chroma_subsamp_x));
  *cr_col_buf =
      (int *)aom_malloc(sizeof(**cr_col_buf) *
                        (chroma_subblock_size_y + (2 >> chroma_subsamp_y)) *
                        (2 >> chroma_subsamp_x));

  *luma_grain_block =
      (int *)aom_malloc(sizeof(**luma_grain_block) * luma_grain_samples);
  *cb_grain_block =
      (int *)aom_malloc(sizeof(**cb_grain_block) * chroma_grain_samples);
  *cr_grain_block =
      (int *)aom_malloc(sizeof(**cr_grain_block) * chroma_grain_samples);
  if (!(*pred_pos_luma_p && *pred_pos_chroma_p && *y_line_buf && *cb_line_buf &&
        *cr_line_buf && *y_col_buf && *cb_col_buf && *cr_col_buf &&
        *luma_grain_block && *cb_grain_block && *cr_grain_block)) {
    dealloc_arrays(params, pred_pos_luma_p, pred_pos_chroma_p, luma_grain_block,
                   cb_grain_block, cr_grain_block, y_line_buf, cb_line_buf,
                   cr_line_buf, y_col_buf, cb_col_buf, cr_col_buf);
    return false;
  }
  return true;
}

// get a number between 0 and 2^bits - 1
static inline int get_random_number(int bits) {
  uint16_t bit;
  bit = ((random_register >> 0) ^ (random_register >> 1) ^
         (random_register >> 3) ^ (random_register >> 12)) &
        1;
  random_register = (random_register >> 1) | (bit << 15);
  return (random_register >> (16 - bits)) & ((1 << bits) - 1);
}

static void init_random_generator(int luma_line, uint16_t seed) {
  // same for the picture

  uint16_t msb = (seed >> 8) & 255;
  uint16_t lsb = seed & 255;

  random_register = (msb << 8) + lsb;

  //  changes for each row
  int luma_num = luma_line >> 5;

  random_register ^= ((luma_num * 37 + 178) & 255) << 8;
  random_register ^= ((luma_num * 173 + 105) & 255);
}

static void generate_luma_grain_block(
    const aom_film_grain_t *params, int **pred_pos_luma, int *luma_grain_block,
    int luma_block_size_y, int luma_block_size_x, int luma_grain_stride,
    int left_pad, int top_pad, int right_pad, int bottom_pad) {
  if (params->num_y_points == 0) {
    memset(luma_grain_block, 0,
           sizeof(*luma_grain_block) * luma_block_size_y * luma_grain_stride);
    return;
  }

  int bit_depth = params->bit_depth;
  int gauss_sec_shift = 12 - bit_depth + params->grain_scale_shift;

  int num_pos_luma = 2 * params->ar_coeff_lag * (params->ar_coeff_lag + 1);
  int rounding_offset = (1 << (params->ar_coeff_shift - 1));

  for (int i = 0; i < luma_block_size_y; i++)
    for (int j = 0; j < luma_block_size_x; j++)
      luma_grain_block[i * luma_grain_stride + j] =
          (gaussian_sequence[get_random_number(gauss_bits)] +
           ((1 << gauss_sec_shift) >> 1)) >>
          gauss_sec_shift;

  for (int i = top_pad; i < luma_block_size_y - bottom_pad; i++)
    for (int j = left_pad; j < luma_block_size_x - right_pad; j++) {
      int wsum = 0;
      for (int pos = 0; pos < num_pos_luma; pos++) {
        wsum = wsum + params->ar_coeffs_y[pos] *
                          luma_grain_block[(i + pred_pos_luma[pos][0]) *
                                               luma_grain_stride +
                                           j + pred_pos_luma[pos][1]];
      }
      luma_grain_block[i * luma_grain_stride + j] =
          clamp(luma_grain_block[i * luma_grain_stride + j] +
                    ((wsum + rounding_offset) >> params->ar_coeff_shift),
                grain_min, grain_max);
    }
}

static bool generate_chroma_grain_blocks(
    const aom_film_grain_t *params, int **pred_pos_chroma,
    int *luma_grain_block, int *cb_grain_block, int *cr_grain_block,
    int luma_grain_stride, int chroma_block_size_y, int chroma_block_size_x,
    int chroma_grain_stride, int left_pad, int top_pad, int right_pad,
    int bottom_pad, int chroma_subsamp_y, int chroma_subsamp_x) {
  int bit_depth = params->bit_depth;
  int gauss_sec_shift = 12 - bit_depth + params->grain_scale_shift;

  int num_pos_chroma = 2 * params->ar_coeff_lag * (params->ar_coeff_lag + 1);
  if (params->num_y_points > 0) ++num_pos_chroma;
  int rounding_offset = (1 << (params->ar_coeff_shift - 1));
  int chroma_grain_block_size = chroma_block_size_y * chroma_grain_stride;

  if (params->num_cb_points || params->chroma_scaling_from_luma) {
    init_random_generator(7 << 5, params->random_seed);

    for (int i = 0; i < chroma_block_size_y; i++)
      for (int j = 0; j < chroma_block_size_x; j++)
        cb_grain_block[i * chroma_grain_stride + j] =
            (gaussian_sequence[get_random_number(gauss_bits)] +
             ((1 << gauss_sec_shift) >> 1)) >>
            gauss_sec_shift;
  } else {
    memset(cb_grain_block, 0,
           sizeof(*cb_grain_block) * chroma_grain_block_size);
  }

  if (params->num_cr_points || params->chroma_scaling_from_luma) {
    init_random_generator(11 << 5, params->random_seed);

    for (int i = 0; i < chroma_block_size_y; i++)
      for (int j = 0; j < chroma_block_size_x; j++)
        cr_grain_block[i * chroma_grain_stride + j] =
            (gaussian_sequence[get_random_number(gauss_bits)] +
             ((1 << gauss_sec_shift) >> 1)) >>
            gauss_sec_shift;
  } else {
    memset(cr_grain_block, 0,
           sizeof(*cr_grain_block) * chroma_grain_block_size);
  }

  for (int i = top_pad; i < chroma_block_size_y - bottom_pad; i++)
    for (int j = left_pad; j < chroma_block_size_x - right_pad; j++) {
      int wsum_cb = 0;
      int wsum_cr = 0;
      for (int pos = 0; pos < num_pos_chroma; pos++) {
        if (pred_pos_chroma[pos][2] == 0) {
          wsum_cb = wsum_cb + params->ar_coeffs_cb[pos] *
                                  cb_grain_block[(i + pred_pos_chroma[pos][0]) *
                                                     chroma_grain_stride +
                                                 j + pred_pos_chroma[pos][1]];
          wsum_cr = wsum_cr + params->ar_coeffs_cr[pos] *
                                  cr_grain_block[(i + pred_pos_chroma[pos][0]) *
                                                     chroma_grain_stride +
                                                 j + pred_pos_chroma[pos][1]];
        } else if (pred_pos_chroma[pos][2] == 1) {
          int av_luma = 0;
          int luma_coord_y = ((i - top_pad) << chroma_subsamp_y) + top_pad;
          int luma_coord_x = ((j - left_pad) << chroma_subsamp_x) + left_pad;

          for (int k = luma_coord_y; k < luma_coord_y + chroma_subsamp_y + 1;
               k++)
            for (int l = luma_coord_x; l < luma_coord_x + chroma_subsamp_x + 1;
                 l++)
              av_luma += luma_grain_block[k * luma_grain_stride + l];

          av_luma =
              (av_luma + ((1 << (chroma_subsamp_y + chroma_subsamp_x)) >> 1)) >>
              (chroma_subsamp_y + chroma_subsamp_x);

          wsum_cb = wsum_cb + params->ar_coeffs_cb[pos] * av_luma;
          wsum_cr = wsum_cr + params->ar_coeffs_cr[pos] * av_luma;
        } else {
          fprintf(
              stderr,
              "Grain synthesis: prediction between two chroma components is "
              "not supported!");
          return false;
        }
      }
      if (params->num_cb_points || params->chroma_scaling_from_luma)
        cb_grain_block[i * chroma_grain_stride + j] =
            clamp(cb_grain_block[i * chroma_grain_stride + j] +
                      ((wsum_cb + rounding_offset) >> params->ar_coeff_shift),
                  grain_min, grain_max);
      if (params->num_cr_points || params->chroma_scaling_from_luma)
        cr_grain_block[i * chroma_grain_stride + j] =
            clamp(cr_grain_block[i * chroma_grain_stride + j] +
                      ((wsum_cr + rounding_offset) >> params->ar_coeff_shift),
                  grain_min, grain_max);
    }
  return true;
}

static void init_scaling_function(const int scaling_points[][2], int num_points,
                                  int scaling_lut[]) {
  if (num_points == 0) return;

  for (int i = 0; i < scaling_points[0][0]; i++)
    scaling_lut[i] = scaling_points[0][1];

  for (int point = 0; point < num_points - 1; point++) {
    int delta_y = scaling_points[point + 1][1] - scaling_points[point][1];
    int delta_x = scaling_points[point + 1][0] - scaling_points[point][0];

    int64_t delta = delta_y * ((65536 + (delta_x >> 1)) / delta_x);

    for (int x = 0; x < delta_x; x++) {
      scaling_lut[scaling_points[point][0] + x] =
          scaling_points[point][1] + (int)((x * delta + 32768) >> 16);
    }
  }

  for (int i = scaling_points[num_points - 1][0]; i < 256; i++)
    scaling_lut[i] = scaling_points[num_points - 1][1];
}

// function that extracts samples from a LUT (and interpolates intemediate
// frames for 10- and 12-bit video)
static int scale_LUT(int *scaling_lut, int index, int bit_depth) {
  int x = index >> (bit_depth - 8);

  if (!(bit_depth - 8) || x == 255)
    return scaling_lut[x];
  else
    return scaling_lut[x] + (((scaling_lut[x + 1] - scaling_lut[x]) *
                                  (index & ((1 << (bit_depth - 8)) - 1)) +
                              (1 << (bit_depth - 9))) >>
                             (bit_depth - 8));
}

static void add_noise_to_block(const aom_film_grain_t *params, uint8_t *luma,
                               uint8_t *cb, uint8_t *cr, int luma_stride,
                               int chroma_stride, int *luma_grain,
                               int *cb_grain, int *cr_grain,
                               int luma_grain_stride, int chroma_grain_stride,
                               int half_luma_height, int half_luma_width,
                               int bit_depth, int chroma_subsamp_y,
                               int chroma_subsamp_x, int mc_identity) {
  int cb_mult = params->cb_mult - 128;            // fixed scale
  int cb_luma_mult = params->cb_luma_mult - 128;  // fixed scale
  int cb_offset = params->cb_offset - 256;

  int cr_mult = params->cr_mult - 128;            // fixed scale
  int cr_luma_mult = params->cr_luma_mult - 128;  // fixed scale
  int cr_offset = params->cr_offset - 256;

  int rounding_offset = (1 << (params->scaling_shift - 1));

  int apply_y = params->num_y_points > 0 ? 1 : 0;
  int apply_cb =
      (params->num_cb_points > 0 || params->chroma_scaling_from_luma) ? 1 : 0;
  int apply_cr =
      (params->num_cr_points > 0 || params->chroma_scaling_from_luma) ? 1 : 0;

  if (params->chroma_scaling_from_luma) {
    cb_mult = 0;        // fixed scale
    cb_luma_mult = 64;  // fixed scale
    cb_offset = 0;

    cr_mult = 0;        // fixed scale
    cr_luma_mult = 64;  // fixed scale
    cr_offset = 0;
  }

  int min_luma, max_luma, min_chroma, max_chroma;

  if (params->clip_to_restricted_range) {
    min_luma = min_luma_legal_range;
    max_luma = max_luma_legal_range;

    if (mc_identity) {
      min_chroma = min_luma_legal_range;
      max_chroma = max_luma_legal_range;
    } else {
      min_chroma = min_chroma_legal_range;
      max_chroma = max_chroma_legal_range;
    }
  } else {
    min_luma = min_chroma = 0;
    max_luma = max_chroma = 255;
  }

  for (int i = 0; i < (half_luma_height << (1 - chroma_subsamp_y)); i++) {
    for (int j = 0; j < (half_luma_width << (1 - chroma_subsamp_x)); j++) {
      int average_luma = 0;
      if (chroma_subsamp_x) {
        average_luma = (luma[(i << chroma_subsamp_y) * luma_stride +
                             (j << chroma_subsamp_x)] +
                        luma[(i << chroma_subsamp_y) * luma_stride +
                             (j << chroma_subsamp_x) + 1] +
                        1) >>
                       1;
      } else {
        average_luma = luma[(i << chroma_subsamp_y) * luma_stride + j];
      }

      if (apply_cb) {
        cb[i * chroma_stride + j] = clamp(
            cb[i * chroma_stride + j] +
                ((scale_LUT(scaling_lut_cb,
                            clamp(((average_luma * cb_luma_mult +
                                    cb_mult * cb[i * chroma_stride + j]) >>
                                   6) +
                                      cb_offset,
                                  0, (256 << (bit_depth - 8)) - 1),
                            8) *
                      cb_grain[i * chroma_grain_stride + j] +
                  rounding_offset) >>
                 params->scaling_shift),
            min_chroma, max_chroma);
      }

      if (apply_cr) {
        cr[i * chroma_stride + j] = clamp(
            cr[i * chroma_stride + j] +
                ((scale_LUT(scaling_lut_cr,
                            clamp(((average_luma * cr_luma_mult +
                                    cr_mult * cr[i * chroma_stride + j]) >>
                                   6) +
                                      cr_offset,
                                  0, (256 << (bit_depth - 8)) - 1),
                            8) *
                      cr_grain[i * chroma_grain_stride + j] +
                  rounding_offset) >>
                 params->scaling_shift),
            min_chroma, max_chroma);
      }
    }
  }

  if (apply_y) {
    for (int i = 0; i < (half_luma_height << 1); i++) {
      for (int j = 0; j < (half_luma_width << 1); j++) {
        luma[i * luma_stride + j] =
            clamp(luma[i * luma_stride + j] +
                      ((scale_LUT(scaling_lut_y, luma[i * luma_stride + j], 8) *
                            luma_grain[i * luma_grain_stride + j] +
                        rounding_offset) >>
                       params->scaling_shift),
                  min_luma, max_luma);
      }
    }
  }
}

static void add_noise_to_block_hbd(
    const aom_film_grain_t *params, uint16_t *luma, uint16_t *cb, uint16_t *cr,
    int luma_stride, int chroma_stride, int *luma_grain, int *cb_grain,
    int *cr_grain, int luma_grain_stride, int chroma_grain_stride,
    int half_luma_height, int half_luma_width, int bit_depth,
    int chroma_subsamp_y, int chroma_subsamp_x, int mc_identity) {
  int cb_mult = params->cb_mult - 128;            // fixed scale
  int cb_luma_mult = params->cb_luma_mult - 128;  // fixed scale
  // offset value depends on the bit depth
  int cb_offset = (params->cb_offset << (bit_depth - 8)) - (1 << bit_depth);

  int cr_mult = params->cr_mult - 128;            // fixed scale
  int cr_luma_mult = params->cr_luma_mult - 128;  // fixed scale
  // offset value depends on the bit depth
  int cr_offset = (params->cr_offset << (bit_depth - 8)) - (1 << bit_depth);

  int rounding_offset = (1 << (params->scaling_shift - 1));

  int apply_y = params->num_y_points > 0 ? 1 : 0;
  int apply_cb =
      (params->num_cb_points > 0 || params->chroma_scaling_from_luma) > 0 ? 1
                                                                          : 0;
  int apply_cr =
      (params->num_cr_points > 0 || params->chroma_scaling_from_luma) > 0 ? 1
                                                                          : 0;

  if (params->chroma_scaling_from_luma) {
    cb_mult = 0;        // fixed scale
    cb_luma_mult = 64;  // fixed scale
    cb_offset = 0;

    cr_mult = 0;        // fixed scale
    cr_luma_mult = 64;  // fixed scale
    cr_offset = 0;
  }

  int min_luma, max_luma, min_chroma, max_chroma;

  if (params->clip_to_restricted_range) {
    min_luma = min_luma_legal_range << (bit_depth - 8);
    max_luma = max_luma_legal_range << (bit_depth - 8);

    if (mc_identity) {
      min_chroma = min_luma_legal_range << (bit_depth - 8);
      max_chroma = max_luma_legal_range << (bit_depth - 8);
    } else {
      min_chroma = min_chroma_legal_range << (bit_depth - 8);
      max_chroma = max_chroma_legal_range << (bit_depth - 8);
    }
  } else {
    min_luma = min_chroma = 0;
    max_luma = max_chroma = (256 << (bit_depth - 8)) - 1;
  }

  for (int i = 0; i < (half_luma_height << (1 - chroma_subsamp_y)); i++) {
    for (int j = 0; j < (half_luma_width << (1 - chroma_subsamp_x)); j++) {
      int average_luma = 0;
      if (chroma_subsamp_x) {
        average_luma = (luma[(i << chroma_subsamp_y) * luma_stride +
                             (j << chroma_subsamp_x)] +
                        luma[(i << chroma_subsamp_y) * luma_stride +
                             (j << chroma_subsamp_x) + 1] +
                        1) >>
                       1;
      } else {
        average_luma = luma[(i << chroma_subsamp_y) * luma_stride + j];
      }

      if (apply_cb) {
        cb[i * chroma_stride + j] = clamp(
            cb[i * chroma_stride + j] +
                ((scale_LUT(scaling_lut_cb,
                            clamp(((average_luma * cb_luma_mult +
                                    cb_mult * cb[i * chroma_stride + j]) >>
                                   6) +
                                      cb_offset,
                                  0, (256 << (bit_depth - 8)) - 1),
                            bit_depth) *
                      cb_grain[i * chroma_grain_stride + j] +
                  rounding_offset) >>
                 params->scaling_shift),
            min_chroma, max_chroma);
      }
      if (apply_cr) {
        cr[i * chroma_stride + j] = clamp(
            cr[i * chroma_stride + j] +
                ((scale_LUT(scaling_lut_cr,
                            clamp(((average_luma * cr_luma_mult +
                                    cr_mult * cr[i * chroma_stride + j]) >>
                                   6) +
                                      cr_offset,
                                  0, (256 << (bit_depth - 8)) - 1),
                            bit_depth) *
                      cr_grain[i * chroma_grain_stride + j] +
                  rounding_offset) >>
                 params->scaling_shift),
            min_chroma, max_chroma);
      }
    }
  }

  if (apply_y) {
    for (int i = 0; i < (half_luma_height << 1); i++) {
      for (int j = 0; j < (half_luma_width << 1); j++) {
        luma[i * luma_stride + j] =
            clamp(luma[i * luma_stride + j] +
                      ((scale_LUT(scaling_lut_y, luma[i * luma_stride + j],
                                  bit_depth) *
                            luma_grain[i * luma_grain_stride + j] +
                        rounding_offset) >>
                       params->scaling_shift),
                  min_luma, max_luma);
      }
    }
  }
}

static void copy_rect(uint8_t *src, int src_stride, uint8_t *dst,
                      int dst_stride, int width, int height,
                      int use_high_bit_depth) {
  int hbd_coeff = use_high_bit_depth ? 2 : 1;
  while (height) {
    memcpy(dst, src, width * sizeof(uint8_t) * hbd_coeff);
    src += src_stride;
    dst += dst_stride;
    --height;
  }
  return;
}

static void copy_area(int *src, int src_stride, int *dst, int dst_stride,
                      int width, int height) {
  while (height) {
    memcpy(dst, src, width * sizeof(*src));
    src += src_stride;
    dst += dst_stride;
    --height;
  }
  return;
}

static void extend_even(uint8_t *dst, int dst_stride, int width, int height,
                        int use_high_bit_depth) {
  if ((width & 1) == 0 && (height & 1) == 0) return;
  if (use_high_bit_depth) {
    uint16_t *dst16 = (uint16_t *)dst;
    int dst16_stride = dst_stride / 2;
    if (width & 1) {
      for (int i = 0; i < height; ++i)
        dst16[i * dst16_stride + width] = dst16[i * dst16_stride + width - 1];
    }
    width = (width + 1) & (~1);
    if (height & 1) {
      memcpy(&dst16[height * dst16_stride], &dst16[(height - 1) * dst16_stride],
             sizeof(*dst16) * width);
    }
  } else {
    if (width & 1) {
      for (int i = 0; i < height; ++i)
        dst[i * dst_stride + width] = dst[i * dst_stride + width - 1];
    }
    width = (width + 1) & (~1);
    if (height & 1) {
      memcpy(&dst[height * dst_stride], &dst[(height - 1) * dst_stride],
             sizeof(*dst) * width);
    }
  }
}

static void ver_boundary_overlap(int *left_block, int left_stride,
                                 int *right_block, int right_stride,
                                 int *dst_block, int dst_stride, int width,
                                 int height) {
  if (width == 1) {
    while (height) {
      *dst_block = clamp((*left_block * 23 + *right_block * 22 + 16) >> 5,
                         grain_min, grain_max);
      left_block += left_stride;
      right_block += right_stride;
      dst_block += dst_stride;
      --height;
    }
    return;
  } else if (width == 2) {
    while (height) {
      dst_block[0] = clamp((27 * left_block[0] + 17 * right_block[0] + 16) >> 5,
                           grain_min, grain_max);
      dst_block[1] = clamp((17 * left_block[1] + 27 * right_block[1] + 16) >> 5,
                           grain_min, grain_max);
      left_block += left_stride;
      right_block += right_stride;
      dst_block += dst_stride;
      --height;
    }
    return;
  }
}

static void hor_boundary_overlap(int *top_block, int top_stride,
                                 int *bottom_block, int bottom_stride,
                                 int *dst_block, int dst_stride, int width,
                                 int height) {
  if (height == 1) {
    while (width) {
      *dst_block = clamp((*top_block * 23 + *bottom_block * 22 + 16) >> 5,
                         grain_min, grain_max);
      ++top_block;
      ++bottom_block;
      ++dst_block;
      --width;
    }
    return;
  } else if (height == 2) {
    while (width) {
      dst_block[0] = clamp((27 * top_block[0] + 17 * bottom_block[0] + 16) >> 5,
                           grain_min, grain_max);
      dst_block[dst_stride] = clamp((17 * top_block[top_stride] +
                                     27 * bottom_block[bottom_stride] + 16) >>
                                        5,
                                    grain_min, grain_max);
      ++top_block;
      ++bottom_block;
      ++dst_block;
      --width;
    }
    return;
  }
}

/*!\brief Add film grain
 *
 * Add film grain to an image
 *
 * Returns 0 for success, -1 for failure
 *
 * \param[in]    grain_params     Grain parameters
 * \param[in]    luma             luma plane
 * \param[in]    cb               cb plane
 * \param[in]    cr               cr plane
 * \param[in]    height           luma plane height
 * \param[in]    width            luma plane width
 * \param[in]    luma_stride      luma plane stride
 * \param[in]    chroma_stride    chroma plane stride
 */

static int add_film_grain_run(const aom_film_grain_t *params, uint8_t *luma,
                              uint8_t *cb, uint8_t *cr, int height, int width,
                              int luma_stride, int chroma_stride,
                              int use_high_bit_depth, int chroma_subsamp_y,
                              int chroma_subsamp_x, int mc_identity) {
  int **pred_pos_luma;
  int **pred_pos_chroma;
  int *luma_grain_block;
  int *cb_grain_block;
  int *cr_grain_block;

  int *y_line_buf;
  int *cb_line_buf;
  int *cr_line_buf;

  int *y_col_buf;
  int *cb_col_buf;
  int *cr_col_buf;

  random_register = params->random_seed;

  int left_pad = 3;
  int right_pad = 3;  // padding to offset for AR coefficients
  int top_pad = 3;
  int bottom_pad = 0;

  int ar_padding = 3;  // maximum lag used for stabilization of AR coefficients

  luma_subblock_size_y = 32;
  luma_subblock_size_x = 32;

  chroma_subblock_size_y = luma_subblock_size_y >> chroma_subsamp_y;
  chroma_subblock_size_x = luma_subblock_size_x >> chroma_subsamp_x;

  // Initial padding is only needed for generation of
  // film grain templates (to stabilize the AR process)
  // Only a 64x64 luma and 32x32 chroma part of a template
  // is used later for adding grain, padding can be discarded

  int luma_block_size_y =
      top_pad + 2 * ar_padding + luma_subblock_size_y * 2 + bottom_pad;
  int luma_block_size_x = left_pad + 2 * ar_padding + luma_subblock_size_x * 2 +
                          2 * ar_padding + right_pad;

  int chroma_block_size_y = top_pad + (2 >> chroma_subsamp_y) * ar_padding +
                            chroma_subblock_size_y * 2 + bottom_pad;
  int chroma_block_size_x = left_pad + (2 >> chroma_subsamp_x) * ar_padding +
                            chroma_subblock_size_x * 2 +
                            (2 >> chroma_subsamp_x) * ar_padding + right_pad;

  int luma_grain_stride = luma_block_size_x;
  int chroma_grain_stride = chroma_block_size_x;

  int overlap = params->overlap_flag;
  int bit_depth = params->bit_depth;

  const int grain_center = 128 << (bit_depth - 8);
  grain_min = 0 - grain_center;
  grain_max = grain_center - 1;

  if (!init_arrays(params, luma_stride, chroma_stride, &pred_pos_luma,
                   &pred_pos_chroma, &luma_grain_block, &cb_grain_block,
                   &cr_grain_block, &y_line_buf, &cb_line_buf, &cr_line_buf,
                   &y_col_buf, &cb_col_buf, &cr_col_buf,
                   luma_block_size_y * luma_block_size_x,
                   chroma_block_size_y * chroma_block_size_x, chroma_subsamp_y,
                   chroma_subsamp_x))
    return -1;

  generate_luma_grain_block(params, pred_pos_luma, luma_grain_block,
                            luma_block_size_y, luma_block_size_x,
                            luma_grain_stride, left_pad, top_pad, right_pad,
                            bottom_pad);

  if (!generate_chroma_grain_blocks(
          params, pred_pos_chroma, luma_grain_block, cb_grain_block,
          cr_grain_block, luma_grain_stride, chroma_block_size_y,
          chroma_block_size_x, chroma_grain_stride, left_pad, top_pad,
          right_pad, bottom_pad, chroma_subsamp_y, chroma_subsamp_x))
    return -1;

  init_scaling_function(params->scaling_points_y, params->num_y_points,
                        scaling_lut_y);

  if (params->chroma_scaling_from_luma) {
    memcpy(scaling_lut_cb, scaling_lut_y, sizeof(*scaling_lut_y) * 256);
    memcpy(scaling_lut_cr, scaling_lut_y, sizeof(*scaling_lut_y) * 256);
  } else {
    init_scaling_function(params->scaling_points_cb, params->num_cb_points,
                          scaling_lut_cb);
    init_scaling_function(params->scaling_points_cr, params->num_cr_points,
                          scaling_lut_cr);
  }
  for (int y = 0; y < height / 2; y += (luma_subblock_size_y >> 1)) {
    init_random_generator(y * 2, params->random_seed);

    for (int x = 0; x < width / 2; x += (luma_subblock_size_x >> 1)) {
      int offset_y = get_random_number(8);
      int offset_x = (offset_y >> 4) & 15;
      offset_y &= 15;

      int luma_offset_y = left_pad + 2 * ar_padding + (offset_y << 1);
      int luma_offset_x = top_pad + 2 * ar_padding + (offset_x << 1);

      int chroma_offset_y = top_pad + (2 >> chroma_subsamp_y) * ar_padding +
                            offset_y * (2 >> chroma_subsamp_y);
      int chroma_offset_x = left_pad + (2 >> chroma_subsamp_x) * ar_padding +
                            offset_x * (2 >> chroma_subsamp_x);

      if (overlap && x) {
        ver_boundary_overlap(
            y_col_buf, 2,
            luma_grain_block + luma_offset_y * luma_grain_stride +
                luma_offset_x,
            luma_grain_stride, y_col_buf, 2, 2,
            AOMMIN(luma_subblock_size_y + 2, height - (y << 1)));

        ver_boundary_overlap(
            cb_col_buf, 2 >> chroma_subsamp_x,
            cb_grain_block + chroma_offset_y * chroma_grain_stride +
                chroma_offset_x,
            chroma_grain_stride, cb_col_buf, 2 >> chroma_subsamp_x,
            2 >> chroma_subsamp_x,
            AOMMIN(chroma_subblock_size_y + (2 >> chroma_subsamp_y),
                   (height - (y << 1)) >> chroma_subsamp_y));

        ver_boundary_overlap(
            cr_col_buf, 2 >> chroma_subsamp_x,
            cr_grain_block + chroma_offset_y * chroma_grain_stride +
                chroma_offset_x,
            chroma_grain_stride, cr_col_buf, 2 >> chroma_subsamp_x,
            2 >> chroma_subsamp_x,
            AOMMIN(chroma_subblock_size_y + (2 >> chroma_subsamp_y),
                   (height - (y << 1)) >> chroma_subsamp_y));

        int i = y ? 1 : 0;

        if (use_high_bit_depth) {
          add_noise_to_block_hbd(
              params,
              (uint16_t *)luma + ((y + i) << 1) * luma_stride + (x << 1),
              (uint16_t *)cb +
                  ((y + i) << (1 - chroma_subsamp_y)) * chroma_stride +
                  (x << (1 - chroma_subsamp_x)),
              (uint16_t *)cr +
                  ((y + i) << (1 - chroma_subsamp_y)) * chroma_stride +
                  (x << (1 - chroma_subsamp_x)),
              luma_stride, chroma_stride, y_col_buf + i * 4,
              cb_col_buf + i * (2 - chroma_subsamp_y) * (2 - chroma_subsamp_x),
              cr_col_buf + i * (2 - chroma_subsamp_y) * (2 - chroma_subsamp_x),
              2, (2 - chroma_subsamp_x),
              AOMMIN(luma_subblock_size_y >> 1, height / 2 - y) - i, 1,
              bit_depth, chroma_subsamp_y, chroma_subsamp_x, mc_identity);
        } else {
          add_noise_to_block(
              params, luma + ((y + i) << 1) * luma_stride + (x << 1),
              cb + ((y + i) << (1 - chroma_subsamp_y)) * chroma_stride +
                  (x << (1 - chroma_subsamp_x)),
              cr + ((y + i) << (1 - chroma_subsamp_y)) * chroma_stride +
                  (x << (1 - chroma_subsamp_x)),
              luma_stride, chroma_stride, y_col_buf + i * 4,
              cb_col_buf + i * (2 - chroma_subsamp_y) * (2 - chroma_subsamp_x),
              cr_col_buf + i * (2 - chroma_subsamp_y) * (2 - chroma_subsamp_x),
              2, (2 - chroma_subsamp_x),
              AOMMIN(luma_subblock_size_y >> 1, height / 2 - y) - i, 1,
              bit_depth, chroma_subsamp_y, chroma_subsamp_x, mc_identity);
        }
      }

      if (overlap && y) {
        if (x) {
          hor_boundary_overlap(y_line_buf + (x << 1), luma_stride, y_col_buf, 2,
                               y_line_buf + (x << 1), luma_stride, 2, 2);

          hor_boundary_overlap(cb_line_buf + x * (2 >> chroma_subsamp_x),
                               chroma_stride, cb_col_buf, 2 >> chroma_subsamp_x,
                               cb_line_buf + x * (2 >> chroma_subsamp_x),
                               chroma_stride, 2 >> chroma_subsamp_x,
                               2 >> chroma_subsamp_y);

          hor_boundary_overlap(cr_line_buf + x * (2 >> chroma_subsamp_x),
                               chroma_stride, cr_col_buf, 2 >> chroma_subsamp_x,
                               cr_line_buf + x * (2 >> chroma_subsamp_x),
                               chroma_stride, 2 >> chroma_subsamp_x,
                               2 >> chroma_subsamp_y);
        }

        hor_boundary_overlap(
            y_line_buf + ((x ? x + 1 : 0) << 1), luma_stride,
            luma_grain_block + luma_offset_y * luma_grain_stride +
                luma_offset_x + (x ? 2 : 0),
            luma_grain_stride, y_line_buf + ((x ? x + 1 : 0) << 1), luma_stride,
            AOMMIN(luma_subblock_size_x - ((x ? 1 : 0) << 1),
                   width - ((x ? x + 1 : 0) << 1)),
            2);

        hor_boundary_overlap(
            cb_line_buf + ((x ? x + 1 : 0) << (1 - chroma_subsamp_x)),
            chroma_stride,
            cb_grain_block + chroma_offset_y * chroma_grain_stride +
                chroma_offset_x + ((x ? 1 : 0) << (1 - chroma_subsamp_x)),
            chroma_grain_stride,
            cb_line_buf + ((x ? x + 1 : 0) << (1 - chroma_subsamp_x)),
            chroma_stride,
            AOMMIN(chroma_subblock_size_x -
                       ((x ? 1 : 0) << (1 - chroma_subsamp_x)),
                   (width - ((x ? x + 1 : 0) << 1)) >> chroma_subsamp_x),
            2 >> chroma_subsamp_y);

        hor_boundary_overlap(
            cr_line_buf + ((x ? x + 1 : 0) << (1 - chroma_subsamp_x)),
            chroma_stride,
            cr_grain_block + chroma_offset_y * chroma_grain_stride +
                chroma_offset_x + ((x ? 1 : 0) << (1 - chroma_subsamp_x)),
            chroma_grain_stride,
            cr_line_buf + ((x ? x + 1 : 0) << (1 - chroma_subsamp_x)),
            chroma_stride,
            AOMMIN(chroma_subblock_size_x -
                       ((x ? 1 : 0) << (1 - chroma_subsamp_x)),
                   (width - ((x ? x + 1 : 0) << 1)) >> chroma_subsamp_x),
            2 >> chroma_subsamp_y);

        if (use_high_bit_depth) {
          add_noise_to_block_hbd(
              params, (uint16_t *)luma + (y << 1) * luma_stride + (x << 1),
              (uint16_t *)cb + (y << (1 - chroma_subsamp_y)) * chroma_stride +
                  (x << ((1 - chroma_subsamp_x))),
              (uint16_t *)cr + (y << (1 - chroma_subsamp_y)) * chroma_stride +
                  (x << ((1 - chroma_subsamp_x))),
              luma_stride, chroma_stride, y_line_buf + (x << 1),
              cb_line_buf + (x << (1 - chroma_subsamp_x)),
              cr_line_buf + (x << (1 - chroma_subsamp_x)), luma_stride,
              chroma_stride, 1,
              AOMMIN(luma_subblock_size_x >> 1, width / 2 - x), bit_depth,
              chroma_subsamp_y, chroma_subsamp_x, mc_identity);
        } else {
          add_noise_to_block(
              params, luma + (y << 1) * luma_stride + (x << 1),
              cb + (y << (1 - chroma_subsamp_y)) * chroma_stride +
                  (x << ((1 - chroma_subsamp_x))),
              cr + (y << (1 - chroma_subsamp_y)) * chroma_stride +
                  (x << ((1 - chroma_subsamp_x))),
              luma_stride, chroma_stride, y_line_buf + (x << 1),
              cb_line_buf + (x << (1 - chroma_subsamp_x)),
              cr_line_buf + (x << (1 - chroma_subsamp_x)), luma_stride,
              chroma_stride, 1,
              AOMMIN(luma_subblock_size_x >> 1, width / 2 - x), bit_depth,
              chroma_subsamp_y, chroma_subsamp_x, mc_identity);
        }
      }

      int i = overlap && y ? 1 : 0;
      int j = overlap && x ? 1 : 0;

      if (use_high_bit_depth) {
        add_noise_to_block_hbd(
            params,
            (uint16_t *)luma + ((y + i) << 1) * luma_stride + ((x + j) << 1),
            (uint16_t *)cb +
                ((y + i) << (1 - chroma_subsamp_y)) * chroma_stride +
                ((x + j) << (1 - chroma_subsamp_x)),
            (uint16_t *)cr +
                ((y + i) << (1 - chroma_subsamp_y)) * chroma_stride +
                ((x + j) << (1 - chroma_subsamp_x)),
            luma_stride, chroma_stride,
            luma_grain_block + (luma_offset_y + (i << 1)) * luma_grain_stride +
                luma_offset_x + (j << 1),
            cb_grain_block +
                (chroma_offset_y + (i << (1 - chroma_subsamp_y))) *
                    chroma_grain_stride +
                chroma_offset_x + (j << (1 - chroma_subsamp_x)),
            cr_grain_block +
                (chroma_offset_y + (i << (1 - chroma_subsamp_y))) *
                    chroma_grain_stride +
                chroma_offset_x + (j << (1 - chroma_subsamp_x)),
            luma_grain_stride, chroma_grain_stride,
            AOMMIN(luma_subblock_size_y >> 1, height / 2 - y) - i,
            AOMMIN(luma_subblock_size_x >> 1, width / 2 - x) - j, bit_depth,
            chroma_subsamp_y, chroma_subsamp_x, mc_identity);
      } else {
        add_noise_to_block(
            params, luma + ((y + i) << 1) * luma_stride + ((x + j) << 1),
            cb + ((y + i) << (1 - chroma_subsamp_y)) * chroma_stride +
                ((x + j) << (1 - chroma_subsamp_x)),
            cr + ((y + i) << (1 - chroma_subsamp_y)) * chroma_stride +
                ((x + j) << (1 - chroma_subsamp_x)),
            luma_stride, chroma_stride,
            luma_grain_block + (luma_offset_y + (i << 1)) * luma_grain_stride +
                luma_offset_x + (j << 1),
            cb_grain_block +
                (chroma_offset_y + (i << (1 - chroma_subsamp_y))) *
                    chroma_grain_stride +
                chroma_offset_x + (j << (1 - chroma_subsamp_x)),
            cr_grain_block +
                (chroma_offset_y + (i << (1 - chroma_subsamp_y))) *
                    chroma_grain_stride +
                chroma_offset_x + (j << (1 - chroma_subsamp_x)),
            luma_grain_stride, chroma_grain_stride,
            AOMMIN(luma_subblock_size_y >> 1, height / 2 - y) - i,
            AOMMIN(luma_subblock_size_x >> 1, width / 2 - x) - j, bit_depth,
            chroma_subsamp_y, chroma_subsamp_x, mc_identity);
      }

      if (overlap) {
        if (x) {
          // Copy overlapped column bufer to line buffer
          copy_area(y_col_buf + (luma_subblock_size_y << 1), 2,
                    y_line_buf + (x << 1), luma_stride, 2, 2);

          copy_area(
              cb_col_buf + (chroma_subblock_size_y << (1 - chroma_subsamp_x)),
              2 >> chroma_subsamp_x,
              cb_line_buf + (x << (1 - chroma_subsamp_x)), chroma_stride,
              2 >> chroma_subsamp_x, 2 >> chroma_subsamp_y);

          copy_area(
              cr_col_buf + (chroma_subblock_size_y << (1 - chroma_subsamp_x)),
              2 >> chroma_subsamp_x,
              cr_line_buf + (x << (1 - chroma_subsamp_x)), chroma_stride,
              2 >> chroma_subsamp_x, 2 >> chroma_subsamp_y);
        }

        // Copy grain to the line buffer for overlap with a bottom block
        copy_area(
            luma_grain_block +
                (luma_offset_y + luma_subblock_size_y) * luma_grain_stride +
                luma_offset_x + ((x ? 2 : 0)),
            luma_grain_stride, y_line_buf + ((x ? x + 1 : 0) << 1), luma_stride,
            AOMMIN(luma_subblock_size_x, width - (x << 1)) - (x ? 2 : 0), 2);

        copy_area(cb_grain_block +
                      (chroma_offset_y + chroma_subblock_size_y) *
                          chroma_grain_stride +
                      chroma_offset_x + (x ? 2 >> chroma_subsamp_x : 0),
                  chroma_grain_stride,
                  cb_line_buf + ((x ? x + 1 : 0) << (1 - chroma_subsamp_x)),
                  chroma_stride,
                  AOMMIN(chroma_subblock_size_x,
                         ((width - (x << 1)) >> chroma_subsamp_x)) -
                      (x ? 2 >> chroma_subsamp_x : 0),
                  2 >> chroma_subsamp_y);

        copy_area(cr_grain_block +
                      (chroma_offset_y + chroma_subblock_size_y) *
                          chroma_grain_stride +
                      chroma_offset_x + (x ? 2 >> chroma_subsamp_x : 0),
                  chroma_grain_stride,
                  cr_line_buf + ((x ? x + 1 : 0) << (1 - chroma_subsamp_x)),
                  chroma_stride,
                  AOMMIN(chroma_subblock_size_x,
                         ((width - (x << 1)) >> chroma_subsamp_x)) -
                      (x ? 2 >> chroma_subsamp_x : 0),
                  2 >> chroma_subsamp_y);

        // Copy grain to the column buffer for overlap with the next block to
        // the right

        copy_area(luma_grain_block + luma_offset_y * luma_grain_stride +
                      luma_offset_x + luma_subblock_size_x,
                  luma_grain_stride, y_col_buf, 2, 2,
                  AOMMIN(luma_subblock_size_y + 2, height - (y << 1)));

        copy_area(cb_grain_block + chroma_offset_y * chroma_grain_stride +
                      chroma_offset_x + chroma_subblock_size_x,
                  chroma_grain_stride, cb_col_buf, 2 >> chroma_subsamp_x,
                  2 >> chroma_subsamp_x,
                  AOMMIN(chroma_subblock_size_y + (2 >> chroma_subsamp_y),
                         (height - (y << 1)) >> chroma_subsamp_y));

        copy_area(cr_grain_block + chroma_offset_y * chroma_grain_stride +
                      chroma_offset_x + chroma_subblock_size_x,
                  chroma_grain_stride, cr_col_buf, 2 >> chroma_subsamp_x,
                  2 >> chroma_subsamp_x,
                  AOMMIN(chroma_subblock_size_y + (2 >> chroma_subsamp_y),
                         (height - (y << 1)) >> chroma_subsamp_y));
      }
    }
  }

  dealloc_arrays(params, &pred_pos_luma, &pred_pos_chroma, &luma_grain_block,
                 &cb_grain_block, &cr_grain_block, &y_line_buf, &cb_line_buf,
                 &cr_line_buf, &y_col_buf, &cb_col_buf, &cr_col_buf);
  return 0;
}

int av1_add_film_grain(const aom_film_grain_t *params, const aom_image_t *src,
                       aom_image_t *dst) {
  uint8_t *luma, *cb, *cr;
  int height, width, luma_stride, chroma_stride;
  int use_high_bit_depth = 0;
  int chroma_subsamp_x = 0;
  int chroma_subsamp_y = 0;
  int mc_identity = src->mc == AOM_CICP_MC_IDENTITY ? 1 : 0;

  switch (src->fmt) {
    case AOM_IMG_FMT_AOMI420:
    case AOM_IMG_FMT_I420:
      use_high_bit_depth = 0;
      chroma_subsamp_x = 1;
      chroma_subsamp_y = 1;
      break;
    case AOM_IMG_FMT_I42016:
      use_high_bit_depth = 1;
      chroma_subsamp_x = 1;
      chroma_subsamp_y = 1;
      break;
      //    case AOM_IMG_FMT_444A:
    case AOM_IMG_FMT_I444:
      use_high_bit_depth = 0;
      chroma_subsamp_x = 0;
      chroma_subsamp_y = 0;
      break;
    case AOM_IMG_FMT_I44416:
      use_high_bit_depth = 1;
      chroma_subsamp_x = 0;
      chroma_subsamp_y = 0;
      break;
    case AOM_IMG_FMT_I422:
      use_high_bit_depth = 0;
      chroma_subsamp_x = 1;
      chroma_subsamp_y = 0;
      break;
    case AOM_IMG_FMT_I42216:
      use_high_bit_depth = 1;
      chroma_subsamp_x = 1;
      chroma_subsamp_y = 0;
      break;
    default:  // unknown input format
      fprintf(stderr, "Film grain error: input format is not supported!");
      return -1;
  }

  assert(params->bit_depth == src->bit_depth);

  dst->fmt = src->fmt;
  dst->bit_depth = src->bit_depth;

  dst->r_w = src->r_w;
  dst->r_h = src->r_h;
  dst->d_w = src->d_w;
  dst->d_h = src->d_h;

  dst->cp = src->cp;
  dst->tc = src->tc;
  dst->mc = src->mc;

  dst->monochrome = src->monochrome;
  dst->csp = src->csp;
  dst->range = src->range;

  dst->x_chroma_shift = src->x_chroma_shift;
  dst->y_chroma_shift = src->y_chroma_shift;

  dst->temporal_id = src->temporal_id;
  dst->spatial_id = src->spatial_id;

  width = src->d_w % 2 ? src->d_w + 1 : src->d_w;
  height = src->d_h % 2 ? src->d_h + 1 : src->d_h;

  copy_rect(src->planes[AOM_PLANE_Y], src->stride[AOM_PLANE_Y],
            dst->planes[AOM_PLANE_Y], dst->stride[AOM_PLANE_Y], src->d_w,
            src->d_h, use_high_bit_depth);
  // Note that dst is already assumed to be aligned to even.
  extend_even(dst->planes[AOM_PLANE_Y], dst->stride[AOM_PLANE_Y], src->d_w,
              src->d_h, use_high_bit_depth);

  if (!src->monochrome) {
    copy_rect(src->planes[AOM_PLANE_U], src->stride[AOM_PLANE_U],
              dst->planes[AOM_PLANE_U], dst->stride[AOM_PLANE_U],
              width >> chroma_subsamp_x, height >> chroma_subsamp_y,
              use_high_bit_depth);

    copy_rect(src->planes[AOM_PLANE_V], src->stride[AOM_PLANE_V],
              dst->planes[AOM_PLANE_V], dst->stride[AOM_PLANE_V],
              width >> chroma_subsamp_x, height >> chroma_subsamp_y,
              use_high_bit_depth);
  }

  luma = dst->planes[AOM_PLANE_Y];
  cb = dst->planes[AOM_PLANE_U];
  cr = dst->planes[AOM_PLANE_V];

  // luma and chroma strides in samples
  luma_stride = dst->stride[AOM_PLANE_Y] >> use_high_bit_depth;
  chroma_stride = dst->stride[AOM_PLANE_U] >> use_high_bit_depth;

  return add_film_grain_run(params, luma, cb, cr, height, width, luma_stride,
                            chroma_stride, use_high_bit_depth, chroma_subsamp_y,
                            chroma_subsamp_x, mc_identity);
}

Messung V0.5
C=97 H=88 G=92

¤ Dauer der Verarbeitung: 0.19 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge