/* The copyright in this software is being made available under the BSD
 * License, included below. This software may be subject to other third party
 * and contributor rights, including patent rights, and no such rights are
 * granted under this license.
 *
 * Copyright (c) 2010-2023, ITU/ISO/IEC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
 *    be used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

 /** \file     SEIFilmGrainSynthesizer.cpp
     \brief    SMPTE RDD5 based film grain synthesis functionality from SEI messages
 */

#include "SEIFilmGrainSynthesizer.h"

#include <stdio.h>
#include <cmath>


/* static look up table definitions */
static const int8_t gaussianLUT[2048] =
{
-11, 12, 103, -11, 42, -35, 12, 59, 77, 98, -87, 3, 65, -78, 45, 56,
-51, 21, 13, -11, -20, -19, 33, -127, 17, -6, -105, 18, 19, 71, 48, -10,
-38, 42, -2, 75, -67, 52, -90, 33, -47, 21, -3, -56, 49, 1, -57, -42,
-1, 120, -127, -108, -49, 9, 14, 127, 122, 109, 52, 127, 2, 7, 114, 19,
30, 12, 77, 112, 82, -61, -127, 111, -52, -29, 2, -49, -24, 58, -29, -73,
12, 112, 67, 79, -3, -114, -87, -6, -5, 40, 58, -81, 49, -27, -31, -34,
-105, 50, 16, -24, -35, -14, -15, -127, -55, -22, -55, -127, -112, 5, -26, -72,
127, 127, -2, 41, 87, -65, -16, 55, 19, 91, -81, -65, -64, 35, -7, -54,
99, -7, 88, 125, -26, 91, 0, 63, 60, -14, -23, 113, -33, 116, 14, 26,
51, -16, 107, -8, 53, 38, -34, 17, -7, 4, -91, 6, 63, 63, -15, 39,
-36, 19, 55, 17, -51, 40, 33, -37, 126, -39, -118, 17, -30, 0, 19, 98,
60, 101, -12, -73, -17, -52, 98, 3, 3, 60, 33, -3, -2, 10, -42, -106,
-38, 14, 127, 16, -127, -31, -86, -39, -56, 46, -41, 75, 23, -19, -22, -70,
74, -54, -2, 32, -45, 17, -92, 59, -64, -67, 56, -102, -29, -87, -34, -92,
68, 5, -74, -61, 93, -43, 14, -26, -38, -126, -17, 16, -127, 64, 34, 31,
93, 17, -51, -59, 71, 77, 81, 127, 127, 61, 33, -106, -93, 0, 0, 75,
-69, 71, 127, -19, -111, 30, 23, 15, 2, 39, 92, 5, 42, 2, -6, 38,
15, 114, -30, -37, 50, 44, 106, 27, 119, 7, -80, 25, -68, -21, 92, -11,
-1, 18, 41, -50, 79, -127, -43, 127, 18, 11, -21, 32, -52, 27, -88, -90,
-39, -19, -10, 24, -118, 72, -24, -44, 2, 12, 86, -107, 39, -33, -127, 47,
51, -24, -22, 46, 0, 15, -35, -69, -2, -74, 24, -6, 0, 29, -3, 45,
32, -32, 117, -45, 79, -24, -17, -109, -10, -70, 88, -48, 24, -91, 120, -37,
50, -127, 58, 32, -82, -10, -17, -7, 46, -127, -15, 89, 127, 17, 98, -39,
-33, 37, 42, -40, -32, -21, 105, -19, 19, 19, -59, -9, 30, 0, -127, 34,
127, -84, 75, 24, -40, -49, -127, -107, -14, 45, -75, 1, 30, -20, 41, -68,
-40, 12, 127, -3, 5, 20, -73, -59, -127, -3, -3, -53, -6, -119, 93, 120,
-80, -50, 0, 20, -46, 67, 78, -12, -22, -127, 36, -41, 56, 119, -5, -116,
-22, 68, -14, -90, 24, -82, -44, -127, 107, -25, -37, 40, -7, -7, -82, 5,
-87, 44, -34, 9, -127, 39, 70, 49, -63, 74, -49, 109, -27, -89, -47, -39,
44, 49, -4, 60, -42, 80, 9, -127, -9, -56, -49, 125, -66, 47, 36, 117,
15, -11, -96, 109, 94, -17, -56, 70, 8, -14, -5, 50, 37, -45, 120, -30,
-76, 40, -46, 6, 3, 69, 17, -78, 1, -79, 6, 127, 43, 26, 127, -127,
28, -55, -26, 55, 112, 48, 107, -1, -77, -1, 53, -9, -22, -43, 123, 108,
127, 102, 68, 46, 5, 1, 123, -13, -55, -34, -49, 89, 65, -105, -5, 94,
-53, 62, 45, 30, 46, 18, -35, 15, 41, 47, -98, -24, 94, -75, 127, -114,
127, -68, 1, -17, 51, -95, 47, 12, 34, -45, -75, 89, -107, -9, -58, -29,
-109, -24, 127, -61, -13, 77, -45, 17, 19, 83, -24, 9, 127, -66, 54, 4,
26, 13, 111, 43, -113, -22, 10, -24, 83, 67, -14, 75, -123, 59, 127, -12,
99, -19, 64, -38, 54, 9, 7, 61, -56, 3, -57, 113, -104, -59, 3, -9,
-47, 74, 85, -55, -34, 12, 118, 28, 93, -72, 13, -99, -72, -20, 30, 72,
-94, 19, -54, 64, -12, -63, -25, 65, 72, -10, 127, 0, -127, 103, -20, -73,
-112, -103, -6, 28, -42, -21, -59, -29, -26, 19, -4, -51, 94, -58, -95, -37,
35, 20, -69, 127, -19, -127, -22, -120, -53, 37, 74, -127, -1, -12, -119, -53,
-28, 38, 69, 17, 16, -114, 89, 62, 24, 37, -23, 49, -101, -32, -9, -95,
-53, 5, 93, -23, -49, -8, 51, 3, -75, -90, -10, -39, 127, -86, -22, 20,
20, 113, 75, 52, -31, 92, -63, 7, -12, 46, 36, 101, -43, -17, -53, -7,
-38, -76, -31, -21, 62, 31, 62, 20, -127, 31, 64, 36, 102, -85, -10, 77,
80, 58, -79, -8, 35, 8, 80, -24, -9, 3, -17, 72, 127, 83, -87, 55,
18, -119, -123, 36, 10, 127, 56, -55, 113, 13, 26, 32, -13, -48, 22, -13,
5, 58, 27, 24, 26, -11, -36, 37, -92, 78, 81, 9, 51, 14, 67, -13,
0, 32, 45, -76, 32, -39, -22, -49, -127, -27, 31, -9, 36, 14, 71, 13,
57, 12, -53, -86, 53, -44, -35, 2, 127, 12, -66, -44, 46, -115, 3, 10,
56, -35, 119, -19, -61, 52, -59, -127, -49, -23, 4, -5, 17, -82, -6, 127,
25, 79, 67, 64, -25, 14, -64, -37, -127, -28, 21, -63, 66, -53, -41, 109,
-62, 15, -22, 13, 29, -63, 20, 27, 95, -44, -59, -116, -10, 79, -49, 22,
-43, -16, 46, -47, -120, -36, -29, -52, -44, 29, 127, -13, 49, -9, -127, 75,
-28, -23, 88, 59, 11, -95, 81, -59, 58, 60, -26, 40, -92, -3, -22, -58,
-45, -59, -22, -53, 71, -29, 66, -32, -23, 14, -17, -66, -24, -28, -62, 47,
38, 17, 16, -37, -24, -11, 8, -27, -19, 59, 45, -49, -47, -4, -22, -81,
30, -67, -127, 74, 102, 5, -18, 98, 34, -66, 42, -52, 7, -59, 24, -58,
-19, -24, -118, -73, 91, 15, -16, 79, -32, -79, -127, -36, 41, 77, -83, 2,
56, 22, -75, 127, -16, -21, 12, 31, 56, -113, -127, 90, 55, 61, 12, 55,
-14, -113, -14, 32, 49, -67, -17, 91, -10, 1, 21, 69, -70, 99, -19, -112,
66, -90, -10, -9, -71, 127, 50, -81, -49, 24, 61, -61, -111, 7, -41, 127,
88, -66, 108, -127, -6, 36, -14, 41, -50, 14, 14, 73, -101, -28, 77, 127,
-8, -100, 88, 38, 121, 88, -125, -60, 13, -94, -115, 20, -67, -87, -94, -119,
44, -28, -30, 18, 5, -53, -61, 20, -43, 11, -77, -60, 13, 29, 3, 6,
-72, 38, -60, -11, 108, -53, 41, 66, -12, -127, -127, -49, 24, 29, 46, 36,
91, 34, -33, 116, -51, -34, -52, 91, 7, -83, 73, -26, -103, 24, -10, 76,
84, 5, 68, -80, -13, -17, -32, -48, 20, 50, 26, 10, 63, -104, -14, 37,
127, 114, 97, 35, 1, -33, -55, 127, -124, -33, 61, -7, 119, -32, -127, -53,
-42, 63, 3, -5, -26, 70, -58, -33, -44, -43, 34, -56, -127, 127, 25, -35,
-11, 16, -81, 29, -58, 40, -127, -127, 20, -47, -11, -36, -63, -52, -32, -82,
78, -76, -73, 8, 27, -72, -9, -74, -85, -86, -57, 25, 78, -10, -97, 35,
-65, 8, -59, 14, 1, -42, 32, -88, -44, 17, -3, -9, 59, 40, 12, -108,
-40, 24, 34, 18, -28, 2, 51, -110, -4, 100, 1, 65, 22, 0, 127, 61,
45, 25, -31, 6, 9, -7, -48, 99, 16, 44, -2, -40, 32, -39, -52, 10,
-110, -19, 56, -127, 69, 26, 51, 92, 40, 61, -52, 45, -38, 13, 85, 122,
27, 66, 45, -111, -83, -3, 31, 37, 19, -36, 58, 71, 39, -78, -47, 58,
-78, 8, -62, -36, -14, 61, 42, -127, 71, -4, 24, -54, 52, -127, 67, -4,
-42, 30, -63, 59, -3, -1, -18, -46, -92, -81, -96, -14, -53, -10, -11, -77,
13, 1, 8, -67, -127, 127, -28, 26, -14, 18, -13, -26, 2, 10, -46, -32,
-15, 27, -31, -59, 59, 77, -121, 28, 40, -54, -62, -31, -21, -37, -32, -6,
-127, -25, -60, 70, -127, 112, -127, 127, 88, -7, 116, 110, 53, 87, -127, 3,
16, 23, 74, -106, -51, 3, 74, -82, -112, -74, 65, 81, 25, 53, 127, -45,
-50, -103, -41, -65, -29, 79, -67, 64, -33, -30, -8, 127, 0, -13, -51, 67,
-14, 5, -92, 29, -35, -8, -90, -57, -3, 36, 43, 44, -31, -69, -7, 36,
39, -51, 43, -81, 58, 6, 127, 12, 57, 66, 46, 59, -43, -42, 41, -15,
-120, 24, 3, -11, 19, -13, 51, 28, 3, 55, -48, -12, -1, 2, 97, -19,
29, 42, 13, 43, 78, -44, 56, -108, -43, -19, 127, 15, -11, -18, -81, 83,
-37, 77, -109, 15, 65, -50, 43, 12, 13, 27, 28, 61, 57, 30, 26, 106,
-18, 56, 13, 97, 4, -8, -62, -103, 94, 108, -44, 52, 27, -47, -9, 105,
-53, 46, 89, 103, -33, 38, -34, 55, 51, 70, -94, -35, -87, -107, -19, -31,
9, -19, 79, -14, 77, 5, -19, -107, 85, 21, -45, -39, -42, 9, -29, 74,
47, -75, 60, -127, 120, -112, -57, -32, 41, 7, 79, 76, 66, 57, 41, -25,
31, 37, -47, -36, 43, -73, -37, 63, 127, -69, -52, 90, -33, -61, 60, -55,
44, 15, 4, -67, 13, -92, 64, 29, -39, -3, 83, -2, -38, -85, -86, 58,
35, -69, -61, 29, -37, -95, -78, 4, 30, -4, -32, -80, -22, -9, -77, 46,
7, -93, -71, 65, 9, -50, 127, -70, 26, -12, -39, -114, 63, -127, -100, 4,
-32, 111, 22, -60, 65, -101, 26, -42, 21, -59, -27, -74, 2, -94, 6, 126,
5, 76, -88, -9, -43, -101, 127, 1, 125, 92, -63, 52, 56, 4, 81, -127,
127, 80, 127, -29, 30, 116, -74, -17, -57, 105, 48, 45, 25, -72, 48, -38,
-108, 31, -34, 4, -11, 41, -127, 52, -104, -43, -37, 52, 2, 47, 87, -9,
77, 27, -41, -25, 90, 86, -56, 75, 10, 33, 78, 58, 127, 127, -7, -73,
49, -33, -106, -35, 38, 57, 53, -17, -4, 83, 52, -108, 54, -125, 28, 23,
56, -43, -88, -17, -6, 47, 23, -9, 0, -13, 111, 75, 27, -52, -38, -34,
39, 30, 66, 39, 38, -64, 38, 3, 21, -32, -51, -28, 54, -38, -87, 20,
52, 115, 18, -81, -70, 0, -14, -46, -46, -3, 125, 16, -14, 23, -82, -84,
-69, -20, -65, -127, 9, 81, -49, 61, 7, -36, -45, -42, 57, -26, 47, 20,
-85, 46, -13, 41, -37, -75, -60, 86, -78, -127, 12, 50, 2, -3, 13, 47,
5, 19, -78, -55, -27, 65, -71, 12, -108, 20, -16, 11, -31, 63, -55, 37,
75, -17, 127, -73, -33, -28, -120, 105, 68, 106, -103, -106, 71, 61, 2, 23,
-3, 33, -5, -15, -67, -15, -23, -54, 15, -63, 76, 58, -110, 1, 83, -27,
22, 75, -39, -17, -11, 64, -17, -127, -54, -66, 31, 96, 116, 3, -114, -7,
-108, -63, 97, 9, 50, 8, 75, -28, 72, 112, -36, -112, 95, -50, 23, -13,
-19, 55, 21, 23, 92, 91, 22, -49, 16, -75, 23, 9, -49, -97, -37, 49,
-36, 36, -127, -86, 43, 127, -24, -24, 84, 83, -35, -34, -12, 109, 102, -38,
51, -68, 34, 19, -22, 49, -32, 127, 40, 24, -93, -4, -3, 105, 3, -58,
-18, 8, 127, -18, 125, 68, 69, -62, 30, -36, 54, -57, -24, 17, 43, -36,
-27, -57, -67, -21, -10, -49, 68, 12, 65, 4, 48, 55, 127, -75, 44, 89,
-66, -13, -78, -82, -91, 22, 30, 33, -40, -87, -34, 96, -91, 39, 10, -64,
-3, -12, 127, -50, -37, -56, 23, -35, -36, -54, 90, -91, 2, 50, 77, -6,
-127, 16, 46, -5, -73, 0, -56, -18, -72, 28, 93, 60, 49, 20, 18, 111,
-111, 32, -83, 47, 47, -10, 35, -88, 43, 57, -98, 127, -17, 0, 1, -39,
-127, -2, 0, 63, 93, 0, 36, -66, -61, -19, 39, -127, 58, 50, -17, 127,
88, -43, -108, -51, -16, 7, -36, 68, 46, -14, 107, 40, 57, 7, 19, 8,
3, 88, -90, -92, -18, -21, -24, 13, 7, -4, -78, -91, -4, 8, -35, -5,
19, 2, -111, 4, -66, -81, 122, -20, -34, -37, -84, 127, 68, 46, 17, 47
};

