• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The libgav1 Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/film_grain.h"
16 
17 #include <algorithm>
18 #include <cassert>
19 #include <cstddef>
20 #include <cstdint>
21 #include <cstring>
22 #include <new>
23 
24 #include "src/dsp/common.h"
25 #include "src/dsp/constants.h"
26 #include "src/dsp/dsp.h"
27 #include "src/utils/array_2d.h"
28 #include "src/utils/blocking_counter.h"
29 #include "src/utils/common.h"
30 #include "src/utils/compiler_attributes.h"
31 #include "src/utils/constants.h"
32 #include "src/utils/logging.h"
33 #include "src/utils/threadpool.h"
34 
35 namespace libgav1 {
36 
37 namespace {
38 
39 // The kGaussianSequence array contains random samples from a Gaussian
40 // distribution with zero mean and standard deviation of about 512 clipped to
41 // the range of [-2048, 2047] (representable by a signed integer using 12 bits
42 // of precision) and rounded to the nearest multiple of 4.
43 //
44 // Note: It is important that every element in the kGaussianSequence array be
45 // less than 2040, so that RightShiftWithRounding(kGaussianSequence[i], 4) is
46 // less than 128 for bitdepth=8 (GrainType=int8_t).
47 constexpr int16_t kGaussianSequence[/*2048*/] = {
48     56,    568,   -180,  172,   124,   -84,   172,   -64,   -900,  24,   820,
49     224,   1248,  996,   272,   -8,    -916,  -388,  -732,  -104,  -188, 800,
50     112,   -652,  -320,  -376,  140,   -252,  492,   -168,  44,    -788, 588,
51     -584,  500,   -228,  12,    680,   272,   -476,  972,   -100,  652,  368,
52     432,   -196,  -720,  -192,  1000,  -332,  652,   -136,  -552,  -604, -4,
53     192,   -220,  -136,  1000,  -52,   372,   -96,   -624,  124,   -24,  396,
54     540,   -12,   -104,  640,   464,   244,   -208,  -84,   368,   -528, -740,
55     248,   -968,  -848,  608,   376,   -60,   -292,  -40,   -156,  252,  -292,
56     248,   224,   -280,  400,   -244,  244,   -60,   76,    -80,   212,  532,
57     340,   128,   -36,   824,   -352,  -60,   -264,  -96,   -612,  416,  -704,
58     220,   -204,  640,   -160,  1220,  -408,  900,   336,   20,    -336, -96,
59     -792,  304,   48,    -28,   -1232, -1172, -448,  104,   -292,  -520, 244,
60     60,    -948,  0,     -708,  268,   108,   356,   -548,  488,   -344, -136,
61     488,   -196,  -224,  656,   -236,  -1128, 60,    4,     140,   276,  -676,
62     -376,  168,   -108,  464,   8,     564,   64,    240,   308,   -300, -400,
63     -456,  -136,  56,    120,   -408,  -116,  436,   504,   -232,  328,  844,
64     -164,  -84,   784,   -168,  232,   -224,  348,   -376,  128,   568,  96,
65     -1244, -288,  276,   848,   832,   -360,  656,   464,   -384,  -332, -356,
66     728,   -388,  160,   -192,  468,   296,   224,   140,   -776,  -100, 280,
67     4,     196,   44,    -36,   -648,  932,   16,    1428,  28,    528,  808,
68     772,   20,    268,   88,    -332,  -284,  124,   -384,  -448,  208,  -228,
69     -1044, -328,  660,   380,   -148,  -300,  588,   240,   540,   28,   136,
70     -88,   -436,  256,   296,   -1000, 1400,  0,     -48,   1056,  -136, 264,
71     -528,  -1108, 632,   -484,  -592,  -344,  796,   124,   -668,  -768, 388,
72     1296,  -232,  -188,  -200,  -288,  -4,    308,   100,   -168,  256,  -500,
73     204,   -508,  648,   -136,  372,   -272,  -120,  -1004, -552,  -548, -384,
74     548,   -296,  428,   -108,  -8,    -912,  -324,  -224,  -88,   -112, -220,
75     -100,  996,   -796,  548,   360,   -216,  180,   428,   -200,  -212, 148,
76     96,    148,   284,   216,   -412,  -320,  120,   -300,  -384,  -604, -572,
77     -332,  -8,    -180,  -176,  696,   116,   -88,   628,   76,    44,   -516,
78     240,   -208,  -40,   100,   -592,  344,   -308,  -452,  -228,  20,   916,
79     -1752, -136,  -340,  -804,  140,   40,    512,   340,   248,   184,  -492,
80     896,   -156,  932,   -628,  328,   -688,  -448,  -616,  -752,  -100, 560,
81     -1020, 180,   -800,  -64,   76,    576,   1068,  396,   660,   552,  -108,
82     -28,   320,   -628,  312,   -92,   -92,   -472,  268,   16,    560,  516,
83     -672,  -52,   492,   -100,  260,   384,   284,   292,   304,   -148, 88,
84     -152,  1012,  1064,  -228,  164,   -376,  -684,  592,   -392,  156,  196,
85     -524,  -64,   -884,  160,   -176,  636,   648,   404,   -396,  -436, 864,
86     424,   -728,  988,   -604,  904,   -592,  296,   -224,  536,   -176, -920,
87     436,   -48,   1176,  -884,  416,   -776,  -824,  -884,  524,   -548, -564,
88     -68,   -164,  -96,   692,   364,   -692,  -1012, -68,   260,   -480, 876,
89     -1116, 452,   -332,  -352,  892,   -1088, 1220,  -676,  12,    -292, 244,
90     496,   372,   -32,   280,   200,   112,   -440,  -96,   24,    -644, -184,
91     56,    -432,  224,   -980,  272,   -260,  144,   -436,  420,   356,  364,
92     -528,  76,    172,   -744,  -368,  404,   -752,  -416,  684,   -688, 72,
93     540,   416,   92,    444,   480,   -72,   -1416, 164,   -1172, -68,  24,
94     424,   264,   1040,  128,   -912,  -524,  -356,  64,    876,   -12,  4,
95     -88,   532,   272,   -524,  320,   276,   -508,  940,   24,    -400, -120,
96     756,   60,    236,   -412,  100,   376,   -484,  400,   -100,  -740, -108,
97     -260,  328,   -268,  224,   -200,  -416,  184,   -604,  -564,  -20,  296,
98     60,    892,   -888,  60,    164,   68,    -760,  216,   -296,  904,  -336,
99     -28,   404,   -356,  -568,  -208,  -1480, -512,  296,   328,   -360, -164,
100     -1560, -776,  1156,  -428,  164,   -504,  -112,  120,   -216,  -148, -264,
101     308,   32,    64,    -72,   72,    116,   176,   -64,   -272,  460,  -536,
102     -784,  -280,  348,   108,   -752,  -132,  524,   -540,  -776,  116,  -296,
103     -1196, -288,  -560,  1040,  -472,  116,   -848,  -1116, 116,   636,  696,
104     284,   -176,  1016,  204,   -864,  -648,  -248,  356,   972,   -584, -204,
105     264,   880,   528,   -24,   -184,  116,   448,   -144,  828,   524,  212,
106     -212,  52,    12,    200,   268,   -488,  -404,  -880,  824,   -672, -40,
107     908,   -248,  500,   716,   -576,  492,   -576,  16,    720,   -108, 384,
108     124,   344,   280,   576,   -500,  252,   104,   -308,  196,   -188, -8,
109     1268,  296,   1032,  -1196, 436,   316,   372,   -432,  -200,  -660, 704,
110     -224,  596,   -132,  268,   32,    -452,  884,   104,   -1008, 424,  -1348,
111     -280,  4,     -1168, 368,   476,   696,   300,   -8,    24,    180,  -592,
112     -196,  388,   304,   500,   724,   -160,  244,   -84,   272,   -256, -420,
113     320,   208,   -144,  -156,  156,   364,   452,   28,    540,   316,  220,
114     -644,  -248,  464,   72,    360,   32,    -388,  496,   -680,  -48,  208,
115     -116,  -408,  60,    -604,  -392,  548,   -840,  784,   -460,  656,  -544,
116     -388,  -264,  908,   -800,  -628,  -612,  -568,  572,   -220,  164,  288,
117     -16,   -308,  308,   -112,  -636,  -760,  280,   -668,  432,   364,  240,
118     -196,  604,   340,   384,   196,   592,   -44,   -500,  432,   -580, -132,
119     636,   -76,   392,   4,     -412,  540,   508,   328,   -356,  -36,  16,
120     -220,  -64,   -248,  -60,   24,    -192,  368,   1040,  92,    -24,  -1044,
121     -32,   40,    104,   148,   192,   -136,  -520,  56,    -816,  -224, 732,
122     392,   356,   212,   -80,   -424,  -1008, -324,  588,   -1496, 576,  460,
123     -816,  -848,  56,    -580,  -92,   -1372, -112,  -496,  200,   364,  52,
124     -140,  48,    -48,   -60,   84,    72,    40,    132,   -356,  -268, -104,
125     -284,  -404,  732,   -520,  164,   -304,  -540,  120,   328,   -76,  -460,
126     756,   388,   588,   236,   -436,  -72,   -176,  -404,  -316,  -148, 716,
127     -604,  404,   -72,   -88,   -888,  -68,   944,   88,    -220,  -344, 960,
128     472,   460,   -232,  704,   120,   832,   -228,  692,   -508,  132,  -476,
129     844,   -748,  -364,  -44,   1116,  -1104, -1056, 76,    428,   552,  -692,
130     60,    356,   96,    -384,  -188,  -612,  -576,  736,   508,   892,  352,
131     -1132, 504,   -24,   -352,  324,   332,   -600,  -312,  292,   508,  -144,
132     -8,    484,   48,    284,   -260,  -240,  256,   -100,  -292,  -204, -44,
133     472,   -204,  908,   -188,  -1000, -256,  92,    1164,  -392,  564,  356,
134     652,   -28,   -884,  256,   484,   -192,  760,   -176,  376,   -524, -452,
135     -436,  860,   -736,  212,   124,   504,   -476,  468,   76,    -472, 552,
136     -692,  -944,  -620,  740,   -240,  400,   132,   20,    192,   -196, 264,
137     -668,  -1012, -60,   296,   -316,  -828,  76,    -156,  284,   -768, -448,
138     -832,  148,   248,   652,   616,   1236,  288,   -328,  -400,  -124, 588,
139     220,   520,   -696,  1032,  768,   -740,  -92,   -272,  296,   448,  -464,
140     412,   -200,  392,   440,   -200,  264,   -152,  -260,  320,   1032, 216,
141     320,   -8,    -64,   156,   -1016, 1084,  1172,  536,   484,   -432, 132,
142     372,   -52,   -256,  84,    116,   -352,  48,    116,   304,   -384, 412,
143     924,   -300,  528,   628,   180,   648,   44,    -980,  -220,  1320, 48,
144     332,   748,   524,   -268,  -720,  540,   -276,  564,   -344,  -208, -196,
145     436,   896,   88,    -392,  132,   80,    -964,  -288,  568,   56,   -48,
146     -456,  888,   8,     552,   -156,  -292,  948,   288,   128,   -716, -292,
147     1192,  -152,  876,   352,   -600,  -260,  -812,  -468,  -28,   -120, -32,
148     -44,   1284,  496,   192,   464,   312,   -76,   -516,  -380,  -456, -1012,
149     -48,   308,   -156,  36,    492,   -156,  -808,  188,   1652,  68,   -120,
150     -116,  316,   160,   -140,  352,   808,   -416,  592,   316,   -480, 56,
151     528,   -204,  -568,  372,   -232,  752,   -344,  744,   -4,    324,  -416,
152     -600,  768,   268,   -248,  -88,   -132,  -420,  -432,  80,    -288, 404,
153     -316,  -1216, -588,  520,   -108,  92,    -320,  368,   -480,  -216, -92,
154     1688,  -300,  180,   1020,  -176,  820,   -68,   -228,  -260,  436,  -904,
155     20,    40,    -508,  440,   -736,  312,   332,   204,   760,   -372, 728,
156     96,    -20,   -632,  -520,  -560,  336,   1076,  -64,   -532,  776,  584,
157     192,   396,   -728,  -520,  276,   -188,  80,    -52,   -612,  -252, -48,
158     648,   212,   -688,  228,   -52,   -260,  428,   -412,  -272,  -404, 180,
159     816,   -796,  48,    152,   484,   -88,   -216,  988,   696,   188,  -528,
160     648,   -116,  -180,  316,   476,   12,    -564,  96,    476,   -252, -364,
161     -376,  -392,  556,   -256,  -576,  260,   -352,  120,   -16,   -136, -260,
162     -492,  72,    556,   660,   580,   616,   772,   436,   424,   -32,  -324,
163     -1268, 416,   -324,  -80,   920,   160,   228,   724,   32,    -516, 64,
164     384,   68,    -128,  136,   240,   248,   -204,  -68,   252,   -932, -120,
165     -480,  -628,  -84,   192,   852,   -404,  -288,  -132,  204,   100,  168,
166     -68,   -196,  -868,  460,   1080,  380,   -80,   244,   0,     484,  -888,
167     64,    184,   352,   600,   460,   164,   604,   -196,  320,   -64,  588,
168     -184,  228,   12,    372,   48,    -848,  -344,  224,   208,   -200, 484,
169     128,   -20,   272,   -468,  -840,  384,   256,   -720,  -520,  -464, -580,
170     112,   -120,  644,   -356,  -208,  -608,  -528,  704,   560,   -424, 392,
171     828,   40,    84,    200,   -152,  0,     -144,  584,   280,   -120, 80,
172     -556,  -972,  -196,  -472,  724,   80,    168,   -32,   88,    160,  -688,
173     0,     160,   356,   372,   -776,  740,   -128,  676,   -248,  -480, 4,
174     -364,  96,    544,   232,   -1032, 956,   236,   356,   20,    -40,  300,
175     24,    -676,  -596,  132,   1120,  -104,  532,   -1096, 568,   648,  444,
176     508,   380,   188,   -376,  -604,  1488,  424,   24,    756,   -220, -192,
177     716,   120,   920,   688,   168,   44,    -460,  568,   284,   1144, 1160,
178     600,   424,   888,   656,   -356,  -320,  220,   316,   -176,  -724, -188,
179     -816,  -628,  -348,  -228,  -380,  1012,  -452,  -660,  736,   928,  404,
180     -696,  -72,   -268,  -892,  128,   184,   -344,  -780,  360,   336,  400,
181     344,   428,   548,   -112,  136,   -228,  -216,  -820,  -516,  340,  92,
182     -136,  116,   -300,  376,   -244,  100,   -316,  -520,  -284,  -12,  824,
183     164,   -548,  -180,  -128,  116,   -924,  -828,  268,   -368,  -580, 620,
184     192,   160,   0,     -1676, 1068,  424,   -56,   -360,  468,   -156, 720,
185     288,   -528,  556,   -364,  548,   -148,  504,   316,   152,   -648, -620,
186     -684,  -24,   -376,  -384,  -108,  -920,  -1032, 768,   180,   -264, -508,
187     -1268, -260,  -60,   300,   -240,  988,   724,   -376,  -576,  -212, -736,
188     556,   192,   1092,  -620,  -880,  376,   -56,   -4,    -216,  -32,  836,
189     268,   396,   1332,  864,   -600,  100,   56,    -412,  -92,   356,  180,
190     884,   -468,  -436,  292,   -388,  -804,  -704,  -840,  368,   -348, 140,
191     -724,  1536,  940,   372,   112,   -372,  436,   -480,  1136,  296,  -32,
192     -228,  132,   -48,   -220,  868,   -1016, -60,   -1044, -464,  328,  916,
193     244,   12,    -736,  -296,  360,   468,   -376,  -108,  -92,   788,  368,
194     -56,   544,   400,   -672,  -420,  728,   16,    320,   44,    -284, -380,
195     -796,  488,   132,   204,   -596,  -372,  88,    -152,  -908,  -636, -572,
196     -624,  -116,  -692,  -200,  -56,   276,   -88,   484,   -324,  948,  864,
197     1000,  -456,  -184,  -276,  292,   -296,  156,   676,   320,   160,  908,
198     -84,   -1236, -288,  -116,  260,   -372,  -644,  732,   -756,  -96,  84,
199     344,   -520,  348,   -688,  240,   -84,   216,   -1044, -136,  -676, -396,
200     -1500, 960,   -40,   176,   168,   1516,  420,   -504,  -344,  -364, -360,
201     1216,  -940,  -380,  -212,  252,   -660,  -708,  484,   -444,  -152, 928,
202     -120,  1112,  476,   -260,  560,   -148,  -344,  108,   -196,  228,  -288,
203     504,   560,   -328,  -88,   288,   -1008, 460,   -228,  468,   -836, -196,
204     76,    388,   232,   412,   -1168, -716,  -644,  756,   -172,  -356, -504,
205     116,   432,   528,   48,    476,   -168,  -608,  448,   160,   -532, -272,
206     28,    -676,  -12,   828,   980,   456,   520,   104,   -104,  256,  -344,
207     -4,    -28,   -368,  -52,   -524,  -572,  -556,  -200,  768,   1124, -208,
208     -512,  176,   232,   248,   -148,  -888,  604,   -600,  -304,  804,  -156,
209     -212,  488,   -192,  -804,  -256,  368,   -360,  -916,  -328,  228,  -240,
210     -448,  -472,  856,   -556,  -364,  572,   -12,   -156,  -368,  -340, 432,
211     252,   -752,  -152,  288,   268,   -580,  -848,  -592,  108,   -76,  244,
212     312,   -716,  592,   -80,   436,   360,   4,     -248,  160,   516,  584,
213     732,   44,    -468,  -280,  -292,  -156,  -588,  28,    308,   912,  24,
214     124,   156,   180,   -252,  944,   -924,  -772,  -520,  -428,  -624, 300,
215     -212,  -1144, 32,    -724,  800,   -1128, -212,  -1288, -848,  180,  -416,
216     440,   192,   -576,  -792,  -76,   -1080, 80,    -532,  -352,  -132, 380,
217     -820,  148,   1112,  128,   164,   456,   700,   -924,  144,   -668, -384,
218     648,   -832,  508,   552,   -52,   -100,  -656,  208,   -568,  748,  -88,
219     680,   232,   300,   192,   -408,  -1012, -152,  -252,  -268,  272,  -876,
220     -664,  -648,  -332,  -136,  16,    12,    1152,  -28,   332,   -536, 320,
221     -672,  -460,  -316,  532,   -260,  228,   -40,   1052,  -816,  180,  88,
222     -496,  -556,  -672,  -368,  428,   92,    356,   404,   -408,  252,  196,
223     -176,  -556,  792,   268,   32,    372,   40,    96,    -332,  328,  120,
224     372,   -900,  -40,   472,   -264,  -592,  952,   128,   656,   112,  664,
225     -232,  420,   4,     -344,  -464,  556,   244,   -416,  -32,   252,  0,
226     -412,  188,   -696,  508,   -476,  324,   -1096, 656,   -312,  560,  264,
227     -136,  304,   160,   -64,   -580,  248,   336,   -720,  560,   -348, -288,
228     -276,  -196,  -500,  852,   -544,  -236,  -1128, -992,  -776,  116,  56,
229     52,    860,   884,   212,   -12,   168,   1020,  512,   -552,  924,  -148,
230     716,   188,   164,   -340,  -520,  -184,  880,   -152,  -680,  -208, -1156,
231     -300,  -528,  -472,  364,   100,   -744,  -1056, -32,   540,   280,  144,
232     -676,  -32,   -232,  -280,  -224,  96,    568,   -76,   172,   148,  148,
233     104,   32,    -296,  -32,   788,   -80,   32,    -16,   280,   288,  944,
234     428,   -484};
235 static_assert(sizeof(kGaussianSequence) / sizeof(kGaussianSequence[0]) == 2048,
236               "");
237 
238 // The number of rows in a contiguous group computed by a single worker thread
239 // before checking for the next available group.
240 constexpr int kFrameChunkHeight = 8;
241 
242 // |width| and |height| refer to the plane, not the frame, meaning any
243 // subsampling should be applied by the caller.
244 template <typename Pixel>
CopyImagePlane(const uint8_t * source_plane,ptrdiff_t source_stride,int width,int height,uint8_t * dest_plane,ptrdiff_t dest_stride)245 inline void CopyImagePlane(const uint8_t* source_plane, ptrdiff_t source_stride,
246                            int width, int height, uint8_t* dest_plane,
247                            ptrdiff_t dest_stride) {
248   // If it's the same buffer there's nothing to do.
249   if (source_plane == dest_plane) return;
250 
251   int y = 0;
252   do {
253     memcpy(dest_plane, source_plane, width * sizeof(Pixel));
254     source_plane += source_stride;
255     dest_plane += dest_stride;
256   } while (++y < height);
257 }
258 
259 }  // namespace
260 
261 template <int bitdepth>
FilmGrain(const FilmGrainParams & params,bool is_monochrome,bool color_matrix_is_identity,int subsampling_x,int subsampling_y,int width,int height,ThreadPool * thread_pool)262 FilmGrain<bitdepth>::FilmGrain(const FilmGrainParams& params,
263                                bool is_monochrome,
264                                bool color_matrix_is_identity, int subsampling_x,
265                                int subsampling_y, int width, int height,
266                                ThreadPool* thread_pool)
267     : params_(params),
268       is_monochrome_(is_monochrome),
269       color_matrix_is_identity_(color_matrix_is_identity),
270       subsampling_x_(subsampling_x),
271       subsampling_y_(subsampling_y),
272       width_(width),
273       height_(height),
274       template_uv_width_((subsampling_x != 0) ? kMinChromaWidth
275                                               : kMaxChromaWidth),
276       template_uv_height_((subsampling_y != 0) ? kMinChromaHeight
277                                                : kMaxChromaHeight),
278       thread_pool_(thread_pool) {}
279 
280 template <int bitdepth>
Init()281 bool FilmGrain<bitdepth>::Init() {
282   // Section 7.18.3.3. Generate grain process.
283   const dsp::Dsp& dsp = *dsp::GetDspTable(bitdepth);
284   // If params_.num_y_points is 0, luma_grain_ will never be read, so we don't
285   // need to generate it.
286   const bool use_luma = params_.num_y_points > 0;
287   if (use_luma) {
288     GenerateLumaGrain(params_, luma_grain_);
289     // If params_.auto_regression_coeff_lag is 0, the filter is the identity
290     // filter and therefore can be skipped.
291     if (params_.auto_regression_coeff_lag > 0) {
292       dsp.film_grain
293           .luma_auto_regression[params_.auto_regression_coeff_lag - 1](
294               params_, luma_grain_);
295     }
296   } else {
297     // Have AddressSanitizer warn if luma_grain_ is used.
298     ASAN_POISON_MEMORY_REGION(luma_grain_, sizeof(luma_grain_));
299   }
300   if (!is_monochrome_) {
301     GenerateChromaGrains(params_, template_uv_width_, template_uv_height_,
302                          u_grain_, v_grain_);
303     if (params_.auto_regression_coeff_lag > 0 || use_luma) {
304       dsp.film_grain.chroma_auto_regression[static_cast<int>(
305           use_luma)][params_.auto_regression_coeff_lag](
306           params_, luma_grain_, subsampling_x_, subsampling_y_, u_grain_,
307           v_grain_);
308     }
309   }
310 
311   // Section 7.18.3.4. Scaling lookup initialization process.
312 
313   // Initialize scaling_lut_y_. If params_.num_y_points > 0, scaling_lut_y_
314   // is used for the Y plane. If params_.chroma_scaling_from_luma is true,
315   // scaling_lut_u_ and scaling_lut_v_ are the same as scaling_lut_y_ and are
316   // set up as aliases. So we need to initialize scaling_lut_y_ under these
317   // two conditions.
318   //
319   // Note: Although it does not seem to make sense, there are test vectors
320   // with chroma_scaling_from_luma=true and params_.num_y_points=0.
321   if (use_luma || params_.chroma_scaling_from_luma) {
322     dsp.film_grain.initialize_scaling_lut(
323         params_.num_y_points, params_.point_y_value, params_.point_y_scaling,
324         scaling_lut_y_);
325   } else {
326     ASAN_POISON_MEMORY_REGION(scaling_lut_y_, sizeof(scaling_lut_y_));
327   }
328   if (!is_monochrome_) {
329     if (params_.chroma_scaling_from_luma) {
330       scaling_lut_u_ = scaling_lut_y_;
331       scaling_lut_v_ = scaling_lut_y_;
332     } else if (params_.num_u_points > 0 || params_.num_v_points > 0) {
333       const size_t buffer_size =
334           (kScalingLookupTableSize + kScalingLookupTablePadding) *
335           (static_cast<int>(params_.num_u_points > 0) +
336            static_cast<int>(params_.num_v_points > 0));
337       scaling_lut_chroma_buffer_.reset(new (std::nothrow) uint8_t[buffer_size]);
338       if (scaling_lut_chroma_buffer_ == nullptr) return false;
339 
340       uint8_t* buffer = scaling_lut_chroma_buffer_.get();
341       if (params_.num_u_points > 0) {
342         scaling_lut_u_ = buffer;
343         dsp.film_grain.initialize_scaling_lut(
344             params_.num_u_points, params_.point_u_value,
345             params_.point_u_scaling, scaling_lut_u_);
346         buffer += kScalingLookupTableSize + kScalingLookupTablePadding;
347       }
348       if (params_.num_v_points > 0) {
349         scaling_lut_v_ = buffer;
350         dsp.film_grain.initialize_scaling_lut(
351             params_.num_v_points, params_.point_v_value,
352             params_.point_v_scaling, scaling_lut_v_);
353       }
354     }
355   }
356   return true;
357 }
358 
359 template <int bitdepth>
GenerateLumaGrain(const FilmGrainParams & params,GrainType * luma_grain)360 void FilmGrain<bitdepth>::GenerateLumaGrain(const FilmGrainParams& params,
361                                             GrainType* luma_grain) {
362   // If params.num_y_points is equal to 0, Section 7.18.3.3 specifies we set
363   // the luma_grain array to all zeros. But the Note at the end of Section
364   // 7.18.3.3 says luma_grain "will never be read in this case". So we don't
365   // call GenerateLumaGrain if params.num_y_points is equal to 0.
366   assert(params.num_y_points > 0);
367   const int shift = 12 - bitdepth + params.grain_scale_shift;
368   uint16_t seed = params.grain_seed;
369   GrainType* luma_grain_row = luma_grain;
370   for (int y = 0; y < kLumaHeight; ++y) {
371     for (int x = 0; x < kLumaWidth; ++x) {
372       luma_grain_row[x] = RightShiftWithRounding(
373           kGaussianSequence[GetFilmGrainRandomNumber(11, &seed)], shift);
374     }
375     luma_grain_row += kLumaWidth;
376   }
377 }
378 
379 template <int bitdepth>
GenerateChromaGrains(const FilmGrainParams & params,int chroma_width,int chroma_height,GrainType * u_grain,GrainType * v_grain)380 void FilmGrain<bitdepth>::GenerateChromaGrains(const FilmGrainParams& params,
381                                                int chroma_width,
382                                                int chroma_height,
383                                                GrainType* u_grain,
384                                                GrainType* v_grain) {
385   const int shift = 12 - bitdepth + params.grain_scale_shift;
386   if (params.num_u_points == 0 && !params.chroma_scaling_from_luma) {
387     memset(u_grain, 0, chroma_height * chroma_width * sizeof(*u_grain));
388   } else {
389     uint16_t seed = params.grain_seed ^ 0xb524;
390     GrainType* u_grain_row = u_grain;
391     assert(chroma_width > 0);
392     assert(chroma_height > 0);
393     int y = 0;
394     do {
395       int x = 0;
396       do {
397         u_grain_row[x] = RightShiftWithRounding(
398             kGaussianSequence[GetFilmGrainRandomNumber(11, &seed)], shift);
399       } while (++x < chroma_width);
400 
401       u_grain_row += chroma_width;
402     } while (++y < chroma_height);
403   }
404   if (params.num_v_points == 0 && !params.chroma_scaling_from_luma) {
405     memset(v_grain, 0, chroma_height * chroma_width * sizeof(*v_grain));
406   } else {
407     GrainType* v_grain_row = v_grain;
408     uint16_t seed = params.grain_seed ^ 0x49d8;
409     int y = 0;
410     do {
411       int x = 0;
412       do {
413         v_grain_row[x] = RightShiftWithRounding(
414             kGaussianSequence[GetFilmGrainRandomNumber(11, &seed)], shift);
415       } while (++x < chroma_width);
416 
417       v_grain_row += chroma_width;
418     } while (++y < chroma_height);
419   }
420 }
421 
422 template <int bitdepth>
AllocateNoiseStripes()423 bool FilmGrain<bitdepth>::AllocateNoiseStripes() {
424   const int half_height = DivideBy2(height_ + 1);
425   assert(half_height > 0);
426   // ceil(half_height / 16.0)
427   const int max_luma_num = DivideBy16(half_height + 15);
428   constexpr int kNoiseStripeHeight = 34;
429   size_t noise_buffer_size = kNoiseStripePadding;
430   if (params_.num_y_points > 0) {
431     noise_buffer_size += max_luma_num * kNoiseStripeHeight * width_;
432   }
433   if (!is_monochrome_) {
434     noise_buffer_size += 2 * max_luma_num *
435                          (kNoiseStripeHeight >> subsampling_y_) *
436                          SubsampledValue(width_, subsampling_x_);
437   }
438   noise_buffer_.reset(new (std::nothrow) GrainType[noise_buffer_size]);
439   if (noise_buffer_ == nullptr) return false;
440   GrainType* noise_buffer = noise_buffer_.get();
441   if (params_.num_y_points > 0) {
442     noise_stripes_[kPlaneY].Reset(max_luma_num, kNoiseStripeHeight * width_,
443                                   noise_buffer);
444     noise_buffer += max_luma_num * kNoiseStripeHeight * width_;
445   }
446   if (!is_monochrome_) {
447     noise_stripes_[kPlaneU].Reset(max_luma_num,
448                                   (kNoiseStripeHeight >> subsampling_y_) *
449                                       SubsampledValue(width_, subsampling_x_),
450                                   noise_buffer);
451     noise_buffer += max_luma_num * (kNoiseStripeHeight >> subsampling_y_) *
452                     SubsampledValue(width_, subsampling_x_);
453     noise_stripes_[kPlaneV].Reset(max_luma_num,
454                                   (kNoiseStripeHeight >> subsampling_y_) *
455                                       SubsampledValue(width_, subsampling_x_),
456                                   noise_buffer);
457   }
458   return true;
459 }
460 
461 template <int bitdepth>
AllocateNoiseImage()462 bool FilmGrain<bitdepth>::AllocateNoiseImage() {
463   if (params_.num_y_points > 0 &&
464       !noise_image_[kPlaneY].Reset(height_, width_ + kNoiseImagePadding,
465                                    /*zero_initialize=*/false)) {
466     return false;
467   }
468   if (!is_monochrome_) {
469     if (!noise_image_[kPlaneU].Reset(
470             (height_ + subsampling_y_) >> subsampling_y_,
471             ((width_ + subsampling_x_) >> subsampling_x_) + kNoiseImagePadding,
472             /*zero_initialize=*/false)) {
473       return false;
474     }
475     if (!noise_image_[kPlaneV].Reset(
476             (height_ + subsampling_y_) >> subsampling_y_,
477             ((width_ + subsampling_x_) >> subsampling_x_) + kNoiseImagePadding,
478             /*zero_initialize=*/false)) {
479       return false;
480     }
481   }
482   return true;
483 }
484 
485 // Uses |overlap_flag| to skip rows that are covered by the overlap computation.
486 template <int bitdepth>
ConstructNoiseImage(const Array2DView<GrainType> * noise_stripes,int width,int height,int subsampling_x,int subsampling_y,int stripe_start_offset,Array2D<GrainType> * noise_image)487 void FilmGrain<bitdepth>::ConstructNoiseImage(
488     const Array2DView<GrainType>* noise_stripes, int width, int height,
489     int subsampling_x, int subsampling_y, int stripe_start_offset,
490     Array2D<GrainType>* noise_image) {
491   const int plane_width = (width + subsampling_x) >> subsampling_x;
492   const int plane_height = (height + subsampling_y) >> subsampling_y;
493   const int stripe_height = 32 >> subsampling_y;
494   const int stripe_mask = stripe_height - 1;
495   int y = 0;
496   // |luma_num| = y >> (5 - |subsampling_y|). Hence |luma_num| == 0 for all y up
497   // to either 16 or 32.
498   const GrainType* first_noise_stripe = (*noise_stripes)[0];
499   do {
500     memcpy((*noise_image)[y], first_noise_stripe + y * plane_width,
501            plane_width * sizeof(first_noise_stripe[0]));
502   } while (++y < std::min(stripe_height, plane_height));
503   // End special iterations for luma_num == 0.
504 
505   int luma_num = 1;
506   for (; y < (plane_height & ~stripe_mask); ++luma_num, y += stripe_height) {
507     const GrainType* noise_stripe = (*noise_stripes)[luma_num];
508     int i = stripe_start_offset;
509     do {
510       memcpy((*noise_image)[y + i], noise_stripe + i * plane_width,
511              plane_width * sizeof(noise_stripe[0]));
512     } while (++i < stripe_height);
513   }
514 
515   // If there is a partial stripe, copy any rows beyond the overlap rows.
516   const int remaining_height = plane_height - y;
517   if (remaining_height > stripe_start_offset) {
518     assert(luma_num < noise_stripes->rows());
519     const GrainType* noise_stripe = (*noise_stripes)[luma_num];
520     int i = stripe_start_offset;
521     do {
522       memcpy((*noise_image)[y + i], noise_stripe + i * plane_width,
523              plane_width * sizeof(noise_stripe[0]));
524     } while (++i < remaining_height);
525   }
526 }
527 
528 template <int bitdepth>
BlendNoiseChromaWorker(const dsp::Dsp & dsp,const Plane * planes,int num_planes,std::atomic<int> * job_counter,int min_value,int max_chroma,const uint8_t * source_plane_y,ptrdiff_t source_stride_y,const uint8_t * source_plane_u,const uint8_t * source_plane_v,ptrdiff_t source_stride_uv,uint8_t * dest_plane_u,uint8_t * dest_plane_v,ptrdiff_t dest_stride_uv)529 void FilmGrain<bitdepth>::BlendNoiseChromaWorker(
530     const dsp::Dsp& dsp, const Plane* planes, int num_planes,
531     std::atomic<int>* job_counter, int min_value, int max_chroma,
532     const uint8_t* source_plane_y, ptrdiff_t source_stride_y,
533     const uint8_t* source_plane_u, const uint8_t* source_plane_v,
534     ptrdiff_t source_stride_uv, uint8_t* dest_plane_u, uint8_t* dest_plane_v,
535     ptrdiff_t dest_stride_uv) {
536   assert(num_planes > 0);
537   const int full_jobs_per_plane = height_ / kFrameChunkHeight;
538   const int remainder_job_height = height_ & (kFrameChunkHeight - 1);
539   const int total_full_jobs = full_jobs_per_plane * num_planes;
540   // If the frame height is not a multiple of kFrameChunkHeight, one job with
541   // a smaller number of rows is necessary at the end of each plane.
542   const int total_jobs =
543       total_full_jobs + ((remainder_job_height == 0) ? 0 : num_planes);
544   int job_index;
545   // Each job corresponds to a slice of kFrameChunkHeight rows in the luma
546   // plane. dsp->blend_noise_chroma handles subsampling.
547   // This loop body handles a slice of one plane or the other, depending on
548   // which are active. That way, threads working on consecutive jobs will keep
549   // the same region of luma source in working memory.
550   while ((job_index = job_counter->fetch_add(1, std::memory_order_relaxed)) <
551          total_jobs) {
552     const Plane plane = planes[job_index % num_planes];
553     const int slice_index = job_index / num_planes;
554     const int start_height = slice_index * kFrameChunkHeight;
555     const int job_height = std::min(height_ - start_height, kFrameChunkHeight);
556 
557     const auto* source_cursor_y = reinterpret_cast<const Pixel*>(
558         source_plane_y + start_height * source_stride_y);
559     const uint8_t* scaling_lut_uv;
560     const uint8_t* source_plane_uv;
561     uint8_t* dest_plane_uv;
562 
563     if (plane == kPlaneU) {
564       scaling_lut_uv = scaling_lut_u_;
565       source_plane_uv = source_plane_u;
566       dest_plane_uv = dest_plane_u;
567     } else {
568       assert(plane == kPlaneV);
569       scaling_lut_uv = scaling_lut_v_;
570       source_plane_uv = source_plane_v;
571       dest_plane_uv = dest_plane_v;
572     }
573     const auto* source_cursor_uv = reinterpret_cast<const Pixel*>(
574         source_plane_uv + (start_height >> subsampling_y_) * source_stride_uv);
575     auto* dest_cursor_uv = reinterpret_cast<Pixel*>(
576         dest_plane_uv + (start_height >> subsampling_y_) * dest_stride_uv);
577     dsp.film_grain.blend_noise_chroma[params_.chroma_scaling_from_luma](
578         plane, params_, noise_image_, min_value, max_chroma, width_, job_height,
579         start_height, subsampling_x_, subsampling_y_, scaling_lut_uv,
580         source_cursor_y, source_stride_y, source_cursor_uv, source_stride_uv,
581         dest_cursor_uv, dest_stride_uv);
582   }
583 }
584 
585 template <int bitdepth>
BlendNoiseLumaWorker(const dsp::Dsp & dsp,std::atomic<int> * job_counter,int min_value,int max_luma,const uint8_t * source_plane_y,ptrdiff_t source_stride_y,uint8_t * dest_plane_y,ptrdiff_t dest_stride_y)586 void FilmGrain<bitdepth>::BlendNoiseLumaWorker(
587     const dsp::Dsp& dsp, std::atomic<int>* job_counter, int min_value,
588     int max_luma, const uint8_t* source_plane_y, ptrdiff_t source_stride_y,
589     uint8_t* dest_plane_y, ptrdiff_t dest_stride_y) {
590   const int total_full_jobs = height_ / kFrameChunkHeight;
591   const int remainder_job_height = height_ & (kFrameChunkHeight - 1);
592   const int total_jobs =
593       total_full_jobs + static_cast<int>(remainder_job_height > 0);
594   int job_index;
595   // Each job is some number of rows in a plane.
596   while ((job_index = job_counter->fetch_add(1, std::memory_order_relaxed)) <
597          total_jobs) {
598     const int start_height = job_index * kFrameChunkHeight;
599     const int job_height = std::min(height_ - start_height, kFrameChunkHeight);
600 
601     const auto* source_cursor_y = reinterpret_cast<const Pixel*>(
602         source_plane_y + start_height * source_stride_y);
603     auto* dest_cursor_y =
604         reinterpret_cast<Pixel*>(dest_plane_y + start_height * dest_stride_y);
605     dsp.film_grain.blend_noise_luma(
606         noise_image_, min_value, max_luma, params_.chroma_scaling, width_,
607         job_height, start_height, scaling_lut_y_, source_cursor_y,
608         source_stride_y, dest_cursor_y, dest_stride_y);
609   }
610 }
611 
612 template <int bitdepth>
AddNoise(const uint8_t * source_plane_y,ptrdiff_t source_stride_y,const uint8_t * source_plane_u,const uint8_t * source_plane_v,ptrdiff_t source_stride_uv,uint8_t * dest_plane_y,ptrdiff_t dest_stride_y,uint8_t * dest_plane_u,uint8_t * dest_plane_v,ptrdiff_t dest_stride_uv)613 bool FilmGrain<bitdepth>::AddNoise(
614     const uint8_t* source_plane_y, ptrdiff_t source_stride_y,
615     const uint8_t* source_plane_u, const uint8_t* source_plane_v,
616     ptrdiff_t source_stride_uv, uint8_t* dest_plane_y, ptrdiff_t dest_stride_y,
617     uint8_t* dest_plane_u, uint8_t* dest_plane_v, ptrdiff_t dest_stride_uv) {
618   if (!Init()) {
619     LIBGAV1_DLOG(ERROR, "Init() failed.");
620     return false;
621   }
622   if (!AllocateNoiseStripes()) {
623     LIBGAV1_DLOG(ERROR, "AllocateNoiseStripes() failed.");
624     return false;
625   }
626 
627   const dsp::Dsp& dsp = *dsp::GetDspTable(bitdepth);
628   const bool use_luma = params_.num_y_points > 0;
629 
630   // Construct noise stripes.
631   if (use_luma) {
632     // The luma plane is never subsampled.
633     dsp.film_grain
634         .construct_noise_stripes[static_cast<int>(params_.overlap_flag)](
635             luma_grain_, params_.grain_seed, width_, height_,
636             /*subsampling_x=*/0, /*subsampling_y=*/0, &noise_stripes_[kPlaneY]);
637   }
638   if (!is_monochrome_) {
639     dsp.film_grain
640         .construct_noise_stripes[static_cast<int>(params_.overlap_flag)](
641             u_grain_, params_.grain_seed, width_, height_, subsampling_x_,
642             subsampling_y_, &noise_stripes_[kPlaneU]);
643     dsp.film_grain
644         .construct_noise_stripes[static_cast<int>(params_.overlap_flag)](
645             v_grain_, params_.grain_seed, width_, height_, subsampling_x_,
646             subsampling_y_, &noise_stripes_[kPlaneV]);
647   }
648 
649   if (!AllocateNoiseImage()) {
650     LIBGAV1_DLOG(ERROR, "AllocateNoiseImage() failed.");
651     return false;
652   }
653 
654   // Construct noise image.
655   if (use_luma) {
656     ConstructNoiseImage(
657         &noise_stripes_[kPlaneY], width_, height_, /*subsampling_x=*/0,
658         /*subsampling_y=*/0, static_cast<int>(params_.overlap_flag) << 1,
659         &noise_image_[kPlaneY]);
660     if (params_.overlap_flag) {
661       dsp.film_grain.construct_noise_image_overlap(
662           &noise_stripes_[kPlaneY], width_, height_, /*subsampling_x=*/0,
663           /*subsampling_y=*/0, &noise_image_[kPlaneY]);
664     }
665   }
666   if (!is_monochrome_) {
667     ConstructNoiseImage(&noise_stripes_[kPlaneU], width_, height_,
668                         subsampling_x_, subsampling_y_,
669                         static_cast<int>(params_.overlap_flag)
670                             << (1 - subsampling_y_),
671                         &noise_image_[kPlaneU]);
672     ConstructNoiseImage(&noise_stripes_[kPlaneV], width_, height_,
673                         subsampling_x_, subsampling_y_,
674                         static_cast<int>(params_.overlap_flag)
675                             << (1 - subsampling_y_),
676                         &noise_image_[kPlaneV]);
677     if (params_.overlap_flag) {
678       dsp.film_grain.construct_noise_image_overlap(
679           &noise_stripes_[kPlaneU], width_, height_, subsampling_x_,
680           subsampling_y_, &noise_image_[kPlaneU]);
681       dsp.film_grain.construct_noise_image_overlap(
682           &noise_stripes_[kPlaneV], width_, height_, subsampling_x_,
683           subsampling_y_, &noise_image_[kPlaneV]);
684     }
685   }
686 
687   // Blend noise image.
688   int min_value;
689   int max_luma;
690   int max_chroma;
691   if (params_.clip_to_restricted_range) {
692     min_value = 16 << (bitdepth - 8);
693     max_luma = 235 << (bitdepth - 8);
694     if (color_matrix_is_identity_) {
695       max_chroma = max_luma;
696     } else {
697       max_chroma = 240 << (bitdepth - 8);
698     }
699   } else {
700     min_value = 0;
701     max_luma = (256 << (bitdepth - 8)) - 1;
702     max_chroma = max_luma;
703   }
704 
705   // Handle all chroma planes first because luma source may be altered in place.
706   if (!is_monochrome_) {
707     // This is done in a strange way but Vector can't be passed by copy to the
708     // lambda capture that spawns the thread.
709     Plane planes_to_blend[2];
710     int num_planes = 0;
711     if (params_.chroma_scaling_from_luma) {
712       // Both noise planes are computed from the luma scaling lookup table.
713       planes_to_blend[num_planes++] = kPlaneU;
714       planes_to_blend[num_planes++] = kPlaneV;
715     } else {
716       const int height_uv = SubsampledValue(height_, subsampling_y_);
717       const int width_uv = SubsampledValue(width_, subsampling_x_);
718 
719       // Noise is applied according to a lookup table defined by pieceiwse
720       // linear "points." If the lookup table is empty, that corresponds to
721       // outputting zero noise.
722       if (params_.num_u_points == 0) {
723         CopyImagePlane<Pixel>(source_plane_u, source_stride_uv, width_uv,
724                               height_uv, dest_plane_u, dest_stride_uv);
725       } else {
726         planes_to_blend[num_planes++] = kPlaneU;
727       }
728       if (params_.num_v_points == 0) {
729         CopyImagePlane<Pixel>(source_plane_v, source_stride_uv, width_uv,
730                               height_uv, dest_plane_v, dest_stride_uv);
731       } else {
732         planes_to_blend[num_planes++] = kPlaneV;
733       }
734     }
735     if (thread_pool_ != nullptr && num_planes > 0) {
736       const int num_workers = thread_pool_->num_threads();
737       BlockingCounter pending_workers(num_workers);
738       std::atomic<int> job_counter(0);
739       for (int i = 0; i < num_workers; ++i) {
740         thread_pool_->Schedule([this, dsp, &pending_workers, &planes_to_blend,
741                                 num_planes, &job_counter, min_value, max_chroma,
742                                 source_plane_y, source_stride_y, source_plane_u,
743                                 source_plane_v, source_stride_uv, dest_plane_u,
744                                 dest_plane_v, dest_stride_uv]() {
745           BlendNoiseChromaWorker(dsp, planes_to_blend, num_planes, &job_counter,
746                                  min_value, max_chroma, source_plane_y,
747                                  source_stride_y, source_plane_u,
748                                  source_plane_v, source_stride_uv, dest_plane_u,
749                                  dest_plane_v, dest_stride_uv);
750           pending_workers.Decrement();
751         });
752       }
753       BlendNoiseChromaWorker(
754           dsp, planes_to_blend, num_planes, &job_counter, min_value, max_chroma,
755           source_plane_y, source_stride_y, source_plane_u, source_plane_v,
756           source_stride_uv, dest_plane_u, dest_plane_v, dest_stride_uv);
757 
758       pending_workers.Wait();
759     } else {
760       // Single threaded.
761       if (params_.num_u_points > 0 || params_.chroma_scaling_from_luma) {
762         dsp.film_grain.blend_noise_chroma[params_.chroma_scaling_from_luma](
763             kPlaneU, params_, noise_image_, min_value, max_chroma, width_,
764             height_, /*start_height=*/0, subsampling_x_, subsampling_y_,
765             scaling_lut_u_, source_plane_y, source_stride_y, source_plane_u,
766             source_stride_uv, dest_plane_u, dest_stride_uv);
767       }
768       if (params_.num_v_points > 0 || params_.chroma_scaling_from_luma) {
769         dsp.film_grain.blend_noise_chroma[params_.chroma_scaling_from_luma](
770             kPlaneV, params_, noise_image_, min_value, max_chroma, width_,
771             height_, /*start_height=*/0, subsampling_x_, subsampling_y_,
772             scaling_lut_v_, source_plane_y, source_stride_y, source_plane_v,
773             source_stride_uv, dest_plane_v, dest_stride_uv);
774       }
775     }
776   }
777   if (use_luma) {
778     if (thread_pool_ != nullptr) {
779       const int num_workers = thread_pool_->num_threads();
780       BlockingCounter pending_workers(num_workers);
781       std::atomic<int> job_counter(0);
782       for (int i = 0; i < num_workers; ++i) {
783         thread_pool_->Schedule(
784             [this, dsp, &pending_workers, &job_counter, min_value, max_luma,
785              source_plane_y, source_stride_y, dest_plane_y, dest_stride_y]() {
786               BlendNoiseLumaWorker(dsp, &job_counter, min_value, max_luma,
787                                    source_plane_y, source_stride_y,
788                                    dest_plane_y, dest_stride_y);
789               pending_workers.Decrement();
790             });
791       }
792 
793       BlendNoiseLumaWorker(dsp, &job_counter, min_value, max_luma,
794                            source_plane_y, source_stride_y, dest_plane_y,
795                            dest_stride_y);
796       pending_workers.Wait();
797     } else {
798       dsp.film_grain.blend_noise_luma(
799           noise_image_, min_value, max_luma, params_.chroma_scaling, width_,
800           height_, /*start_height=*/0, scaling_lut_y_, source_plane_y,
801           source_stride_y, dest_plane_y, dest_stride_y);
802     }
803   } else {
804     CopyImagePlane<Pixel>(source_plane_y, source_stride_y, width_, height_,
805                           dest_plane_y, dest_stride_y);
806   }
807 
808   return true;
809 }
810 
811 // Explicit instantiations.
812 template class FilmGrain<8>;
813 #if LIBGAV1_MAX_BITDEPTH >= 10
814 template class FilmGrain<10>;
815 #endif
816 
817 }  // namespace libgav1
818