| #!/usr/bin/env python3 | |
| ## Copyright (c) 2026, Alliance for Open Media. All rights reserved. | |
| ## | |
| ## This source code is subject to the terms of the BSD 2 Clause License and | |
| ## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License | |
| ## was not distributed with this source code in the LICENSE file, you can | |
| ## obtain it at www.aomedia.org/license/software. If the Alliance for Open | |
| ## Media Patent License 1.0 was not distributed with this source code in the | |
| ## PATENTS file, you can obtain it at www.aomedia.org/license/patent. | |
| ## | |
| # Precompute libaom AV1 wedge / inter-intra blending masks at codegen time | |
| # so they can live in `.rdata` and be COW shared across processes instead | |
| # of being computed at runtime into writable `.data` buffers. | |
| # | |
| # Output is a header fragment included by av1/common/reconinter.c that | |
| # defines: | |
| # - wedge_mask_obl (input to wedge_mask_buf generation) | |
| # - wedge_mask_buf (compound prediction masks, indexed by offset) | |
| # - smooth_interintra_mask_buf | |
| # - wedge_masks (offsets into wedge_mask_buf) | |
| # | |
| # The algorithms in this file mirror init_wedge_master_masks(), | |
| # init_wedge_masks() and init_smooth_interintra_masks() in | |
| # av1/common/reconinter.c. They must stay in sync with that source. If those | |
| # helpers, the master tables, the wedge codebooks, the block-size enum, or | |
| # ii_weights1d ever change upstream, regenerate the .inc by running this | |
| # script and committing the result. | |
| # | |
| # Regenerate: | |
| # python3 source/libaom/tools/gen_wedge_masks_data.py | |
| # --output source/libaom/av1/common/wedge_masks_data.inc | |
| import argparse | |
| import os | |
| import sys | |
| import textwrap | |
| # ----------------------------------------------------------------------------- | |
| # Constants (must match reconinter.h / enums.h). | |
| # ----------------------------------------------------------------------------- | |
| MAX_WEDGE_TYPES = 16 | |
| MAX_WEDGE_SIZE_LOG2 = 5 | |
| MAX_WEDGE_SIZE = 1 << MAX_WEDGE_SIZE_LOG2 # 32 | |
| MAX_WEDGE_SQUARE = MAX_WEDGE_SIZE * MAX_WEDGE_SIZE # 1024 | |
| MASK_MASTER_SIZE = MAX_WEDGE_SIZE << 1 # 64 | |
| MASK_MASTER_STRIDE = MASK_MASTER_SIZE | |
| WEDGE_WEIGHT_BITS = 6 | |
| INTERINTRA_MODES = 4 | |
| MAX_SB_SIZE_LOG2 = 7 | |
| MAX_SB_SIZE = 1 << MAX_SB_SIZE_LOG2 # 128 | |
| # WedgeDirectionType. | |
| WEDGE_HORIZONTAL = 0 | |
| WEDGE_VERTICAL = 1 | |
| WEDGE_OBLIQUE27 = 2 | |
| WEDGE_OBLIQUE63 = 3 | |
| WEDGE_OBLIQUE117 = 4 | |
| WEDGE_OBLIQUE153 = 5 | |
| WEDGE_DIRECTIONS = 6 | |
| # BLOCK_SIZE enum order (must match enums.h). | |
| BLOCK_NAMES = [ | |
| "BLOCK_4X4", "BLOCK_4X8", "BLOCK_8X4", "BLOCK_8X8", | |
| "BLOCK_8X16", "BLOCK_16X8", "BLOCK_16X16", "BLOCK_16X32", | |
| "BLOCK_32X16", "BLOCK_32X32", "BLOCK_32X64", "BLOCK_64X32", | |
| "BLOCK_64X64", "BLOCK_64X128", "BLOCK_128X64", "BLOCK_128X128", | |
| "BLOCK_4X16", "BLOCK_16X4", "BLOCK_8X32", "BLOCK_32X8", | |
| "BLOCK_16X64", "BLOCK_64X16", | |
| ] | |
| BLOCK_SIZES_ALL = len(BLOCK_NAMES) | |
| def _parse_block_dims(name): | |
| # e.g. "BLOCK_8X16" -> (8, 16) | |
| body = name.split("_", 1)[1] | |
| w, h = body.split("X") | |
| return int(w), int(h) | |
| BLOCK_WIDTH = [_parse_block_dims(n)[0] for n in BLOCK_NAMES] | |
| BLOCK_HEIGHT = [_parse_block_dims(n)[1] for n in BLOCK_NAMES] | |
| def _bi(name): | |
| return BLOCK_NAMES.index(name) | |
| # Inter-intra modes (must match enums.h). | |
| II_DC_PRED, II_V_PRED, II_H_PRED, II_SMOOTH_PRED = 0, 1, 2, 3 | |
| # ----------------------------------------------------------------------------- | |
| # Master mask tables (see wedge_master_oblique_odd / _even / _vertical in | |
| # av1/common/reconinter.c). | |
| # ----------------------------------------------------------------------------- | |
| WEDGE_MASTER_OBLIQUE_ODD = [ | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 6, 18, | |
| 37, 53, 60, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
| 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
| ] | |
| WEDGE_MASTER_OBLIQUE_EVEN = [ | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 11, 27, | |
| 46, 58, 62, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
| 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
| ] | |
| WEDGE_MASTER_VERTICAL = [ | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 7, 21, | |
| 43, 57, 62, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
| 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
| ] | |
| # wedge_signflip_lookup (see av1/common/reconinter.c). | |
| WEDGE_SIGNFLIP_LOOKUP = [ | |
| [0]*16, [0]*16, [0]*16, | |
| [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1], # BLOCK_8X8 | |
| [1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1], # BLOCK_8X16 | |
| [1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1], # BLOCK_16X8 | |
| [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1], # BLOCK_16X16 | |
| [1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1], # BLOCK_16X32 | |
| [1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1], # BLOCK_32X16 | |
| [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1], # BLOCK_32X32 | |
| [0]*16, [0]*16, [0]*16, [0]*16, [0]*16, [0]*16, [0]*16, [0]*16, | |
| [1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1], # BLOCK_8X32 | |
| [1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1], # BLOCK_32X8 | |
| [0]*16, [0]*16, | |
| ] | |
| assert len(WEDGE_SIGNFLIP_LOOKUP) == BLOCK_SIZES_ALL | |
| # Wedge codebooks (see wedge_codebook_16_* in av1/common/reconinter.c). | |
| # Each entry is | |
| # (direction, x_offset, y_offset). | |
| WEDGE_CODEBOOK_16_HGTW = [ | |
| (WEDGE_OBLIQUE27, 4, 4), (WEDGE_OBLIQUE63, 4, 4), | |
| (WEDGE_OBLIQUE117, 4, 4), (WEDGE_OBLIQUE153, 4, 4), | |
| (WEDGE_HORIZONTAL, 4, 2), (WEDGE_HORIZONTAL, 4, 4), | |
| (WEDGE_HORIZONTAL, 4, 6), (WEDGE_VERTICAL, 4, 4), | |
| (WEDGE_OBLIQUE27, 4, 2), (WEDGE_OBLIQUE27, 4, 6), | |
| (WEDGE_OBLIQUE153, 4, 2), (WEDGE_OBLIQUE153, 4, 6), | |
| (WEDGE_OBLIQUE63, 2, 4), (WEDGE_OBLIQUE63, 6, 4), | |
| (WEDGE_OBLIQUE117, 2, 4), (WEDGE_OBLIQUE117, 6, 4), | |
| ] | |
| WEDGE_CODEBOOK_16_HLTW = [ | |
| (WEDGE_OBLIQUE27, 4, 4), (WEDGE_OBLIQUE63, 4, 4), | |
| (WEDGE_OBLIQUE117, 4, 4), (WEDGE_OBLIQUE153, 4, 4), | |
| (WEDGE_VERTICAL, 2, 4), (WEDGE_VERTICAL, 4, 4), | |
| (WEDGE_VERTICAL, 6, 4), (WEDGE_HORIZONTAL, 4, 4), | |
| (WEDGE_OBLIQUE27, 4, 2), (WEDGE_OBLIQUE27, 4, 6), | |
| (WEDGE_OBLIQUE153, 4, 2), (WEDGE_OBLIQUE153, 4, 6), | |
| (WEDGE_OBLIQUE63, 2, 4), (WEDGE_OBLIQUE63, 6, 4), | |
| (WEDGE_OBLIQUE117, 2, 4), (WEDGE_OBLIQUE117, 6, 4), | |
| ] | |
| WEDGE_CODEBOOK_16_HEQW = [ | |
| (WEDGE_OBLIQUE27, 4, 4), (WEDGE_OBLIQUE63, 4, 4), | |
| (WEDGE_OBLIQUE117, 4, 4), (WEDGE_OBLIQUE153, 4, 4), | |
| (WEDGE_HORIZONTAL, 4, 2), (WEDGE_HORIZONTAL, 4, 6), | |
| (WEDGE_VERTICAL, 2, 4), (WEDGE_VERTICAL, 6, 4), | |
| (WEDGE_OBLIQUE27, 4, 2), (WEDGE_OBLIQUE27, 4, 6), | |
| (WEDGE_OBLIQUE153, 4, 2), (WEDGE_OBLIQUE153, 4, 6), | |
| (WEDGE_OBLIQUE63, 2, 4), (WEDGE_OBLIQUE63, 6, 4), | |
| (WEDGE_OBLIQUE117, 2, 4), (WEDGE_OBLIQUE117, 6, 4), | |
| ] | |
| # av1_wedge_params_lookup. Stored here as (wtypes, codebook). The masks/ | |
| # signflip pointers are reconstructed from BLOCK index. See | |
| # av1_wedge_params_lookup in av1/common/reconinter.c. | |
| WEDGE_PARAMS_LOOKUP = [ | |
| (0, None), | |
| (0, None), | |
| (0, None), | |
| (MAX_WEDGE_TYPES, WEDGE_CODEBOOK_16_HEQW), # BLOCK_8X8 | |
| (MAX_WEDGE_TYPES, WEDGE_CODEBOOK_16_HGTW), # BLOCK_8X16 | |
| (MAX_WEDGE_TYPES, WEDGE_CODEBOOK_16_HLTW), # BLOCK_16X8 | |
| (MAX_WEDGE_TYPES, WEDGE_CODEBOOK_16_HEQW), # BLOCK_16X16 | |
| (MAX_WEDGE_TYPES, WEDGE_CODEBOOK_16_HGTW), # BLOCK_16X32 | |
| (MAX_WEDGE_TYPES, WEDGE_CODEBOOK_16_HLTW), # BLOCK_32X16 | |
| (MAX_WEDGE_TYPES, WEDGE_CODEBOOK_16_HEQW), # BLOCK_32X32 | |
| (0, None), (0, None), (0, None), (0, None), | |
| (0, None), (0, None), (0, None), (0, None), | |
| (MAX_WEDGE_TYPES, WEDGE_CODEBOOK_16_HGTW), # BLOCK_8X32 | |
| (MAX_WEDGE_TYPES, WEDGE_CODEBOOK_16_HLTW), # BLOCK_32X8 | |
| (0, None), (0, None), | |
| ] | |
| assert len(WEDGE_PARAMS_LOOKUP) == BLOCK_SIZES_ALL | |
| # ii_weights1d (see av1/common/reconinter.c). | |
| II_WEIGHTS_1D = [ | |
| 60, 58, 56, 54, 52, 50, 48, 47, 45, 44, 42, 41, 39, 38, 37, 35, 34, 33, 32, | |
| 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 22, 21, 20, 19, 19, 18, 18, 17, 16, | |
| 16, 15, 15, 14, 14, 13, 13, 12, 12, 12, 11, 11, 10, 10, 10, 9, 9, 9, 8, | |
| 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, | |
| 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, | |
| 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, | |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
| ] | |
| assert len(II_WEIGHTS_1D) == MAX_SB_SIZE | |
| # ii_size_scales (see av1/common/reconinter.c). | |
| II_SIZE_SCALES = [ | |
| 32, 16, 16, 16, 8, 8, 8, 4, | |
| 4, 4, 2, 2, 2, 1, 1, 1, | |
| 8, 8, 4, 4, 2, 2, | |
| ] | |
| assert len(II_SIZE_SCALES) == BLOCK_SIZES_ALL | |
| # ----------------------------------------------------------------------------- | |
| # Algorithm: build wedge_mask_obl (init_wedge_master_masks in reconinter.c). | |
| # ----------------------------------------------------------------------------- | |
| def shift_copy(src, dst, dst_off, shift, width): | |
| """Mirrors shift_copy() in reconinter.c.""" | |
| if shift >= 0: | |
| for i in range(width - shift): | |
| dst[dst_off + shift + i] = src[i] | |
| for i in range(shift): | |
| dst[dst_off + i] = src[0] | |
| else: | |
| n = -shift | |
| for i in range(width - n): | |
| dst[dst_off + i] = src[n + i] | |
| for i in range(n): | |
| dst[dst_off + width - n + i] = src[width - 1] | |
| def build_wedge_mask_obl(): | |
| # [negative][direction] indexed flat per direction, MASK_MASTER_SIZE^2 bytes. | |
| obl = [[[0] * (MASK_MASTER_SIZE * MASK_MASTER_SIZE) | |
| for _ in range(WEDGE_DIRECTIONS)] | |
| for _ in range(2)] | |
| w = MASK_MASTER_SIZE | |
| h = MASK_MASTER_SIZE | |
| stride = MASK_MASTER_STRIDE | |
| shift = h // 4 | |
| i = 0 | |
| while i < h: | |
| shift_copy(WEDGE_MASTER_OBLIQUE_EVEN, | |
| obl[0][WEDGE_OBLIQUE63], i * stride, shift, MASK_MASTER_SIZE) | |
| shift -= 1 | |
| shift_copy(WEDGE_MASTER_OBLIQUE_ODD, | |
| obl[0][WEDGE_OBLIQUE63], (i + 1) * stride, shift, | |
| MASK_MASTER_SIZE) | |
| # Vertical rows are just the vertical master copied each row. | |
| for k in range(MASK_MASTER_SIZE): | |
| obl[0][WEDGE_VERTICAL][i * stride + k] = WEDGE_MASTER_VERTICAL[k] | |
| obl[0][WEDGE_VERTICAL][(i + 1) * stride + k] = ( | |
| WEDGE_MASTER_VERTICAL[k]) | |
| i += 2 | |
| max_val = 1 << WEDGE_WEIGHT_BITS # 64 | |
| for i in range(h): | |
| for j in range(w): | |
| msk = obl[0][WEDGE_OBLIQUE63][i * stride + j] | |
| obl[0][WEDGE_OBLIQUE27][j * stride + i] = msk | |
| obl[0][WEDGE_OBLIQUE117][i * stride + w - 1 - j] = max_val - msk | |
| obl[0][WEDGE_OBLIQUE153][(w - 1 - j) * stride + i] = max_val - msk | |
| obl[1][WEDGE_OBLIQUE63][i * stride + j] = max_val - msk | |
| obl[1][WEDGE_OBLIQUE27][j * stride + i] = max_val - msk | |
| obl[1][WEDGE_OBLIQUE117][i * stride + w - 1 - j] = msk | |
| obl[1][WEDGE_OBLIQUE153][(w - 1 - j) * stride + i] = msk | |
| mskx = obl[0][WEDGE_VERTICAL][i * stride + j] | |
| obl[0][WEDGE_HORIZONTAL][j * stride + i] = mskx | |
| obl[1][WEDGE_VERTICAL][i * stride + j] = max_val - mskx | |
| obl[1][WEDGE_HORIZONTAL][j * stride + i] = max_val - mskx | |
| return obl | |
| # ----------------------------------------------------------------------------- | |
| # Algorithm: build wedge_mask_buf and wedge_masks offset table | |
| # (init_wedge_masks + get_wedge_mask_inplace + aom_convolve_copy). | |
| # ----------------------------------------------------------------------------- | |
| def get_wedge_mask_inplace_offset(wedge_obl, w_idx, neg, bsize): | |
| """Compute the starting (row, col) into wedge_mask_obl[neg^wsignflip][dir] | |
| for a given wedge index/sign/block-size; mirrors get_wedge_mask_inplace(). | |
| Returns (which_neg, direction, row_off, col_off).""" | |
| bw = BLOCK_WIDTH[bsize] | |
| bh = BLOCK_HEIGHT[bsize] | |
| wtypes, codebook = WEDGE_PARAMS_LOOKUP[bsize] | |
| assert wtypes > 0 | |
| direction, x_off, y_off = codebook[w_idx] | |
| woff = (x_off * bw) >> 3 | |
| hoff = (y_off * bh) >> 3 | |
| wsignflip = WEDGE_SIGNFLIP_LOOKUP[bsize][w_idx] | |
| which = neg ^ wsignflip | |
| row_off = MASK_MASTER_SIZE // 2 - hoff | |
| col_off = MASK_MASTER_SIZE // 2 - woff | |
| return which, direction, row_off, col_off | |
| def build_wedge_mask_buf(obl): | |
| buf_size = 2 * MAX_WEDGE_TYPES * 4 * MAX_WEDGE_SQUARE | |
| buf = [0] * buf_size | |
| # Offset table: wedge_masks[bsize][sign][wedge_index] -> uint32 offset. | |
| offsets = [[[0] * MAX_WEDGE_TYPES for _ in range(2)] | |
| for _ in range(BLOCK_SIZES_ALL)] | |
| dst = 0 | |
| for bsize in range(BLOCK_SIZES_ALL): | |
| wtypes, _ = WEDGE_PARAMS_LOOKUP[bsize] | |
| if wtypes == 0: | |
| continue | |
| bw = BLOCK_WIDTH[bsize] | |
| bh = BLOCK_HEIGHT[bsize] | |
| for w_idx in range(wtypes): | |
| # sign 0 | |
| which, direction, row_off, col_off = ( | |
| get_wedge_mask_inplace_offset(obl, w_idx, 0, bsize)) | |
| src = obl[which][direction] | |
| for r in range(bh): | |
| for c in range(bw): | |
| buf[dst + r * bw + c] = src[ | |
| (row_off + r) * MASK_MASTER_STRIDE + (col_off + c)] | |
| offsets[bsize][0][w_idx] = dst | |
| dst += bw * bh | |
| # sign 1 | |
| which, direction, row_off, col_off = ( | |
| get_wedge_mask_inplace_offset(obl, w_idx, 1, bsize)) | |
| src = obl[which][direction] | |
| for r in range(bh): | |
| for c in range(bw): | |
| buf[dst + r * bw + c] = src[ | |
| (row_off + r) * MASK_MASTER_STRIDE + (col_off + c)] | |
| offsets[bsize][1][w_idx] = dst | |
| dst += bw * bh | |
| assert dst <= buf_size, (dst, buf_size) | |
| return buf, offsets | |
| # ----------------------------------------------------------------------------- | |
| # Algorithm: build smooth_interintra_mask_buf | |
| # (init_smooth_interintra_masks + build_smooth_interintra_mask). | |
| # ----------------------------------------------------------------------------- | |
| def build_smooth_interintra(mask, mask_off, stride, plane_bsize, mode): | |
| bw = BLOCK_WIDTH[plane_bsize] | |
| bh = BLOCK_HEIGHT[plane_bsize] | |
| size_scale = II_SIZE_SCALES[plane_bsize] | |
| if mode == II_V_PRED: | |
| for i in range(bh): | |
| row = mask_off + i * stride | |
| val = II_WEIGHTS_1D[i * size_scale] | |
| for j in range(bw): | |
| mask[row + j] = val | |
| elif mode == II_H_PRED: | |
| for i in range(bh): | |
| row = mask_off + i * stride | |
| for j in range(bw): | |
| mask[row + j] = II_WEIGHTS_1D[j * size_scale] | |
| elif mode == II_SMOOTH_PRED: | |
| for i in range(bh): | |
| row = mask_off + i * stride | |
| for j in range(bw): | |
| mask[row + j] = II_WEIGHTS_1D[min(i, j) * size_scale] | |
| else: # II_DC_PRED or default | |
| for i in range(bh): | |
| row = mask_off + i * stride | |
| for j in range(bw): | |
| mask[row + j] = 32 | |
| def build_smooth_interintra_mask_buf(): | |
| # [INTERINTRA_MODES][BLOCK_SIZES_ALL][MAX_WEDGE_SQUARE] | |
| buf = [[[0] * MAX_WEDGE_SQUARE for _ in range(BLOCK_SIZES_ALL)] | |
| for _ in range(INTERINTRA_MODES)] | |
| for m in range(INTERINTRA_MODES): | |
| for bs in range(BLOCK_SIZES_ALL): | |
| bw = BLOCK_WIDTH[bs] | |
| bh = BLOCK_HEIGHT[bs] | |
| if bw > MAX_WEDGE_SIZE or bh > MAX_WEDGE_SIZE: | |
| continue | |
| build_smooth_interintra(buf[m][bs], 0, bw, bs, m) | |
| return buf | |
| # ----------------------------------------------------------------------------- | |
| # C emission helpers. | |
| # ----------------------------------------------------------------------------- | |
| def emit_bytes_block(values, indent=" ", per_line=16): | |
| lines = [] | |
| for chunk_start in range(0, len(values), per_line): | |
| chunk = values[chunk_start:chunk_start + per_line] | |
| parts = ["{:>3d}".format(v) for v in chunk] | |
| lines.append(indent + ", ".join(parts) + ",") | |
| return "\n".join(lines) | |
| def emit_wedge_mask_obl(out, obl): | |
| out.append( | |
| "DECLARE_ALIGNED(16, static const uint8_t,") | |
| out.append( | |
| " wedge_mask_obl[2][WEDGE_DIRECTIONS]") | |
| out.append( | |
| " [MASK_MASTER_SIZE * MASK_MASTER_SIZE]) = {") | |
| for neg in range(2): | |
| out.append(" {") | |
| for direction in range(WEDGE_DIRECTIONS): | |
| out.append(" {{ // [{}][dir={}]".format(neg, direction)) | |
| out.append(emit_bytes_block(obl[neg][direction], indent=" ")) | |
| out.append(" },") | |
| out.append(" },") | |
| out.append("};") | |
| def emit_wedge_mask_buf(out, buf): | |
| out.append( | |
| "DECLARE_ALIGNED(16, static const uint8_t,") | |
| out.append( | |
| " wedge_mask_buf[2 * MAX_WEDGE_TYPES * 4 *" | |
| " MAX_WEDGE_SQUARE]) = {") | |
| out.append(emit_bytes_block(buf, indent=" ")) | |
| out.append("};") | |
| def emit_smooth_interintra(out, buf): | |
| out.append( | |
| "DECLARE_ALIGNED(16, static const uint8_t,") | |
| out.append( | |
| " smooth_interintra_mask_buf[INTERINTRA_MODES]") | |
| out.append( | |
| " [BLOCK_SIZES_ALL]") | |
| out.append( | |
| " [MAX_WEDGE_SQUARE]) = {") | |
| for m in range(INTERINTRA_MODES): | |
| out.append(" {") | |
| for bs in range(BLOCK_SIZES_ALL): | |
| out.append(" {{ // mode={}, bsize={}".format(m, BLOCK_NAMES[bs])) | |
| out.append(emit_bytes_block(buf[m][bs], indent=" ")) | |
| out.append(" },") | |
| out.append(" },") | |
| out.append("};") | |
| def emit_wedge_offsets(out, offsets): | |
| out.append( | |
| "static const wedge_masks_type wedge_masks[BLOCK_SIZES_ALL][2] = {") | |
| for bs in range(BLOCK_SIZES_ALL): | |
| out.append(" { // " + BLOCK_NAMES[bs]) | |
| for sign in range(2): | |
| row = offsets[bs][sign] | |
| parts = ["{:>6d}u".format(v) for v in row] | |
| out.append(" { " + ", ".join(parts) + " },") | |
| out.append(" },") | |
| out.append("};") | |
| HEADER_BANNER = """\ | |
| /* | |
| * Copyright (c) 2026, Alliance for Open Media. All rights reserved. | |
| * | |
| * This source code is subject to the terms of the BSD 2 Clause License and | |
| * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License | |
| * was not distributed with this source code in the LICENSE file, you can | |
| * obtain it at www.aomedia.org/license/software. If the Alliance for Open | |
| * Media Patent License 1.0 was not distributed with this source code in the | |
| * PATENTS file, you can obtain it at www.aomedia.org/license/patent. | |
| */ | |
| // This file is auto-generated by tools/gen_wedge_masks_data.py. DO NOT EDIT. | |
| // | |
| // It precomputes the libaom AV1 compound-prediction wedge masks and | |
| // inter-intra smooth blending masks so they can live in `.rdata` and be | |
| // COW-shared across processes, instead of being computed at runtime into | |
| // writable `.data` buffers via av1_init_wedge_masks(). | |
| // | |
| // `wedge_masks` stores byte offsets into `wedge_mask_buf` (not pointers) so | |
| // no loader relocations are needed. | |
| // | |
| // To regenerate after upstream changes to reconinter.c (wedge codebooks, | |
| // master tables, BLOCK_SIZE enum, ii_weights1d, etc.) run: | |
| // python3 source/libaom/tools/gen_wedge_masks_data.py | |
| // --output source/libaom/av1/common/wedge_masks_data.inc | |
| """ | |
| def write_output(path, obl, buf, smooth, offsets): | |
| out = [] | |
| out.append(HEADER_BANNER) | |
| # Disable clang-format on the generated data tables. The emitters below | |
| # produce a deliberately column-aligned, 16-values-per-line layout so the | |
| # ~14k lines of mask data are visually scannable (you can eyeball the | |
| # wedge patterns and offset progressions). clang-format would otherwise | |
| # reflow each table to fit the project column limit, jumbling rows | |
| # together, destroying the alignment, and producing a several-thousand- | |
| # line diff every time anyone re-ran it. | |
| out.append("// The data tables below are emitted by") | |
| out.append("// tools/gen_wedge_masks_data.py with a fixed 16-values-per-row") | |
| out.append("// layout for visual readability. clang-format would reflow") | |
| out.append("// them to fit the project column limit, destroying the") | |
| out.append("// alignment and producing a many-thousand-line diff. Keep the") | |
| out.append("// formatter off until the closing `// clang-format on` below.") | |
| out.append("// clang-format off") | |
| out.append("") | |
| # `wedge_mask_obl` is intentionally not emitted: it was only used at | |
| # runtime as an intermediate to populate `wedge_mask_buf`, which is now | |
| # precomputed below. Dropping it saves ~48 KB of .rdata. | |
| _ = obl # retained for verification harnesses; unused here. | |
| emit_wedge_mask_buf(out, buf) | |
| out.append("") | |
| emit_smooth_interintra(out, smooth) | |
| out.append("") | |
| emit_wedge_offsets(out, offsets) | |
| out.append("") | |
| out.append("// clang-format on") | |
| out.append("") | |
| text = "\n".join(out) | |
| with open(path, "w", encoding="utf-8", newline="\n") as f: | |
| f.write(text) | |
| def main(): | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| default_out = os.path.normpath( | |
| os.path.join(os.path.dirname(__file__), "..", "av1", "common", | |
| "wedge_masks_data.inc")) | |
| parser.add_argument("--output", default=default_out, | |
| help="Path to wedge_masks_data.inc (default: %(default)s)") | |
| args = parser.parse_args() | |
| obl = build_wedge_mask_obl() | |
| buf, offsets = build_wedge_mask_buf(obl) | |
| smooth = build_smooth_interintra_mask_buf() | |
| write_output(args.output, obl, buf, smooth, offsets) | |
| print("Wrote {}".format(args.output)) | |
| if __name__ == "__main__": | |
| main() |