static const uint32_t seedLUT[256] = {
747538460, 1088979410, 1744950180, 1767011913, 1403382928, 521866116, 1060417601, 2110622736,
1557184770, 105289385, 585624216, 1827676546, 1191843873, 1018104344, 1123590530, 663361569,
2023850500, 76561770, 1226763489, 80325252, 1992581442, 502705249, 740409860, 516219202,
557974537, 1883843076, 720112066, 1640137737, 1820967556, 40667586, 155354121, 1820967557,
1115949072, 1631803309, 98284748, 287433856, 2119719977, 988742797, 1827432592, 579378475,
1017745956, 1309377032, 1316535465, 2074315269, 1923385360, 209722667, 1546228260, 168102420,
135274561, 355958469, 248291472, 2127839491, 146920100, 585982612, 1611702337, 696506029,
1386498192, 1258072451, 1212240548, 1043171860, 1217404993, 1090770605, 1386498193, 169093201,
541098240, 1468005469, 456510673, 1578687785, 1838217424, 2010752065, 2089828354, 1362717428,
970073673, 854129835, 714793201, 1266069081, 1047060864, 1991471829, 1098097741, 913883585,
1669598224, 1337918685, 1219264706, 1799741108, 1834116681, 683417731, 1120274457, 1073098457,
1648396544, 176642749, 31171789, 718317889, 1266977808, 1400892508, 549749008, 1808010512,
67112961, 1005669825, 903663673, 1771104465, 1277749632, 1229754427, 950632997, 1979371465,
2074373264, 305357524, 1049387408, 1171033360, 1686114305, 2147468765, 1941195985, 117709841,
809550080, 991480851, 1816248997, 1561503561, 329575568, 780651196, 1659144592, 1910793616,
604016641, 1665084765, 1530186961, 1870928913, 809550081, 2079346113, 71307521, 876663040,
1073807360, 832356664, 1573927377, 204073344, 2026918147, 1702476788, 2043881033, 57949587,
2001393952, 1197426649, 1186508931, 332056865, 950043140, 890043474, 349099312, 148914948,
236204097, 2022643605, 1441981517, 498130129, 1443421481, 924216797, 1817491777, 1913146664,
1411989632, 929068432, 495735097, 1684636033, 1284520017, 432816184, 1344884865, 210843729,
676364544, 234449232, 12112337, 1350619139, 1753272996, 2037118872, 1408560528, 533334916,
1043640385, 357326099, 201376421, 110375493, 541106497, 416159637, 242512193, 777294080,
1614872576, 1535546636, 870600145, 910810409, 1821440209, 1605432464, 1145147393, 951695441,
1758494976, 1506656568, 1557150160, 608221521, 1073840384, 217672017, 684818688, 1750138880,
16777217, 677990609, 953274371, 1770050213, 1359128393, 1797602707, 1984616737, 1865815816,
2120835200, 2051677060, 1772234061, 1579794881, 1652821009, 1742099468, 1887260865, 46468113,
1011925248, 1134107920, 881643832, 1354774993, 472508800, 1892499769, 1752793472, 1962502272,
687898625, 883538000, 1354355153, 1761673473, 944820481, 2020102353, 22020353, 961597696,
1342242816, 964808962, 1355809701, 17016649, 1386540177, 647682692, 1849012289, 751668241,
1557184768, 127374604, 1927564752, 1045744913, 1614921984, 43588881, 1016185088, 1544617984,
1090519041, 136122424, 215038417, 1563027841, 2026918145, 1688778833, 701530369, 1372639488,
1342242817, 2036945104, 953274369, 1750192384, 16842753, 964808960, 1359020032, 1358954497
};

