FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/aom_film_grain_template.c
Date: 2024-04-19 17:50:32
Exec Total Coverage
Lines: 0 261 0.0%
Functions: 0 14 0.0%
Branches: 0 172 0.0%

Line Branch Exec Source
1 /*
2 * AOM film grain synthesis
3 * Copyright (c) 2023 Niklas Haas <ffmpeg@haasn.xyz>
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /*
23 * Copyright © 2018, Niklas Haas
24 * Copyright © 2018, VideoLAN and dav1d authors
25 * Copyright © 2018, Two Orioles, LLC
26 * All rights reserved.
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions are met:
30 *
31 * 1. Redistributions of source code must retain the above copyright notice, this
32 * list of conditions and the following disclaimer.
33 *
34 * 2. Redistributions in binary form must reproduce the above copyright notice,
35 * this list of conditions and the following disclaimer in the documentation
36 * and/or other materials provided with the distribution.
37 *
38 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
39 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
40 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
41 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
42 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48 */
49
50 #include "bit_depth_template.c"
51
52 #undef entry
53 #undef bitdepth
54 #undef bitdepth_max
55 #undef HBD_DECL
56 #undef HBD_CALL
57 #undef SCALING_SIZE
58
59 #if BIT_DEPTH > 8
60 # define entry int16_t
61 # define bitdepth_max ((1 << bitdepth) - 1)
62 # define HBD_DECL , const int bitdepth
63 # define HBD_CALL , bitdepth
64 # define SCALING_SIZE 4096
65 #else
66 # define entry int8_t
67 # define bitdepth 8
68 # define bitdepth_max UINT8_MAX
69 # define HBD_DECL
70 # define HBD_CALL
71 # define SCALING_SIZE 256
72 #endif
73
74 static void FUNC(generate_grain_y_c)(entry buf[][GRAIN_WIDTH],
75 const AVFilmGrainParams *const params
76 HBD_DECL)
77 {
78 const AVFilmGrainAOMParams *const data = &params->codec.aom;
79 const int bitdepth_min_8 = bitdepth - 8;
80 unsigned seed = params->seed;
81 const int shift = 4 - bitdepth_min_8 + data->grain_scale_shift;
82 const int grain_ctr = 128 << bitdepth_min_8;
83 const int grain_min = -grain_ctr, grain_max = grain_ctr - 1;
84
85 const int ar_pad = 3;
86 const int ar_lag = data->ar_coeff_lag;
87
88 for (int y = 0; y < GRAIN_HEIGHT; y++) {
89 for (int x = 0; x < GRAIN_WIDTH; x++) {
90 const int value = get_random_number(11, &seed);
91 buf[y][x] = round2(gaussian_sequence[ value ], shift);
92 }
93 }
94
95 for (int y = ar_pad; y < GRAIN_HEIGHT; y++) {
96 for (int x = ar_pad; x < GRAIN_WIDTH - ar_pad; x++) {
97 const int8_t *coeff = data->ar_coeffs_y;
98 int sum = 0, grain;
99 for (int dy = -ar_lag; dy <= 0; dy++) {
100 for (int dx = -ar_lag; dx <= ar_lag; dx++) {
101 if (!dx && !dy)
102 break;
103 sum += *(coeff++) * buf[y + dy][x + dx];
104 }
105 }
106
107 grain = buf[y][x] + round2(sum, data->ar_coeff_shift);
108 buf[y][x] = av_clip(grain, grain_min, grain_max);
109 }
110 }
111 }
112
113 static void
114 FUNC(generate_grain_uv_c)(entry buf[][GRAIN_WIDTH],
115 const entry buf_y[][GRAIN_WIDTH],
116 const AVFilmGrainParams *const params, const intptr_t uv,
117 const int subx, const int suby HBD_DECL)
118 {
119 const AVFilmGrainAOMParams *const data = &params->codec.aom;
120 const int bitdepth_min_8 = bitdepth - 8;
121 unsigned seed = params->seed ^ (uv ? 0x49d8 : 0xb524);
122 const int shift = 4 - bitdepth_min_8 + data->grain_scale_shift;
123 const int grain_ctr = 128 << bitdepth_min_8;
124 const int grain_min = -grain_ctr, grain_max = grain_ctr - 1;
125
126 const int chromaW = subx ? SUB_GRAIN_WIDTH : GRAIN_WIDTH;
127 const int chromaH = suby ? SUB_GRAIN_HEIGHT : GRAIN_HEIGHT;
128
129 const int ar_pad = 3;
130 const int ar_lag = data->ar_coeff_lag;
131
132 for (int y = 0; y < chromaH; y++) {
133 for (int x = 0; x < chromaW; x++) {
134 const int value = get_random_number(11, &seed);
135 buf[y][x] = round2(gaussian_sequence[ value ], shift);
136 }
137 }
138
139 for (int y = ar_pad; y < chromaH; y++) {
140 for (int x = ar_pad; x < chromaW - ar_pad; x++) {
141 const int8_t *coeff = data->ar_coeffs_uv[uv];
142 int sum = 0, grain;
143 for (int dy = -ar_lag; dy <= 0; dy++) {
144 for (int dx = -ar_lag; dx <= ar_lag; dx++) {
145 // For the final (current) pixel, we need to add in the
146 // contribution from the luma grain texture
147 if (!dx && !dy) {
148 const int lumaX = ((x - ar_pad) << subx) + ar_pad;
149 const int lumaY = ((y - ar_pad) << suby) + ar_pad;
150 int luma = 0;
151 if (!data->num_y_points)
152 break;
153 for (int i = 0; i <= suby; i++) {
154 for (int j = 0; j <= subx; j++) {
155 luma += buf_y[lumaY + i][lumaX + j];
156 }
157 }
158 luma = round2(luma, subx + suby);
159 sum += luma * (*coeff);
160 break;
161 }
162
163 sum += *(coeff++) * buf[y + dy][x + dx];
164 }
165 }
166
167 grain = buf[y][x] + round2(sum, data->ar_coeff_shift);
168 buf[y][x] = av_clip(grain, grain_min, grain_max);
169 }
170 }
171 }
172
173 // samples from the correct block of a grain LUT, while taking into account the
174 // offsets provided by the offsets cache
175 static inline entry FUNC(sample_lut)(const entry grain_lut[][GRAIN_WIDTH],
176 const int offsets[2][2],
177 const int subx, const int suby,
178 const int bx, const int by,
179 const int x, const int y)
180 {
181 const int randval = offsets[bx][by];
182 const int offx = 3 + (2 >> subx) * (3 + (randval >> 4));
183 const int offy = 3 + (2 >> suby) * (3 + (randval & 0xF));
184 return grain_lut[offy + y + (FG_BLOCK_SIZE >> suby) * by]
185 [offx + x + (FG_BLOCK_SIZE >> subx) * bx];
186 }
187
188 static void FUNC(fgy_32x32xn_c)(pixel *const dst_row, const pixel *const src_row,
189 const ptrdiff_t stride,
190 const AVFilmGrainParams *const params, const size_t pw,
191 const uint8_t scaling[SCALING_SIZE],
192 const entry grain_lut[][GRAIN_WIDTH],
193 const int bh, const int row_num HBD_DECL)
194 {
195 const AVFilmGrainAOMParams *const data = &params->codec.aom;
196 const int rows = 1 + (data->overlap_flag && row_num > 0);
197 const int bitdepth_min_8 = bitdepth - 8;
198 const int grain_ctr = 128 << bitdepth_min_8;
199 const int grain_min = -grain_ctr, grain_max = grain_ctr - 1;
200 unsigned seed[2];
201 int offsets[2 /* col offset */][2 /* row offset */];
202
203 int min_value, max_value;
204 if (data->limit_output_range) {
205 min_value = 16 << bitdepth_min_8;
206 max_value = 235 << bitdepth_min_8;
207 } else {
208 min_value = 0;
209 max_value = bitdepth_max;
210 }
211
212 // seed[0] contains the current row, seed[1] contains the previous
213 for (int i = 0; i < rows; i++) {
214 seed[i] = params->seed;
215 seed[i] ^= (((row_num - i) * 37 + 178) & 0xFF) << 8;
216 seed[i] ^= (((row_num - i) * 173 + 105) & 0xFF);
217 }
218
219 av_assert1(stride % (FG_BLOCK_SIZE * sizeof(pixel)) == 0);
220
221 // process this row in FG_BLOCK_SIZE^2 blocks
222 for (unsigned bx = 0; bx < pw; bx += FG_BLOCK_SIZE) {
223 const int bw = FFMIN(FG_BLOCK_SIZE, (int) pw - bx);
224 const pixel *src;
225 pixel *dst;
226 int noise;
227
228 // x/y block offsets to compensate for overlapped regions
229 const int ystart = data->overlap_flag && row_num ? FFMIN(2, bh) : 0;
230 const int xstart = data->overlap_flag && bx ? FFMIN(2, bw) : 0;
231
232 static const int w[2][2] = { { 27, 17 }, { 17, 27 } };
233
234 if (data->overlap_flag && bx) {
235 // shift previous offsets left
236 for (int i = 0; i < rows; i++)
237 offsets[1][i] = offsets[0][i];
238 }
239
240 // update current offsets
241 for (int i = 0; i < rows; i++)
242 offsets[0][i] = get_random_number(8, &seed[i]);
243
244 #define add_noise_y(x, y, grain) \
245 src = (const pixel*)((const char*)src_row + (y) * stride) + (x) + bx; \
246 dst = (pixel*)((char*)dst_row + (y) * stride) + (x) + bx; \
247 noise = round2(scaling[ *src ] * (grain), data->scaling_shift); \
248 *dst = av_clip(*src + noise, min_value, max_value);
249
250 for (int y = ystart; y < bh; y++) {
251 // Non-overlapped image region (straightforward)
252 for (int x = xstart; x < bw; x++) {
253 int grain = FUNC(sample_lut)(grain_lut, offsets, 0, 0, 0, 0, x, y);
254 add_noise_y(x, y, grain);
255 }
256
257 // Special case for overlapped column
258 for (int x = 0; x < xstart; x++) {
259 int grain = FUNC(sample_lut)(grain_lut, offsets, 0, 0, 0, 0, x, y);
260 int old = FUNC(sample_lut)(grain_lut, offsets, 0, 0, 1, 0, x, y);
261 grain = round2(old * w[x][0] + grain * w[x][1], 5);
262 grain = av_clip(grain, grain_min, grain_max);
263 add_noise_y(x, y, grain);
264 }
265 }
266
267 for (int y = 0; y < ystart; y++) {
268 // Special case for overlapped row (sans corner)
269 for (int x = xstart; x < bw; x++) {
270 int grain = FUNC(sample_lut)(grain_lut, offsets, 0, 0, 0, 0, x, y);
271 int old = FUNC(sample_lut)(grain_lut, offsets, 0, 0, 0, 1, x, y);
272 grain = round2(old * w[y][0] + grain * w[y][1], 5);
273 grain = av_clip(grain, grain_min, grain_max);
274 add_noise_y(x, y, grain);
275 }
276
277 // Special case for doubly-overlapped corner
278 for (int x = 0; x < xstart; x++) {
279 int grain = FUNC(sample_lut)(grain_lut, offsets, 0, 0, 0, 0, x, y);
280 int top = FUNC(sample_lut)(grain_lut, offsets, 0, 0, 0, 1, x, y);
281 int old = FUNC(sample_lut)(grain_lut, offsets, 0, 0, 1, 1, x, y);
282
283 // Blend the top pixel with the top left block
284 top = round2(old * w[x][0] + top * w[x][1], 5);
285 top = av_clip(top, grain_min, grain_max);
286
287 // Blend the current pixel with the left block
288 old = FUNC(sample_lut)(grain_lut, offsets, 0, 0, 1, 0, x, y);
289 grain = round2(old * w[x][0] + grain * w[x][1], 5);
290 grain = av_clip(grain, grain_min, grain_max);
291
292 // Mix the row rows together and apply grain
293 grain = round2(top * w[y][0] + grain * w[y][1], 5);
294 grain = av_clip(grain, grain_min, grain_max);
295 add_noise_y(x, y, grain);
296 }
297 }
298 }
299 }
300
301 static void
302 FUNC(fguv_32x32xn_c)(pixel *const dst_row, const pixel *const src_row,
303 const ptrdiff_t stride, const AVFilmGrainParams *const params,
304 const size_t pw, const uint8_t scaling[SCALING_SIZE],
305 const entry grain_lut[][GRAIN_WIDTH], const int bh,
306 const int row_num, const pixel *const luma_row,
307 const ptrdiff_t luma_stride, const int uv, const int is_id,
308 const int sx, const int sy HBD_DECL)
309 {
310 const AVFilmGrainAOMParams *const data = &params->codec.aom;
311 const int rows = 1 + (data->overlap_flag && row_num > 0);
312 const int bitdepth_min_8 = bitdepth - 8;
313 const int grain_ctr = 128 << bitdepth_min_8;
314 const int grain_min = -grain_ctr, grain_max = grain_ctr - 1;
315 unsigned seed[2];
316 int offsets[2 /* col offset */][2 /* row offset */];
317
318 int min_value, max_value;
319 if (data->limit_output_range) {
320 min_value = 16 << bitdepth_min_8;
321 max_value = (is_id ? 235 : 240) << bitdepth_min_8;
322 } else {
323 min_value = 0;
324 max_value = bitdepth_max;
325 }
326
327 // seed[0] contains the current row, seed[1] contains the previous
328 for (int i = 0; i < rows; i++) {
329 seed[i] = params->seed;
330 seed[i] ^= (((row_num - i) * 37 + 178) & 0xFF) << 8;
331 seed[i] ^= (((row_num - i) * 173 + 105) & 0xFF);
332 }
333
334 av_assert1(stride % (FG_BLOCK_SIZE * sizeof(pixel)) == 0);
335
336 // process this row in FG_BLOCK_SIZE^2 blocks (subsampled)
337 for (unsigned bx = 0; bx < pw; bx += FG_BLOCK_SIZE >> sx) {
338 const int bw = FFMIN(FG_BLOCK_SIZE >> sx, (int)(pw - bx));
339 int val, lx, ly, noise;
340 const pixel *src, *luma;
341 pixel *dst, avg;
342
343 // x/y block offsets to compensate for overlapped regions
344 const int ystart = data->overlap_flag && row_num ? FFMIN(2 >> sy, bh) : 0;
345 const int xstart = data->overlap_flag && bx ? FFMIN(2 >> sx, bw) : 0;
346
347 static const int w[2 /* sub */][2 /* off */][2] = {
348 { { 27, 17 }, { 17, 27 } },
349 { { 23, 22 } },
350 };
351
352 if (data->overlap_flag && bx) {
353 // shift previous offsets left
354 for (int i = 0; i < rows; i++)
355 offsets[1][i] = offsets[0][i];
356 }
357
358 // update current offsets
359 for (int i = 0; i < rows; i++)
360 offsets[0][i] = get_random_number(8, &seed[i]);
361
362 #define add_noise_uv(x, y, grain) \
363 lx = (bx + x) << sx; \
364 ly = y << sy; \
365 luma = (const pixel*)((const char*)luma_row + ly * luma_stride) + lx;\
366 avg = luma[0]; \
367 if (sx) \
368 avg = (avg + luma[1] + 1) >> 1; \
369 src = (const pixel*)((const char *)src_row + (y) * stride) + bx + (x);\
370 dst = (pixel *) ((char *) dst_row + (y) * stride) + bx + (x); \
371 val = avg; \
372 if (!data->chroma_scaling_from_luma) { \
373 const int combined = avg * data->uv_mult_luma[uv] + \
374 *src * data->uv_mult[uv]; \
375 val = av_clip( (combined >> 6) + \
376 (data->uv_offset[uv] * (1 << bitdepth_min_8)), \
377 0, bitdepth_max ); \
378 } \
379 noise = round2(scaling[ val ] * (grain), data->scaling_shift); \
380 *dst = av_clip(*src + noise, min_value, max_value);
381
382 for (int y = ystart; y < bh; y++) {
383 // Non-overlapped image region (straightforward)
384 for (int x = xstart; x < bw; x++) {
385 int grain = FUNC(sample_lut)(grain_lut, offsets, sx, sy, 0, 0, x, y);
386 add_noise_uv(x, y, grain);
387 }
388
389 // Special case for overlapped column
390 for (int x = 0; x < xstart; x++) {
391 int grain = FUNC(sample_lut)(grain_lut, offsets, sx, sy, 0, 0, x, y);
392 int old = FUNC(sample_lut)(grain_lut, offsets, sx, sy, 1, 0, x, y);
393 grain = round2(old * w[sx][x][0] + grain * w[sx][x][1], 5);
394 grain = av_clip(grain, grain_min, grain_max);
395 add_noise_uv(x, y, grain);
396 }
397 }
398
399 for (int y = 0; y < ystart; y++) {
400 // Special case for overlapped row (sans corner)
401 for (int x = xstart; x < bw; x++) {
402 int grain = FUNC(sample_lut)(grain_lut, offsets, sx, sy, 0, 0, x, y);
403 int old = FUNC(sample_lut)(grain_lut, offsets, sx, sy, 0, 1, x, y);
404 grain = round2(old * w[sy][y][0] + grain * w[sy][y][1], 5);
405 grain = av_clip(grain, grain_min, grain_max);
406 add_noise_uv(x, y, grain);
407 }
408
409 // Special case for doubly-overlapped corner
410 for (int x = 0; x < xstart; x++) {
411 int top = FUNC(sample_lut)(grain_lut, offsets, sx, sy, 0, 1, x, y);
412 int old = FUNC(sample_lut)(grain_lut, offsets, sx, sy, 1, 1, x, y);
413 int grain = FUNC(sample_lut)(grain_lut, offsets, sx, sy, 0, 0, x, y);
414
415 // Blend the top pixel with the top left block
416 top = round2(old * w[sx][x][0] + top * w[sx][x][1], 5);
417 top = av_clip(top, grain_min, grain_max);
418
419 // Blend the current pixel with the left block
420 old = FUNC(sample_lut)(grain_lut, offsets, sx, sy, 1, 0, x, y);
421 grain = round2(old * w[sx][x][0] + grain * w[sx][x][1], 5);
422 grain = av_clip(grain, grain_min, grain_max);
423
424 // Mix the row rows together and apply to image
425 grain = round2(top * w[sy][y][0] + grain * w[sy][y][1], 5);
426 grain = av_clip(grain, grain_min, grain_max);
427 add_noise_uv(x, y, grain);
428 }
429 }
430 }
431 }
432
433 static void FUNC(generate_scaling)(const uint8_t points[][2], const int num,
434 uint8_t scaling[SCALING_SIZE] HBD_DECL)
435 {
436 const int shift_x = bitdepth - 8;
437 const int scaling_size = 1 << bitdepth;
438 const int max_value = points[num - 1][0] << shift_x;
439 av_assert0(scaling_size <= SCALING_SIZE);
440
441 if (num == 0) {
442 memset(scaling, 0, scaling_size);
443 return;
444 }
445
446 // Fill up the preceding entries with the initial value
447 memset(scaling, points[0][1], points[0][0] << shift_x);
448
449 // Linearly interpolate the values in the middle
450 for (int i = 0; i < num - 1; i++) {
451 const int bx = points[i][0];
452 const int by = points[i][1];
453 const int ex = points[i+1][0];
454 const int ey = points[i+1][1];
455 const int dx = ex - bx;
456 const int dy = ey - by;
457 const int delta = dy * ((0x10000 + (dx >> 1)) / dx);
458 av_assert1(dx > 0);
459 for (int x = 0, d = 0x8000; x < dx; x++) {
460 scaling[(bx + x) << shift_x] = by + (d >> 16);
461 d += delta;
462 }
463 }
464
465 // Fill up the remaining entries with the final value
466 memset(&scaling[max_value], points[num - 1][1], scaling_size - max_value);
467
468 #if BIT_DEPTH != 8
469 for (int i = 0; i < num - 1; i++) {
470 const int pad = 1 << shift_x, rnd = pad >> 1;
471 const int bx = points[i][0] << shift_x;
472 const int ex = points[i+1][0] << shift_x;
473 const int dx = ex - bx;
474 for (int x = 0; x < dx; x += pad) {
475 const int range = scaling[bx + x + pad] - scaling[bx + x];
476 for (int n = 1, r = rnd; n < pad; n++) {
477 r += range;
478 scaling[bx + x + n] = scaling[bx + x] + (r >> shift_x);
479 }
480 }
481 }
482 #endif
483 }
484
485 static av_always_inline void
486 FUNC(apply_grain_row)(AVFrame *out, const AVFrame *in,
487 const int ss_x, const int ss_y,
488 const uint8_t scaling[3][SCALING_SIZE],
489 const entry grain_lut[3][GRAIN_HEIGHT+1][GRAIN_WIDTH],
490 const AVFilmGrainParams *params,
491 const int row HBD_DECL)
492 {
493 // Synthesize grain for the affected planes
494 const AVFilmGrainAOMParams *const data = &params->codec.aom;
495 const int cpw = (out->width + ss_x) >> ss_x;
496 const int is_id = out->colorspace == AVCOL_SPC_RGB;
497 const int bh = (FFMIN(out->height - row * FG_BLOCK_SIZE, FG_BLOCK_SIZE) + ss_y) >> ss_y;
498 const ptrdiff_t uv_off = row * FG_BLOCK_SIZE * out->linesize[1] >> ss_y;
499 pixel *const luma_src = (pixel *)
500 ((char *) in->data[0] + row * FG_BLOCK_SIZE * in->linesize[0]);
501
502 if (data->num_y_points) {
503 const int bh = FFMIN(out->height - row * FG_BLOCK_SIZE, FG_BLOCK_SIZE);
504 const ptrdiff_t off = row * FG_BLOCK_SIZE * out->linesize[0];
505 FUNC(fgy_32x32xn_c)((pixel *) ((char *) out->data[0] + off), luma_src,
506 out->linesize[0], params, out->width, scaling[0],
507 grain_lut[0], bh, row HBD_CALL);
508 }
509
510 if (!data->num_uv_points[0] && !data->num_uv_points[1] &&
511 !data->chroma_scaling_from_luma)
512 {
513 return;
514 }
515
516 // extend padding pixels
517 if (out->width & ss_x) {
518 pixel *ptr = luma_src;
519 for (int y = 0; y < bh; y++) {
520 ptr[out->width] = ptr[out->width - 1];
521 ptr = (pixel *) ((char *) ptr + (in->linesize[0] << ss_y));
522 }
523 }
524
525 if (data->chroma_scaling_from_luma) {
526 for (int pl = 0; pl < 2; pl++)
527 FUNC(fguv_32x32xn_c)((pixel *) ((char *) out->data[1 + pl] + uv_off),
528 (const pixel *) ((const char *) in->data[1 + pl] + uv_off),
529 in->linesize[1], params, cpw, scaling[0],
530 grain_lut[1 + pl], bh, row, luma_src,
531 in->linesize[0], pl, is_id, ss_x, ss_y HBD_CALL);
532 } else {
533 for (int pl = 0; pl < 2; pl++) {
534 if (data->num_uv_points[pl]) {
535 FUNC(fguv_32x32xn_c)((pixel *) ((char *) out->data[1 + pl] + uv_off),
536 (const pixel *) ((const char *) in->data[1 + pl] + uv_off),
537 in->linesize[1], params, cpw, scaling[1 + pl],
538 grain_lut[1 + pl], bh, row, luma_src,
539 in->linesize[0], pl, is_id, ss_x, ss_y HBD_CALL);
540 }
541 }
542 }
543 }
544
545 static int FUNC(apply_film_grain)(AVFrame *out_frame, const AVFrame *in_frame,
546 const AVFilmGrainParams *params HBD_DECL)
547 {
548 entry grain_lut[3][GRAIN_HEIGHT + 1][GRAIN_WIDTH];
549 uint8_t scaling[3][SCALING_SIZE];
550
551 const AVFilmGrainAOMParams *const data = &params->codec.aom;
552 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(out_frame->format);
553 const int rows = AV_CEIL_RSHIFT(out_frame->height, 5); /* log2(FG_BLOCK_SIZE) */
554 const int subx = desc->log2_chroma_w, suby = desc->log2_chroma_h;
555
556 // Generate grain LUTs as needed
557 FUNC(generate_grain_y_c)(grain_lut[0], params HBD_CALL);
558 if (data->num_uv_points[0] || data->chroma_scaling_from_luma)
559 FUNC(generate_grain_uv_c)(grain_lut[1], grain_lut[0], params, 0, subx, suby HBD_CALL);
560 if (data->num_uv_points[1] || data->chroma_scaling_from_luma)
561 FUNC(generate_grain_uv_c)(grain_lut[2], grain_lut[0], params, 1, subx, suby HBD_CALL);
562
563 // Generate scaling LUTs as needed
564 if (data->num_y_points || data->chroma_scaling_from_luma)
565 FUNC(generate_scaling)(data->y_points, data->num_y_points, scaling[0] HBD_CALL);
566 if (data->num_uv_points[0])
567 FUNC(generate_scaling)(data->uv_points[0], data->num_uv_points[0], scaling[1] HBD_CALL);
568 if (data->num_uv_points[1])
569 FUNC(generate_scaling)(data->uv_points[1], data->num_uv_points[1], scaling[2] HBD_CALL);
570
571 for (int row = 0; row < rows; row++) {
572 FUNC(apply_grain_row)(out_frame, in_frame, subx, suby, scaling, grain_lut,
573 params, row HBD_CALL);
574 }
575
576 return 0;
577 }
578