static const uint32_t deblockFactor[13] =
{ 64, 71, 77, 84, 90, 96, 103, 109, 116, 122, 128, 128, 128 };

SEIFilmGrainSynthesizer::SEIFilmGrainSynthesizer()
  : m_width(0)
  , m_height(0)
  , m_chromaFormat(ChromaFormat::UNDEFINED)
  , m_bitDepth(0)
  , m_idrPicId(0)
  , m_grainSynt(nullptr)
  , m_fgsBlkSize(8)
  , m_poc(0)
  , m_errorCode(0)
  , m_fgcParameters(nullptr)
{

}

void SEIFilmGrainSynthesizer::create(uint32_t width, uint32_t height, ChromaFormat fmt, uint8_t bitDepth, uint32_t idrPicId)
{
  m_width             = width;
  m_height            = height;
  m_chromaFormat      = fmt;
  m_bitDepth          = bitDepth;
  m_idrPicId          = idrPicId;
  m_fgsBlkSize        = 8;
  m_errorCode         = 0;

  if (!m_grainSynt)
    m_grainSynt       = new GrainSynthesisStruct;
  if (!m_fgcParameters)
    m_fgcParameters   = new SEIFilmGrainCharacteristics;
}

SEIFilmGrainSynthesizer::~SEIFilmGrainSynthesizer()
{
  destroy();
}

void SEIFilmGrainSynthesizer::fgsInit()
{
  deriveFGSBlkSize();
  dataBaseGen();
}

void SEIFilmGrainSynthesizer::destroy()
{
  if (m_fgcParameters)
    delete m_fgcParameters;
  if (m_grainSynt)
    delete m_grainSynt;
}

void SEIFilmGrainSynthesizer::grainSynthesizeAndBlend(PelStorage* pGrainBuf, bool isIdrPic)
{
  uint8_t     compCtr; /* number of color components */
  uint8_t     color_offset[MAX_NUM_COMPONENT];
  uint32_t    widthComp[MAX_NUM_COMPONENT], heightComp[MAX_NUM_COMPONENT];
  ptrdiff_t   strideComp[MAX_NUM_COMPONENT];
  uint32_t *  offsetsArr[MAX_NUM_COMPONENT];
  Pel *       decComp[MAX_NUM_COMPONENT];
  uint32_t    pseudoRandValEc;
  uint32_t    picOffset;

  /* from SMPTE RDD5 */
  color_offset[0] = COLOUR_OFFSET_LUMA;
  color_offset[1] = COLOUR_OFFSET_CR;
  color_offset[2] = COLOUR_OFFSET_CB;

  if (0 != m_fgcParameters->m_filmGrainCharacteristicsCancelFlag)
  {
    return;
  }

  const int numComp = getNumberValidComponents(m_chromaFormat);

  widthComp[0]  = m_width;
  heightComp[0] = m_height;

  if (ChromaFormat::_420 == m_chromaFormat)
  {
    widthComp[1]  = (m_width >> 1);
    widthComp[2]  = (m_width >> 1);
    heightComp[1] = (m_height >> 1);
    heightComp[2] = (m_height >> 1);
  }
  else if (ChromaFormat::_422 == m_chromaFormat)
  {
    widthComp[1]  = (m_width >> 1);
    widthComp[2]  = (m_width >> 1);
    heightComp[1] = m_height;
    heightComp[2] = m_height;
  }

  /*Allocate memory for offsets assuming 16x16 block size,
  32x32 will need lesser than this*/
  uint32_t maxNumBlocks = ((m_width >> 4) + 1) * ((m_height >> 4) + 1);

  for (compCtr = 0; compCtr < numComp; compCtr++)
  {
    offsetsArr[compCtr] = new uint32_t[maxNumBlocks];
  }

  decComp[0] = pGrainBuf->bufs[0].buf;
  decComp[1] = pGrainBuf->bufs[1].buf;
  decComp[2] = pGrainBuf->bufs[2].buf;

  /* component strides */
  strideComp[0] = pGrainBuf->bufs[0].stride;
  strideComp[1] = 0;
  strideComp[2] = 0;

  if (isChromaEnabled(m_chromaFormat))
  {
    strideComp[1] = pGrainBuf->bufs[1].stride;
    strideComp[2] = pGrainBuf->bufs[2].stride;
  }

  int32_t numBlks_x[MAX_NUM_COMPONENT];
  int32_t numBlks_y[MAX_NUM_COMPONENT];

  picOffset = m_poc;
  for (compCtr = 0; compCtr < numComp; compCtr++)
  {
    if (BLK_32 == m_fgsBlkSize)
    {
      numBlks_x[compCtr]         = (widthComp[compCtr] >> 5) + ((widthComp[compCtr] & 0x1F) ? 1 : 0);
      numBlks_y[compCtr]         = (heightComp[compCtr] >> 5) + ((heightComp[compCtr] & 0x1F) ? 1 : 0);
    }
    else
    {
      numBlks_x[compCtr]         = (widthComp[compCtr] >> 4) + ((widthComp[compCtr] & 0xF) ? 1 : 0);
      numBlks_y[compCtr]         = (heightComp[compCtr] >> 4) + ((heightComp[compCtr] & 0xF) ? 1 : 0);
    }
  }

  for (compCtr = 0; compCtr < numComp; compCtr++)
  {
    if (1 == m_fgcParameters->m_compModel[compCtr].presentFlag)
    {
      uint32_t *tmp = offsetsArr[compCtr];
      int       i, j;

      /* Seed initialization for current picture*/
      pseudoRandValEc = seedLUT[((picOffset + color_offset[compCtr]) & 0xFF)];

      for (i = 0; i < numBlks_y[compCtr]; i++)
      {
        for (j = 0; j < numBlks_x[compCtr]; j++)
        {
          *tmp            = pseudoRandValEc;
          pseudoRandValEc = prng(pseudoRandValEc);
          tmp++;
        }
      }
    }
  }

  m_fgsArgs.numComp = numComp;
  for (compCtr = 0; compCtr < numComp; compCtr++)
  {
    if (1 == m_fgcParameters->m_compModel[compCtr].presentFlag)
    {
      m_fgsArgs.decComp[compCtr]    = decComp[compCtr];
      m_fgsArgs.widthComp[compCtr]  = widthComp[compCtr];
      m_fgsArgs.strideComp[compCtr] = strideComp[compCtr];
      m_fgsArgs.fgsOffsets[compCtr] = offsetsArr[compCtr];

      if (BLK_32 == m_fgsBlkSize)
      {
        m_fgsArgs.heightComp[compCtr] = numBlks_y[compCtr] * BLK_32;
      }
      else
      {
        m_fgsArgs.heightComp[compCtr] = numBlks_y[compCtr] * BLK_16;
      }
    }
  }
  m_fgsArgs.pFgcParameters = m_fgcParameters;
  m_fgsArgs.blkSize = m_fgsBlkSize;
  m_fgsArgs.bitDepth = m_bitDepth;
  m_fgsArgs.pGrainSynt = m_grainSynt;

  fgsProcess(m_fgsArgs);

  for (compCtr = 0; compCtr < numComp; compCtr++)
  {
    delete offsetsArr[compCtr];
  }
  return;
}

/* Function validates film grain parameters and returns 0 for valid parameters of SMPTE-RDD5 else 1*/
/* Also down converts the chroma model values for 4:2:0 and 4:2:2 chroma_formats */
uint8_t SEIFilmGrainSynthesizer::grainValidateParams()
{
  uint8_t   compCtr, intensityCtr, multiGrainCheck[MAX_NUM_COMPONENT][MAX_NUM_INTENSITIES] = { { 0 } };
  uint16_t  multiGrainCtr;
  uint8_t   limitCompModelVal1[10] = { 0 }, limitCompModelVal2[10] = { 0 };
  uint8_t   num_comp_model_pairs = 0, limitCompModelCtr, compPairMatch;

  memset(m_grainSynt->intensityInterval, INTENSITY_INTERVAL_MATCH_FAIL, sizeof(m_grainSynt->intensityInterval));

  if ((m_width < MIN_WIDTH) || (m_width > MAX_WIDTH) || (m_width % 4))
  {
    return FGS_INVALID_WIDTH; /* Width not supported */
  }
  if ((m_height < MIN_HEIGHT) || (m_height > MAX_HEIGHT) || (m_height % 4))
  {
    return FGS_INVALID_HEIGHT; /* Height not  supported */
  }
  if (m_chromaFormat == ChromaFormat::UNDEFINED)
  {
    return FGS_INVALID_CHROMA_FORMAT; /* Chroma format not supported */
  }

  const int numComp = getNumberValidComponents(m_chromaFormat);

  if ((m_bitDepth < MIN_BIT_DEPTH) || (m_bitDepth > MAX_BIT_DEPTH))
  {
    return FGS_INVALID_BIT_DEPTH; /* Bit depth not supported */
  }

  if ((0 != m_fgcParameters->m_filmGrainCharacteristicsCancelFlag) &&
      (1 != m_fgcParameters->m_filmGrainCharacteristicsCancelFlag))
  {
    return FGS_INVALID_FGC_CANCEL_FLAG; /* Film grain synthesis disabled */
  }

  if (FILM_GRAIN_MODEL_ID_VALUE != m_fgcParameters->m_filmGrainModelId)
  {
    return FGS_INVALID_GRAIN_MODEL_ID; /* Not supported */
  }

  if (0 != m_fgcParameters->m_separateColourDescriptionPresentFlag)
  {
    return FGS_INVALID_SEP_COL_DES_FLAG; /* Not supported */
  }

  if (BLENDING_MODE_VALUE != m_fgcParameters->m_blendingModeId)
  {
    return FGS_INVALID_BLEND_MODE; /* Not supported */
  }

  if (m_fgcParameters->m_compModel[0].presentFlag || m_fgcParameters->m_compModel[1].presentFlag || m_fgcParameters->m_compModel[2].presentFlag) {
    if ((m_fgcParameters->m_log2ScaleFactor < MIN_LOG2SCALE_VALUE) ||
      (m_fgcParameters->m_log2ScaleFactor > MAX_LOG2SCALE_VALUE))
    {
      return FGS_INVALID_LOG2_SCALE_FACTOR; /* Not supported  */
    }
  }

  /* validation of component model present flag */
  for (compCtr = 0; compCtr < numComp; compCtr++)
  {
    if ((m_fgcParameters->m_compModel[compCtr].presentFlag != true) &&
        (m_fgcParameters->m_compModel[compCtr].presentFlag != false))
    {
      return FGS_INVALID_COMP_MODEL_PRESENT_FLAG; /* Not supported  */
    }
    if (m_fgcParameters->m_compModel[compCtr].presentFlag &&
       (m_fgcParameters->m_compModel[compCtr].numModelValues > MAX_ALLOWED_MODEL_VALUES))
    {
      return FGS_INVALID_NUM_MODEL_VALUES; /* Not supported  */
    }
  }

  /* validation of intensity intervals and  */
  for (compCtr = 0; compCtr < numComp; compCtr++)
  {
    if (m_fgcParameters->m_compModel[compCtr].presentFlag)
    {
      for (intensityCtr = 0; intensityCtr < m_fgcParameters->m_compModel[compCtr].intensityValues.size(); intensityCtr++)
      {

        if (m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].intensityIntervalLowerBound >
            m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].intensityIntervalUpperBound)
        {
          return FGS_INVALID_INTENSITY_BOUNDARY_VALUES; /* Not supported  */
        }

        for (multiGrainCtr = m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].intensityIntervalLowerBound;
             multiGrainCtr <= m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].intensityIntervalUpperBound; multiGrainCtr++)
        {
          m_grainSynt->intensityInterval[compCtr][multiGrainCtr] = intensityCtr;
          if (multiGrainCheck[compCtr][multiGrainCtr]) /* Non over lap */
          {
            return FGS_INVALID_INTENSITY_BOUNDARY_VALUES; /* Not supported  */
          }
          else
          {
            multiGrainCheck[compCtr][multiGrainCtr] = 1;
          }
        }

        m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue.resize(MAX_NUM_MODEL_VALUES);
        /* default initialization for cut off frequencies */
        if (1 == m_fgcParameters->m_compModel[compCtr].numModelValues)
        {
          m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[1] = DEFAULT_HORZ_CUT_OFF_FREQUENCY;
          m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[2] = m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[1];
        }
        else if (2 == m_fgcParameters->m_compModel[compCtr].numModelValues)
        {
          m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[2] = m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[1];
        }

        /* Error check on model component value */
        if (m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[0] > (MAX_STANDARD_DEVIATION << (m_bitDepth - BIT_DEPTH_8)))
        {
          return FGS_INVALID_STANDARD_DEVIATION; /* Not supported  */
        }
        else if ((m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[1] < MIN_CUT_OFF_FREQUENCY) ||
                 (m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[1] > MAX_CUT_OFF_FREQUENCY))
        {
          return FGS_INVALID_CUT_OFF_FREQUENCIES; /* Not supported  */
        }
        else if ((m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[2] < MIN_CUT_OFF_FREQUENCY) ||
                 (m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[2] > MAX_CUT_OFF_FREQUENCY))
        {
          return FGS_INVALID_CUT_OFF_FREQUENCIES; /* Not supported  */
        }

        /* conversion of component model values for 4:2:0 and 4:4:4 */
        if (ChromaFormat::_444 != m_chromaFormat && (compCtr > 0))
        {
          if (ChromaFormat::_420 == m_chromaFormat) /* 4:2:0 */
          {
            m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[0] >>= 1;
            m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[1] =
              CLIP3(MIN_CUT_OFF_FREQUENCY, MAX_CUT_OFF_FREQUENCY,
              (m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[1] << 1));
            m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[2] =
              CLIP3(MIN_CUT_OFF_FREQUENCY, MAX_CUT_OFF_FREQUENCY,
              (m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[2] << 1));

          }
          else if (ChromaFormat::_422 == m_chromaFormat) /* 4:2:2 */
          {
            m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[0] =
              (m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[0] * SCALE_DOWN_422) >> Q_FORMAT_SCALING;

            m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[1] =
              CLIP3(MIN_CUT_OFF_FREQUENCY, MAX_CUT_OFF_FREQUENCY,
              (m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[1] << 1));
          }
        }

        compPairMatch = 0;
        for (limitCompModelCtr = 0; limitCompModelCtr <= num_comp_model_pairs; limitCompModelCtr++)
        {
          if ((limitCompModelVal1[limitCompModelCtr] ==
            m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[1]) &&
            (limitCompModelVal2[limitCompModelCtr] ==
              m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[2]))
          {
            compPairMatch = 1;
          }
        }

        if (0 == compPairMatch)
        {
          num_comp_model_pairs++;
          /* max allowed pairs are 10 as per SMPTE -RDD5*/
          if (num_comp_model_pairs > MAX_ALLOWED_COMP_MODEL_PAIRS)
          {
            return FGS_INVALID_NUM_CUT_OFF_FREQ_PAIRS; /* Not supported  */
          }
          limitCompModelVal1[num_comp_model_pairs - 1] =
            m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[1];
          limitCompModelVal2[num_comp_model_pairs - 1] =
            m_fgcParameters->m_compModel[compCtr].intensityValues[intensityCtr].compModelValue[2];
        }
      }
    }
  }
  return FGS_SUCCESS; /* Success */
}

void SEIFilmGrainSynthesizer::deriveFGSBlkSize()
{
  uint32_t picSizeInLumaSamples = m_height * m_width;
  if (picSizeInLumaSamples <= (1920 * 1080))
  {
    m_fgsBlkSize = BLK_8;
  }
  else if (picSizeInLumaSamples <= (3840 * 2160))
  {
    m_fgsBlkSize = BLK_16;
  }
  else
  {
    m_fgsBlkSize = BLK_32;
  }
}
void SEIFilmGrainSynthesizer::dataBaseGen()
{
  uint32_t      pseudoRandValEhv;
  uint8_t       h, v; /* Horizaontal and vertical cut off frequencies (+2)*/
  uint32_t      ScaleCutOffFh, ScaleCutOffFv, l, r, i, j, k;
  int32_t       B[DATA_BASE_SIZE][DATA_BASE_SIZE], IDCT[DATA_BASE_SIZE][DATA_BASE_SIZE];
  int32_t       Grain[DATA_BASE_SIZE][DATA_BASE_SIZE];

  const TMatrixCoeff* Tmp = g_trCoreDCT2P64[TRANSFORM_FORWARD][0];
  const int transform_scale = 9;                  // upscaling of original transform as specified in VVC (for 64x64 block)
  const int add_1st = 1 << (transform_scale - 1);

  TMatrixCoeff T[DATA_BASE_SIZE][DATA_BASE_SIZE]; // Original
  TMatrixCoeff TT[DATA_BASE_SIZE][DATA_BASE_SIZE]; // Transpose
  for (int x = 0; x < DATA_BASE_SIZE; x++)
  {
    for (int y = 0; y < DATA_BASE_SIZE; y++)
    {
      T[x][y] = Tmp[x * 64 + y]; /* Matrix Original */
      TT[y][x] = Tmp[x * 64 + y]; /* Matrix Transpose */
    }
  }

  for (h = 0; h < NUM_CUT_OFF_FREQ; h++)
  {
    for (v = 0; v < NUM_CUT_OFF_FREQ; v++)
    {
      memset(&B, 0, DATA_BASE_SIZE*DATA_BASE_SIZE * sizeof(int32_t));
      memset(&IDCT, 0, DATA_BASE_SIZE*DATA_BASE_SIZE * sizeof(int32_t));
      memset(&Grain, 0, DATA_BASE_SIZE*DATA_BASE_SIZE * sizeof(int32_t));
      ScaleCutOffFh = ((h + 3) << 2) - 1;
      ScaleCutOffFv = ((v + 3) << 2) - 1;

      /* ehv : seed to be used for the psudo random generator for a given h and v */
      pseudoRandValEhv = seedLUT[h + v * 13];

      for (l = 0, r = 0; l <= ScaleCutOffFv; l++)
      {
        for (k = 0; k <= ScaleCutOffFh; k += 4)
        {
          B[k][l] = gaussianLUT[pseudoRandValEhv % 2048];
          B[k + 1][l] = gaussianLUT[(pseudoRandValEhv + 1) % 2048];
          B[k + 2][l] = gaussianLUT[(pseudoRandValEhv + 2) % 2048];
          B[k + 3][l] = gaussianLUT[(pseudoRandValEhv + 3) % 2048];
          r++;
          pseudoRandValEhv = prng(pseudoRandValEhv);
        }
      }
      B[0][0] = 0;

      for (i = 0; i < DATA_BASE_SIZE; i++)
      {
        for (j = 0; j < DATA_BASE_SIZE; j++)
        {
          for (k = 0; k < DATA_BASE_SIZE; k++)
          {
            IDCT[i][j] += TT[i][k] * B[k][j];
          }
          IDCT[i][j] += add_1st;
          IDCT[i][j] = IDCT[i][j] >> transform_scale;
        }
      }

      for (i = 0; i < DATA_BASE_SIZE; i++)
      {
        for (j = 0; j < DATA_BASE_SIZE; j++)
        {
          for (k = 0; k < DATA_BASE_SIZE; k++)
          {
            Grain[i][j] += IDCT[i][k] * T[k][j];
          }
          Grain[i][j] += add_1st;
          Grain[i][j] = Grain[i][j] >> transform_scale;
          m_grainSynt->dataBase[h][v][j][i] = CLIP3(-127, 127, Grain[i][j]);
        }
      }

      /* De-blocking at horizontal block edges */
      for (l = 0; l < DATA_BASE_SIZE; l += m_fgsBlkSize)
      {
        for (k = 0; k < DATA_BASE_SIZE; k++)
        {
          m_grainSynt->dataBase[h][v][l][k] = ((m_grainSynt->dataBase[h][v][l][k]) * deblockFactor[v]) >> 7;
          m_grainSynt->dataBase[h][v][l + m_fgsBlkSize - 1][k] = ((m_grainSynt->dataBase[h][v][l + m_fgsBlkSize - 1][k]) * deblockFactor[v]) >> 7;
        }
      }
    }
  }
  return;
}

uint32_t SEIFilmGrainSynthesizer::prng(uint32_t x_r)
{
  uint32_t addVal;
  addVal  = (1 + ((x_r & (POS_2)) > 0) + ((x_r & (POS_30)) > 0)) % 2;
  x_r     = (x_r << 1) + addVal;
  return x_r;
}

uint32_t SEIFilmGrainSynthesizer::fgsProcess(fgsProcessArgs &inArgs)
{
  uint32_t errorCode;
  uint8_t  blkSize = inArgs.blkSize;

  if (blkSize == 8)
    errorCode = fgsSimulationBlending_8x8(&inArgs);
  else if (blkSize == 16)
    errorCode = fgsSimulationBlending_16x16(&inArgs);
  else if (blkSize == 32)
    errorCode = fgsSimulationBlending_32x32(&inArgs);
  else
    errorCode = FGS_FAIL;

  return errorCode;
}

void SEIFilmGrainSynthesizer::deblockGrainStripe(Pel *grainStripe, uint32_t widthComp, uint32_t heightComp,
  uint32_t strideComp, uint32_t blkSize)
{
  int32_t  left1, left0, right0, right1;
  uint32_t pos, vertCtr;

  uint32_t widthCropped = (widthComp - blkSize);

  for (vertCtr = 0; vertCtr < heightComp; vertCtr++)
  {
    for (pos = 0; pos < widthCropped; pos += blkSize)
    {
      left1 = *(grainStripe + blkSize - 2);
      left0 = *(grainStripe + blkSize - 1);
      right0 = *(grainStripe + blkSize + 0);
      right1 = *(grainStripe + blkSize + 1);
      *(grainStripe + blkSize + 0) = (left0 + (right0 << 1) + right1) >> 2;
      *(grainStripe + blkSize - 1) = (left1 + (left0 << 1) + right0) >> 2;
      grainStripe += blkSize;
    }
    grainStripe = grainStripe + (strideComp - pos);
  }
  return;
}

void SEIFilmGrainSynthesizer::blendStripe(Pel *decSampleHbdOffsetY, Pel *grainStripe, uint32_t widthComp,
                                          ptrdiff_t strideSrc, ptrdiff_t strideGrain, uint32_t blockHeight,
                                          uint8_t bitDepth)
{
  uint32_t k, l;
  uint16_t maxRange;
  maxRange = (1 << bitDepth) - 1;

  int32_t  grainSample;
  uint16_t decodeSampleHbd;
  uint8_t bitDepthShift = (bitDepth - BIT_DEPTH_8);
  ptrdiff_t bufInc        = (strideSrc - widthComp);
  ptrdiff_t grainBufInc   = (strideGrain - widthComp);

  for (l = 0; l < blockHeight; l++) /* y direction */
  {
    for (k = 0; k < widthComp; k++) /* x direction */
    {
      decodeSampleHbd = *decSampleHbdOffsetY;
      grainSample = *grainStripe;
      grainSample <<= bitDepthShift;
      grainSample = CLIP3(0, maxRange, grainSample + decodeSampleHbd);
      *decSampleHbdOffsetY = (Pel)grainSample;
      decSampleHbdOffsetY++;
      grainStripe++;
    }
    decSampleHbdOffsetY += bufInc;
    grainStripe += grainBufInc;
  }
  return;
}

void SEIFilmGrainSynthesizer::blendStripe_32x32(Pel *decSampleHbdOffsetY, Pel *grainStripe, uint32_t widthComp,
                                                ptrdiff_t strideSrc, ptrdiff_t strideGrain, uint32_t blockHeight,
                                                uint8_t bitDepth)
{
  uint32_t k, l;
  uint16_t maxRange;
  maxRange = (1 << bitDepth) - 1;

  int32_t  grainSample;
  uint16_t decodeSampleHbd;
  uint8_t bitDepthShift = (bitDepth - BIT_DEPTH_8);
  ptrdiff_t bufInc        = (strideSrc - widthComp);
  ptrdiff_t grainBufInc   = (strideGrain - widthComp);

  for (l = 0; l < blockHeight; l++) /* y direction */
  {
    for (k = 0; k < widthComp; k++) /* x direction */
    {
      decodeSampleHbd = *decSampleHbdOffsetY;
      grainSample = *grainStripe;
      grainSample <<= bitDepthShift;
      grainSample = CLIP3(0, maxRange, grainSample + decodeSampleHbd);
      *decSampleHbdOffsetY = (Pel)grainSample;
      decSampleHbdOffsetY++;
      grainStripe++;
    }
    decSampleHbdOffsetY += bufInc;
    grainStripe += grainBufInc;
  }
  return;
}

Pel SEIFilmGrainSynthesizer::blockAverage_8x8(Pel *decSampleBlk8, ptrdiff_t widthComp, uint16_t *pNumSamples,
                                              uint8_t ySize, uint8_t xSize, uint8_t bitDepth)
{
  uint32_t blockAvg = 0;
  uint8_t  k;
  uint8_t l;
  for (k = 0; k < ySize; k++)
  {
    for (l = 0; l < xSize; l++)
    {
      blockAvg += *decSampleBlk8;
      decSampleBlk8++;
    }
    decSampleBlk8 += widthComp - xSize;
  }

  blockAvg = blockAvg >> (BLK_8_shift + (bitDepth - BIT_DEPTH_8));
  *pNumSamples = BLK_AREA_8x8;

  return blockAvg;
}

uint32_t SEIFilmGrainSynthesizer::blockAverage_16x16(Pel *decSampleBlk8, ptrdiff_t widthComp, uint16_t *pNumSamples,
                                                     uint8_t ySize, uint8_t xSize, uint8_t bitDepth)
{
  uint32_t blockAvg = 0;
  uint8_t  k;
  uint8_t l;
  for (k = 0; k < ySize; k++)
  {
    for (l = 0; l < xSize; l++)
    {
      blockAvg += *decSampleBlk8;
      decSampleBlk8++;
    }
    decSampleBlk8 += widthComp - xSize;
  }

  // blockAvg = blockAvg >> (BLK_16_shift + (bitDepth - BIT_DEPTH_8));
  // If BLK_16 is not used or changed BLK_AREA_16x16 has to be changed
  *pNumSamples = BLK_AREA_16x16;
  return blockAvg;
}

uint32_t SEIFilmGrainSynthesizer::blockAverage_32x32(Pel *decSampleBlk32, ptrdiff_t strideComp, uint8_t bitDepth)
{
  uint32_t blockAvg = 0;
  uint8_t  k;
  uint8_t l;
  ptrdiff_t bufInc = strideComp - BLK_32;
  for (k = 0; k < BLK_32; k++)
  {
    for (l = 0; l < BLK_32; l++)
    {
      blockAvg += *decSampleBlk32++;
    }
    decSampleBlk32 += bufInc;
  }
  blockAvg = blockAvg >> (BLK_32_shift + (bitDepth - BIT_DEPTH_8));
  return blockAvg;
}

void SEIFilmGrainSynthesizer::simulateGrainBlk8x8(Pel *grainStripe, uint32_t grainStripeOffsetBlk8,
  GrainSynthesisStruct *grain_synt, uint32_t width,
  uint8_t log2ScaleFactor, int16_t scaleFactor, uint32_t kOffset,
  uint32_t lOffset, uint8_t h, uint8_t v, uint32_t xSize)
{
  uint32_t l;
  int8_t * database_h_v = &grain_synt->dataBase[h][v][lOffset][kOffset];
  grainStripe += grainStripeOffsetBlk8;
  uint32_t k;
  for (l = 0; l < BLK_8; l++) /* y direction */
  {
    for (k = 0; k < xSize; k++) /* x direction */
    {
      *grainStripe = ((scaleFactor * (*database_h_v)) >> (log2ScaleFactor + GRAIN_SCALE));
      grainStripe++;
      database_h_v++;
    }
    grainStripe += width - xSize;
    database_h_v += DATA_BASE_SIZE - xSize;
  }
  return;
}

void SEIFilmGrainSynthesizer::simulateGrainBlk16x16(Pel *grainStripe, uint32_t grainStripeOffsetBlk8,
  GrainSynthesisStruct *grain_synt, uint32_t width,
  uint8_t log2ScaleFactor, int16_t scaleFactor, uint32_t kOffset,
  uint32_t lOffset, uint8_t h, uint8_t v, uint32_t xSize)
{
  uint32_t l;
  int8_t * database_h_v = &grain_synt->dataBase[h][v][lOffset][kOffset];
  grainStripe += grainStripeOffsetBlk8;
  uint32_t k;
  for (l = 0; l < BLK_16; l++) /* y direction */
  {
    for (k = 0; k < xSize; k++) /* x direction */
    {
      *grainStripe = (int16_t)(((int32_t)scaleFactor * (*database_h_v)) >> (log2ScaleFactor + GRAIN_SCALE));
      grainStripe++;
      database_h_v++;
    }
    grainStripe += width - xSize;
    database_h_v += DATA_BASE_SIZE - xSize;
  }
  return;
}

void SEIFilmGrainSynthesizer::simulateGrainBlk32x32(Pel *grainStripe, uint32_t grainStripeOffsetBlk32,
  GrainSynthesisStruct *grain_synt, uint32_t width,
  uint8_t log2ScaleFactor, int16_t scaleFactor, uint32_t kOffset,
  uint32_t lOffset, uint8_t h, uint8_t v)
{
  uint32_t l;
  int8_t * database_h_v = &grain_synt->dataBase[h][v][lOffset][kOffset];
  grainStripe += grainStripeOffsetBlk32;
  uint32_t k;
  uint8_t shiftVal = log2ScaleFactor + GRAIN_SCALE;
  uint32_t grainbufInc = width - BLK_32;

  for (l = 0; l < BLK_32; l++) /* y direction */
  {
    for (k = 0; k < BLK_32; k++) /* x direction */
    {
      *grainStripe = ((scaleFactor * (*database_h_v)) >> shiftVal);
      grainStripe++;
      database_h_v++;
    }
    grainStripe += grainbufInc;
    database_h_v += DATA_BASE_SIZE - BLK_32;
  }
  return;
}

uint32_t SEIFilmGrainSynthesizer::fgsSimulationBlending_8x8(fgsProcessArgs *inArgs)
{
  uint8_t  numComp, compCtr, blkId; /* number of color components */
  uint8_t  log2ScaleFactor, h, v;
  uint8_t  bitDepth; /*grain bit depth and decoded bit depth are assumed to be same */
  uint32_t  widthComp[MAX_NUM_COMPONENT], heightComp[MAX_NUM_COMPONENT];
  ptrdiff_t strideComp[MAX_NUM_COMPONENT];
  Pel *    decSampleHbdBlk16, *decSampleHbdBlk8, *decSampleHbdOffsetY;
  Pel *    decHbdComp[MAX_NUM_COMPONENT];
  uint16_t numSamples;
  int16_t  scaleFactor;
  uint32_t  kOffset, lOffset, grainStripeOffset, grainStripeOffsetBlk8;
  ptrdiff_t offsetBlk8x8;
  uint32_t kOffset_const, lOffset_const;
  int16_t  scaleFactor_const;
  Pel *    grainStripe; /* worth a row of 16x16 : Max size : 16xw;*/
  int32_t  yOffset8x8, xOffset8x8;
  uint32_t x, y;
  uint32_t blockAvg, intensityInt; /* ec : seed to be used for the psudo random generator for a given color component */
  uint32_t grainStripeWidth;
  uint32_t wdPadded;

  bitDepth        = inArgs->bitDepth;
  numComp         = inArgs->numComp;
  log2ScaleFactor = inArgs->pFgcParameters->m_log2ScaleFactor;

  for (compCtr = 0; compCtr < numComp; compCtr++)
  {
    decHbdComp[compCtr] = inArgs->decComp[compCtr];
    strideComp[compCtr] = inArgs->strideComp[compCtr];
    widthComp[compCtr]  = inArgs->widthComp[compCtr];
    heightComp[compCtr] = inArgs->heightComp[compCtr];
  }

  wdPadded = ((inArgs->widthComp[0] - 1) | 0xF) + 1;
  grainStripe = new Pel[wdPadded * BLK_16];

  if (0 == inArgs->pFgcParameters->m_filmGrainCharacteristicsCancelFlag)
  {
    for (compCtr = 0; compCtr < numComp; compCtr++)
    {
      if (1 == inArgs->pFgcParameters->m_compModel[compCtr].presentFlag)
      {
        decSampleHbdOffsetY  = decHbdComp[compCtr];
        uint32_t *offset_tmp = inArgs->fgsOffsets[compCtr];
        grainStripeWidth = ((widthComp[compCtr] - 1) | 0xF) + 1;   // Make next muliptle of 16

        /* Loop of 16x16 blocks */
        for (y = 0; y < heightComp[compCtr]; y += BLK_16)
        {
          /* Initialization of grain stripe of 16xwidth size */
          memset(grainStripe, 0, (grainStripeWidth * BLK_16 * sizeof(Pel)));
          for (x = 0; x < widthComp[compCtr]; x += BLK_16)
          {
            /* start position offset of decoded sample in x direction */
            grainStripeOffset = x;

            decSampleHbdBlk16 = decSampleHbdOffsetY + x;

            kOffset_const = (MSB16(*offset_tmp) % 52);
            kOffset_const &= 0xFFFC;

            lOffset_const = (LSB16(*offset_tmp) % 56);
            lOffset_const &= 0xFFF8;
            scaleFactor_const = 1 - 2 * BIT0(*offset_tmp);
            for (blkId = 0; blkId < NUM_8x8_BLKS_16x16; blkId++)
            {
              yOffset8x8   = (blkId >> 1) * BLK_8;
              xOffset8x8   = (blkId & 0x1) * BLK_8;
              offsetBlk8x8 = xOffset8x8 + (yOffset8x8 * strideComp[compCtr]);

              grainStripeOffsetBlk8 = grainStripeOffset + (xOffset8x8 + (yOffset8x8 * grainStripeWidth));

              decSampleHbdBlk8 = decSampleHbdBlk16 + offsetBlk8x8;
              blockAvg = blockAverage_8x8(decSampleHbdBlk8, strideComp[compCtr], &numSamples, BLK_8, BLK_8, bitDepth);

              /* Selection of the component model */
              intensityInt = inArgs->pGrainSynt->intensityInterval[compCtr][blockAvg];

              if (INTENSITY_INTERVAL_MATCH_FAIL != intensityInt)
              {
                /* 8x8 grain block offset using co-ordinates of decoded 8x8 block in the frame */
                // kOffset = kOffset_const;
                kOffset = kOffset_const + xOffset8x8;

                lOffset = lOffset_const + yOffset8x8;

                scaleFactor =
                  scaleFactor_const
                  * inArgs->pFgcParameters->m_compModel[compCtr].intensityValues[intensityInt].compModelValue[0];
                h = inArgs->pFgcParameters->m_compModel[compCtr].intensityValues[intensityInt].compModelValue[1] - 2;
                v = inArgs->pFgcParameters->m_compModel[compCtr].intensityValues[intensityInt].compModelValue[2] - 2;

                /* 8x8 block grain simulation */
                simulateGrainBlk8x8(grainStripe, grainStripeOffsetBlk8, inArgs->pGrainSynt, grainStripeWidth,
                                    log2ScaleFactor, scaleFactor, kOffset, lOffset, h, v, BLK_8);
              } /* only if average falls in any interval */
              //  }/* includes corner case handling */
            } /* 8x8 level block processing */

            /* uppdate the PRNG once per 16x16 block of samples */
            offset_tmp++;
          } /* End of 16xwidth grain simulation */

          /* deblocking at the vertical edges of 8x8 at 16xwidth*/
          deblockGrainStripe(grainStripe, widthComp[compCtr], BLK_16, grainStripeWidth, BLK_8);

          /* Blending of size 16xwidth*/

          blendStripe(decSampleHbdOffsetY, grainStripe, widthComp[compCtr], strideComp[compCtr], grainStripeWidth,
                      BLK_16, bitDepth);
          decSampleHbdOffsetY += BLK_16 * strideComp[compCtr];

        } /* end of component loop */
      }
    }
  }

  delete [] grainStripe;
  return FGS_SUCCESS;
}

uint32_t SEIFilmGrainSynthesizer::fgsSimulationBlending_16x16(fgsProcessArgs *inArgs)
{
  uint8_t  numComp, compCtr; /* number of color components */
  uint8_t  log2ScaleFactor, h, v;
  uint8_t  bitDepth; /*grain bit depth and decoded bit depth are assumed to be same */
  uint32_t  widthComp[MAX_NUM_COMPONENT], heightComp[MAX_NUM_COMPONENT];
  ptrdiff_t strideComp[MAX_NUM_COMPONENT];
  Pel *    decSampleHbdBlk16, *decSampleHbdOffsetY;
  Pel *    decHbdComp[MAX_NUM_COMPONENT];
  uint16_t numSamples;
  int16_t  scaleFactor;
  uint32_t kOffset, lOffset, grainStripeOffset;
  Pel *    grainStripe; /* worth a row of 16x16 : Max size : 16xw;*/
  uint32_t x, y;
  uint32_t blockAvg, intensityInt; /* ec : seed to be used for the psudo random generator for a given color component */
  uint32_t grainStripeWidth;
  uint32_t wdPadded;

  bitDepth        = inArgs->bitDepth;
  numComp         = inArgs->numComp;
  log2ScaleFactor = inArgs->pFgcParameters->m_log2ScaleFactor;

  for (compCtr = 0; compCtr < numComp; compCtr++)
  {
    decHbdComp[compCtr] = inArgs->decComp[compCtr];
    strideComp[compCtr] = inArgs->strideComp[compCtr];
    widthComp[compCtr]  = inArgs->widthComp[compCtr];
    heightComp[compCtr] = inArgs->heightComp[compCtr];
  }

  wdPadded = ((inArgs->widthComp[0] - 1) | 0xF) + 1;
  grainStripe = new Pel[wdPadded * BLK_16];

  if (0 == inArgs->pFgcParameters->m_filmGrainCharacteristicsCancelFlag)
  {
    for (compCtr = 0; compCtr < numComp; compCtr++)
    {
      if (1 == inArgs->pFgcParameters->m_compModel[compCtr].presentFlag)
      {
        decSampleHbdOffsetY  = decHbdComp[compCtr];
        uint32_t *offset_tmp = inArgs->fgsOffsets[compCtr];
        grainStripeWidth = ((widthComp[compCtr] - 1) | 0xF) + 1;   // Make next muliptle of 16

        /* Loop of 16x16 blocks */
        for (y = 0; y < heightComp[compCtr]; y += BLK_16)
        {
          /* Initialization of grain stripe of 16xwidth size */
          memset(grainStripe, 0, (grainStripeWidth * BLK_16 * sizeof(Pel)));
          for (x = 0; x < widthComp[compCtr]; x += BLK_16)
          {
            /* start position offset of decoded sample in x direction */
            grainStripeOffset = x;

            decSampleHbdBlk16 = decSampleHbdOffsetY + x;

            blockAvg =
              blockAverage_16x16(decSampleHbdBlk16, strideComp[compCtr], &numSamples, BLK_16, BLK_16, bitDepth);
            blockAvg = blockAvg >> (BLK_16_shift + (bitDepth - BIT_DEPTH_8));
            /* Selection of the component model */
            intensityInt = inArgs->pGrainSynt->intensityInterval[compCtr][blockAvg];

            if (INTENSITY_INTERVAL_MATCH_FAIL != intensityInt)
            {
              kOffset = (MSB16(*offset_tmp) % 52);
              kOffset &= 0xFFFC;

              lOffset = (LSB16(*offset_tmp) % 56);
              lOffset &= 0xFFF8;
              scaleFactor = 1 - 2 * BIT0(*offset_tmp);

              scaleFactor *=
                inArgs->pFgcParameters->m_compModel[compCtr].intensityValues[intensityInt].compModelValue[0];
              h = inArgs->pFgcParameters->m_compModel[compCtr].intensityValues[intensityInt].compModelValue[1] - 2;
              v = inArgs->pFgcParameters->m_compModel[compCtr].intensityValues[intensityInt].compModelValue[2] - 2;

              /* 16x16 block grain simulation */
              simulateGrainBlk16x16(grainStripe, grainStripeOffset, inArgs->pGrainSynt, grainStripeWidth,
                                    log2ScaleFactor, scaleFactor, kOffset, lOffset, h, v, BLK_16);

            } /* only if average falls in any interval */
            //  }/* includes corner case handling */
            /* uppdate the PRNG once per 16x16 block of samples */
            offset_tmp++;
          } /* End of 16xwidth grain simulation */
          /* deblocking at the vertical edges of 16x16 at 16xwidth*/
          deblockGrainStripe(grainStripe, widthComp[compCtr], BLK_16, grainStripeWidth, BLK_16);

          /* Blending of size 16xwidth*/
          blendStripe(decSampleHbdOffsetY, grainStripe, widthComp[compCtr], strideComp[compCtr], grainStripeWidth,
                      BLK_16, bitDepth);
          decSampleHbdOffsetY += BLK_16 * strideComp[compCtr];

        } /* end of component loop */
      }
    }
  }

  delete [] grainStripe;
  return FGS_SUCCESS;
}

uint32_t SEIFilmGrainSynthesizer::fgsSimulationBlending_32x32(fgsProcessArgs *inArgs)
{
  uint8_t  numComp, compCtr; /* number of color components */
  uint8_t  log2ScaleFactor, h, v;
  uint8_t  bitDepth; /*grain bit depth and decoded bit depth are assumed to be same */
  uint32_t  widthComp[MAX_NUM_COMPONENT], heightComp[MAX_NUM_COMPONENT];
  ptrdiff_t strideComp[MAX_NUM_COMPONENT];
  Pel *    decSampleBlk32, *decSampleOffsetY;
  Pel *    decComp[MAX_NUM_COMPONENT];
  int16_t  scaleFactor;
  uint32_t kOffset, lOffset, grainStripeOffset;
  Pel *    grainStripe;
  uint32_t x, y;
  uint32_t blockAvg, intensityInt; /* ec : seed to be used for the psudo random generator for a given color component */
  uint32_t grainStripeWidth;
  uint32_t wdPadded;

  bitDepth = inArgs->bitDepth;
  numComp  = inArgs->numComp;

  log2ScaleFactor = inArgs->pFgcParameters->m_log2ScaleFactor;

  for (compCtr = 0; compCtr < numComp; compCtr++)
  {
    decComp[compCtr]    = inArgs->decComp[compCtr];
    strideComp[compCtr] = inArgs->strideComp[compCtr];
    heightComp[compCtr] = inArgs->heightComp[compCtr];
    widthComp[compCtr]  = inArgs->widthComp[compCtr];
  }

  wdPadded = ((inArgs->widthComp[0] - 1) | 0x1F) + 1;
  grainStripe = new Pel[wdPadded * BLK_32];

  if (0 == inArgs->pFgcParameters->m_filmGrainCharacteristicsCancelFlag)
  {
    for (compCtr = 0; compCtr < numComp; compCtr++)
    {
      if (1 == inArgs->pFgcParameters->m_compModel[compCtr].presentFlag)
      {
        uint32_t *offset_tmp = inArgs->fgsOffsets[compCtr];
        decSampleOffsetY     = decComp[compCtr];
        grainStripeWidth = ((widthComp[compCtr] - 1) | 0x1F) + 1;   // Make next muliptle of 32

        /* Loop of 32x32 blocks */
        for (y = 0; y < heightComp[compCtr]; y += BLK_32)
        {
          /* Initialization of grain stripe of 32xwidth size */
          memset(grainStripe, 0, (grainStripeWidth * BLK_32 * sizeof(Pel)));
          for (x = 0; x < widthComp[compCtr]; x += BLK_32)
          {
            /* start position offset of decoded sample in x direction */
            grainStripeOffset = x;
            decSampleBlk32    = decSampleOffsetY + x;
            blockAvg = blockAverage_32x32(decSampleBlk32, strideComp[compCtr], bitDepth);

            /* Selection of the component model */
            intensityInt = inArgs->pGrainSynt->intensityInterval[compCtr][blockAvg];

            if (INTENSITY_INTERVAL_MATCH_FAIL != intensityInt)
            {
              kOffset = (MSB16(*offset_tmp) % 36);
              kOffset &= 0xFFFC;

              lOffset = (LSB16(*offset_tmp) % 40);
              lOffset &= 0xFFF8;
              scaleFactor = 1 - 2 * BIT0(*offset_tmp);

              scaleFactor *= inArgs->pFgcParameters->m_compModel[compCtr].intensityValues[intensityInt].compModelValue[0];
              h = inArgs->pFgcParameters->m_compModel[compCtr].intensityValues[intensityInt].compModelValue[1] - 2;
              v = inArgs->pFgcParameters->m_compModel[compCtr].intensityValues[intensityInt].compModelValue[2] - 2;

              /* 32x32 block grain simulation */
              simulateGrainBlk32x32(grainStripe, grainStripeOffset, inArgs->pGrainSynt, grainStripeWidth,
                                    log2ScaleFactor, scaleFactor, kOffset, lOffset, h, v);

            } /* only if average falls in any interval */

            /* uppdate the PRNG once per 16x16 block of samples */
            offset_tmp++;
          } /* End of 32xwidth grain simulation */

          /* deblocking at the vertical edges of 8x8 at 16xwidth*/
          deblockGrainStripe(grainStripe, widthComp[compCtr], BLK_32, grainStripeWidth, BLK_32);

          blendStripe_32x32(decSampleOffsetY, grainStripe, widthComp[compCtr], strideComp[compCtr], grainStripeWidth, BLK_32, bitDepth);
          decSampleOffsetY += BLK_32 * strideComp[compCtr];
        } /* end of component loop */
      }
    }
  }

  delete [] grainStripe;
  return FGS_SUCCESS;
}

