From patchwork Sun Nov 12 10:35:13 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 44620 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:92a5:b0:181:818d:5e7f with SMTP id q37csp728039pzg; Sun, 12 Nov 2023 02:36:11 -0800 (PST) X-Google-Smtp-Source: AGHT+IG5x1Kv7z86/C7Mw9G46uBfsHS564Alge38GCQYjTrT9UKkXPmUyGZ0goDUhSOhBzWJLpQ6 X-Received: by 2002:a17:906:13c9:b0:9d0:6108:993a with SMTP id g9-20020a17090613c900b009d06108993amr2929335ejc.36.1699785371100; Sun, 12 Nov 2023 02:36:11 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id v5-20020a1709063bc500b009930d9d6b4csi1584547ejf.888.2023.11.12.02.36.10; Sun, 12 Nov 2023 02:36:11 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@outlook.com header.s=selector1 header.b=s2eZlJO2; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 2714968CACA; Sun, 12 Nov 2023 12:36:07 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from APC01-PSA-obe.outbound.protection.outlook.com (mail-psaapc01olkn2055.outbound.protection.outlook.com [40.92.52.55]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id F000F68C851 for ; Sun, 12 Nov 2023 12:35:59 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=QJzFdIulNpfS3D8+I0KXYOA0RZCqGTAb73KIMO0u5kTFCoI0zu5pP4pF1hMgvgfKDf4E8rmWa0tNiZ2v3qelqwuAU8TTTu6lCPnhTa9GKjQgWiWLQUVWVdKKQSlVco5GaE0VowLymJ4SDZdjcUIXn7oUzezbD8B9LjGjItjcnSoJP0naZ9jtknLDV9W5ASzg3cB+TnSOZjlmtMT4ao5cBK0M09PejU38YmBE4FuLnwqMN8EabFZszLfp+pShp5lYI5XYt/AP/SaG2xv1Uh8mSFgGZHyOPnIO7nPXlRNXZbfGAUO5/yparPOhqU/gdgR9kneLor0WUTif2G7BDmNC6A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=3LcHAW3lMrd3WAMVNE07/1xIg7fLGo9QcM0mBaofbJY=; b=BfUrixgl5Fd2hqoke9RUbawfr8EoWjcQ9+4Xj2IgpaAgjD5uWh4MdIup3B0qJb1bmXj7OBq6KvUdFFtug9d83xRl+hMO3m1hp3pmyJUwqKoOoWb2VLo/+p2kCHZJdMh8MHZdzqjjUwSh/z8uXC2yK+oxOj1aHgn6GZj2sZcQrC8+dnltV9wZtf4DapW/n81lAB4cTmW3qTFHtn1+z3FLTcKtq9eXZFrXcKk6a7jZlAy5tgAm/BGiErGzXgvEFD0epgi8Ag0Fw2ZC3aKaTOWq5byRXsdCM0V4GrR7uVB7FvVFkLQgRCiWAQuUcqwhr2czgJo8+eU9RZCkSdmUFXLboA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=outlook.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=3LcHAW3lMrd3WAMVNE07/1xIg7fLGo9QcM0mBaofbJY=; b=s2eZlJO2sKiZ26Rv3PTFVcgkJVAaCwBG2hi7qEMU1pNM+SYP8w+2JRKueeAg8UgZk3RC0+aBiz2sXzpmqfdSe/3vLwXrgDnZTNTi99yzWrQE+WoB5h0wqqllmGbeQDcVEAVVKiqpE72DaIbQF/tpGN3yzRHZfybJFIK1W4sg/EmNcNTaxWS021rIfrn5PsBCPrKerSvODrhRUSOXb7XkPX9DUgKyUnDsp3wZhfSHqykKN+n3Xa5tgEUmbK5HstITc2tEk5UKkyqY5Bmo9et5Tet3sqrhvapjMzSyJsyliZpHMXnoBTtzxboCxox+HJF8Mm8Q/IHoFEArl3HElUoOeg== Received: from TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) by TYSPR06MB6768.apcprd06.prod.outlook.com (2603:1096:400:475::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6954.29; Sun, 12 Nov 2023 10:35:54 +0000 Received: from TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f]) by TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f%4]) with mapi id 15.20.6954.028; Sun, 12 Nov 2023 10:35:54 +0000 From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Nov 2023 18:35:13 +0800 Message-ID: X-Mailer: git-send-email 2.25.1 X-TMN: [sFHyHZyWcwEQRk3Do6ZAICaPGfe2vcAY] X-ClientProxiedBy: TYWP286CA0030.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:262::20) To TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) X-Microsoft-Original-Message-ID: <20231112103526.11245-1-nuomi2021@gmail.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 2 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: TYSPR06MB6433:EE_|TYSPR06MB6768:EE_ X-MS-Office365-Filtering-Correlation-Id: b54f1516-d812-46a7-c135-08dbe36b25ac X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: ZywDRHzb+SnNa7hEvh2EX6/FOWV86Jn30vOQ2acZDqGX6FNbW8Qt2Dm3hFAz3J4lId86GL50P0HnzbeYNUm6l8W2cl5uYJVGwuey7Bn0DfmfPDpIhozSA9ZWNJSQvwsxyD2R9UoIkiATC5YgVtLeFlB8TesqrHTS7IoUNHhoNTeQ0SOd3rO87XOZMNoqZoJ8qH4mR0czLBrPpwgn0gni1WiRsM3CXiKsMDGQ4dVAoZ7S9m68M+Q6okdGnswfozCS2rz6U12Y2pU8+VN/G5SZPSAB7wbHyQQw5Rm0NaATsYGmsY+32Nea43pw56HgkgN2uClaJS/6Ubl0yMdcbyCmdeptfr3iiBgfKxVY9OFd0fJvqGEzQ/ojEGWhfw+v6/WDvCIYlt4QxWcPTARcJQd1DbkSqpWRNjZ9T2XLA0hOknzGEQWO+CjjP9lGAawsxa7uLNKXvUUz0ALbieHnQZgpiDXBEgxlP15via1ojdg5nV/eTAmE2ORSXaWRZi1x2mNkdp8ccmZKN+Y/p8yP00L6yl0VrkKvlWolG9GlXhesoqR7wJOVGE6B0qWEBTyFbd2FzVs8j+ItUJv7jp8JBrBqhKa1U4LvOk2tkefjRy7Hue0wczJlaUvI7rErtC7AGa9n X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: w4fiVvGT/J5BxjCWfBU+x2GWkR4pHiIkhW9XWdCgwAHBLjOwhFC0PAmQOzKyMG1NbpBx8Jqx4GIVr7RFB1j7zFHdlobfhdkvXP/iFDuxj/YvgQT0ycZ1n53rgdOfz3TwqXCvGlxLcgnIg0w7vesH1AQM1/a4oVQNmz/unitKetLHJd6GAJMLKv49KzMz8y+pYWyWn3bmxx8px7CrA9dFj6n4VEOdg8W43naA4Yl+fi4pfaUa7HPGfR1rk3SDRoZRJnk+WDou94mhmxesZ4llckPd6iRK7AKHgS7ptBhVnZRy5ghjEuZHe/wWi6oX5N39awlVlkchFnwORkvX2lCjUlgpqRw83zYuvM8J2SFo7fF2jVcXNrLfZSGlfrAWCkqQAWs64wVcBDzrDdcxxgALCBeW8SmTPjqVbQm9lmDEzpQK/pf7xny8ryRLqaRwwH0DrzFvtCUOfA8C9qztw13NPsxoLbd2CEbKRI/ENh3t8ZzflzeB00X43Hv7ybBGwtAYpWXdjLQJwjqJ1IyT5cgecTBxIaEswDNZLBLduMb+I1LnrrrMdbapr2ozSUMkC3aLUZ4sLH1kiv9Ojgezedx599/ftLELH6PbLUp8h/oQtijaQj3a4eNd5/+pYVGGBb9RRC28/WvG0/G8F2jpWNathYv2PubcAtY87twXAdFSlnvvK5LE70CEzoTkPxt/07/c9hL3mYicsakxQuOr7b9FsVsOUHF3t05Z7sV16n4MJFxv/WGZb82h6iF4qN+YXFnzOcoH5S66AwTQZf2JTvLUFoC7zyym65qWTH65TVcAlDdH5V3tR+cGb/MdlZjIvCd8ZIM1knsvrgQpqGEJE0U4KMXY+VqzCAlZKcs7ZEgO+GbEYqlZglURR6n4mXLsPJ4SMKcRCsVrf1HzB17R1NcqG+indo7YSgWZAEc/s3sMbHP9f4KdBHjofF6hXfzfyc0fdXYA17HJ0EPkwn3pt1JUd3LcDZFWCAVJKxt/fy8fxxZxMQEz+RkqoL2gj8L5Kjuu2MZDVsA+/VaMZk1dwwQMD2WnaibfNNnAHEweMOW5+/ENOGPaDTtJ4lbp8iXteZRvRsZvg73VlirK71Xj0i1lxgpWJjrQK3BAVovaMUNowBvPVQJYhBTZJ5GdSSb/GLxh298ogL5cdEADNQn/KHBN+faFk6B9rZMkoqAZkvrbXcvhC0BmwPGlkR1eu4EH4PllWnxBPV1ImnSc/hGBEqbqUDnJzEu7VFLrRlfqs6HlroiHRWUddhrdAKPF1woEX4B3 X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: b54f1516-d812-46a7-c135-08dbe36b25ac X-MS-Exchange-CrossTenant-AuthSource: TYSPR06MB6433.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Nov 2023 10:35:54.0213 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYSPR06MB6768 Subject: [FFmpeg-devel] [PATCH v5 01/14] vvcdec: add vvc decoder stub X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Nuo Mi Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: gjFVS5noMsz8 --- configure | 1 + libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/vvc/Makefile | 4 + libavcodec/vvc/vvcdec.c | 62 ++++++++++++ libavcodec/vvc/vvcdec.h | 204 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 273 insertions(+) create mode 100644 libavcodec/vvc/Makefile create mode 100644 libavcodec/vvc/vvcdec.c create mode 100644 libavcodec/vvc/vvcdec.h diff --git a/configure b/configure index 46d7a5cf0e..770cbb2f8e 100755 --- a/configure +++ b/configure @@ -3013,6 +3013,7 @@ vp6f_decoder_select="vp6_decoder" vp7_decoder_select="h264pred videodsp vp8dsp" vp8_decoder_select="h264pred videodsp vp8dsp" vp9_decoder_select="videodsp vp9_parser vp9_superframe_split_bsf" +vvc_decoder_select="cabac golomb videodsp" wcmv_decoder_select="inflate_wrapper" webp_decoder_select="vp8_decoder exif" wmalossless_decoder_select="llauddsp" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 57d57f3ab5..aba849f408 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -64,6 +64,7 @@ OBJS = ac3_parser.o \ xiph.o \ # subsystems +include $(SRC_PATH)/libavcodec/vvc/Makefile OBJS-$(CONFIG_AANDCTTABLES) += aandcttab.o OBJS-$(CONFIG_AC3DSP) += ac3dsp.o ac3.o ac3tab.o OBJS-$(CONFIG_ADTS_HEADER) += adts_header.o mpeg4audio_sample_rates.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 2662adb754..6c5aa6b44b 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -389,6 +389,7 @@ extern const FFCodec ff_vp9_rkmpp_decoder; extern const FFCodec ff_vp9_v4l2m2m_decoder; extern const FFCodec ff_vqa_decoder; extern const FFCodec ff_vqc_decoder; +extern const FFCodec ff_vvc_decoder; extern const FFCodec ff_wbmp_decoder; extern const FFCodec ff_wbmp_encoder; extern const FFCodec ff_webp_decoder; diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile new file mode 100644 index 0000000000..bd14dbc1df --- /dev/null +++ b/libavcodec/vvc/Makefile @@ -0,0 +1,4 @@ +clean:: + $(RM) $(CLEANSUFFIXES:%=libavcodec/vvc/%) + +OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ diff --git a/libavcodec/vvc/vvcdec.c b/libavcodec/vvc/vvcdec.c new file mode 100644 index 0000000000..3c591ce875 --- /dev/null +++ b/libavcodec/vvc/vvcdec.c @@ -0,0 +1,62 @@ +/* + * VVC video decoder + * + * Copyright (C) 2021 Nuo Mi + * Copyright (C) 2022 Xu Mu + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "libavcodec/codec_internal.h" +#include "libavcodec/profiles.h" + +#include "vvcdec.h" + +static int vvc_decode_frame(AVCodecContext *avctx, AVFrame *output, + int *got_output, AVPacket *avpkt) +{ + return avpkt->size; +} + +static void vvc_decode_flush(AVCodecContext *avctx) +{ +} + +static av_cold int vvc_decode_free(AVCodecContext *avctx) +{ + return 0; +} + +static av_cold int vvc_decode_init(AVCodecContext *avctx) +{ + return 0; +} + +const FFCodec ff_vvc_decoder = { + .p.name = "vvc", + .p.long_name = NULL_IF_CONFIG_SMALL("VVC (Versatile Video Coding)"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VVC, + .priv_data_size = sizeof(VVCContext), + .init = vvc_decode_init, + .close = vvc_decode_free, + FF_CODEC_DECODE_CB(vvc_decode_frame), + .flush = vvc_decode_flush, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS, + .caps_internal = FF_CODEC_CAP_EXPORTS_CROPPING | FF_CODEC_CAP_INIT_CLEANUP | + FF_CODEC_CAP_AUTO_THREADS, + .p.profiles = NULL_IF_CONFIG_SMALL(ff_vvc_profiles), +}; diff --git a/libavcodec/vvc/vvcdec.h b/libavcodec/vvc/vvcdec.h new file mode 100644 index 0000000000..2823faee75 --- /dev/null +++ b/libavcodec/vvc/vvcdec.h @@ -0,0 +1,204 @@ +/* + * VVC video decoder + * + * Copyright (C) 2021 Nuo Mi + * Copyright (C) 2022 Xu Mu + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VVCDEC_H +#define AVCODEC_VVCDEC_H + +#include "libavcodec/vvc.h" + +#define LUMA 0 +#define CHROMA 1 +#define CB 1 +#define CR 2 +#define JCBCR 3 + +#define MIN_TU_LOG2 2 ///< MinTbLog2SizeY +#define MIN_PU_LOG2 2 + +#define L0 0 +#define L1 1 + +typedef struct RefPicList { + struct VVCFrame *ref[VVC_MAX_REF_ENTRIES]; + int list[VVC_MAX_REF_ENTRIES]; + int isLongTerm[VVC_MAX_REF_ENTRIES]; + int nb_refs; +} RefPicList; + +typedef struct RefPicListTab { + RefPicList refPicList[2]; +} RefPicListTab; + +typedef struct VVCFrame { + struct AVFrame *frame; + + struct MvField *tab_dmvr_mvf; ///< RefStruct reference + RefPicListTab **rpl_tab; ///< RefStruct reference + RefPicListTab *rpl; ///< RefStruct reference + int nb_rpl_elems; + + int ctb_count; + + int poc; + + struct VVCFrame *collocated_ref; + + struct FrameProgress *progress; ///< RefStruct reference + + /** + * A sequence counter, so that old frames are output first + * after a POC reset + */ + uint16_t sequence; + /** + * A combination of VVC_FRAME_FLAG_* + */ + uint8_t flags; +} VVCFrame; + +typedef struct SliceContext { + int slice_idx; + struct EntryPoint *eps; + int nb_eps; + RefPicList *rpl; +} SliceContext; + +typedef struct VVCFrameContext { + struct AVCodecContext *avctx; + + // +1 for the current frame + VVCFrame DPB[VVC_MAX_DPB_SIZE + 1]; + + struct AVFrame *frame; + struct AVFrame *output_frame; + + SliceContext **slices; + int nb_slices; + int nb_slices_allocated; + + VVCFrame *ref; + + struct VVCFrameThread *ft; + + uint64_t decode_order; + + struct FFRefStructPool *tab_dmvr_mvf_pool; + struct FFRefStructPool *rpl_tab_pool; + + struct FFRefStructPool *cu_pool; + struct FFRefStructPool *tu_pool; + + struct { + int16_t *slice_idx; + + int *cb_pos_x[2]; ///< CbPosX[][][] + int *cb_pos_y[2]; ///< CbPosY[][][] + uint8_t *cb_width[2]; ///< CbWidth[][][] + uint8_t *cb_height[2]; ///< CbHeight[][][] + uint8_t *cqt_depth[2]; ///< CqtDepth[][][] + int8_t *qp[VVC_MAX_SAMPLE_ARRAYS]; + + uint8_t *skip; ///< CuSkipFlag[][] + uint8_t *ispmf; ///< intra_sub_partitions_mode_flag + uint8_t *msm[2]; ///< MttSplitMode[][][] in 32 pixels + uint8_t *imf; ///< IntraMipFlag[][] + uint8_t *imtf; ///< intra_mip_transposed_flag[][] + uint8_t *imm; ///< intra_mip_mode[][] + uint8_t *ipm; ///< IntraPredModeY[][] + uint8_t *cpm[2]; ///< CuPredMode[][][] + uint8_t *msf; ///< MergeSubblockFlag[][] + uint8_t *iaf; ///< InterAffineFlag[][] + uint8_t *mmi; ///< MotionModelIdc[][] + struct Mv *cp_mv[2]; ///< CpMvLX[][][][MAX_CONTROL_POINTS]; + struct MvField *mvf; ///< MvDmvrL0, MvDmvrL1 + + uint8_t *tu_coded_flag[VVC_MAX_SAMPLE_ARRAYS]; ///< tu_y_coded_flag[][], tu_cb_coded_flag[][], tu_cr_coded_flag[][] + uint8_t *tu_joint_cbcr_residual_flag; ///< tu_joint_cbcr_residual_flag[][] + int *tb_pos_x0[2]; + int *tb_pos_y0[2]; + uint8_t *tb_width[2]; + uint8_t *tb_height[2]; + uint8_t *pcmf[2]; + + uint8_t *horizontal_bs[VVC_MAX_SAMPLE_ARRAYS]; + uint8_t *vertical_bs[VVC_MAX_SAMPLE_ARRAYS]; + uint8_t *horizontal_p; ///< horizontal maxFilterLengthPs for luma + uint8_t *horizontal_q; ///< horizontal maxFilterLengthQs for luma + uint8_t *vertical_p; ///< vertical maxFilterLengthPs for luma + uint8_t *vertical_q; ///< vertical maxFilterLengthQs for luma + + uint8_t *sao_pixel_buffer_h[VVC_MAX_SAMPLE_ARRAYS]; + uint8_t *sao_pixel_buffer_v[VVC_MAX_SAMPLE_ARRAYS]; + uint8_t *alf_pixel_buffer_h[VVC_MAX_SAMPLE_ARRAYS][2]; + uint8_t *alf_pixel_buffer_v[VVC_MAX_SAMPLE_ARRAYS][2]; + + int *coeffs; + struct CTU *ctus; + + //used in arrays_init only + int ctu_count; + int ctu_size; + int pic_size_in_min_cb; + int pic_size_in_min_pu; + int pic_size_in_min_tu; + int ctu_width; + int ctu_height; + int width; + int height; + int chroma_format_idc; + int pixel_shift; + int bs_width; + int bs_height; + } tab; +} VVCFrameContext; + +typedef struct VVCContext { + struct AVCodecContext *avctx; + + int temporal_id; ///< temporal_id_plus1 - 1 + int poc_tid0; + + int eos; ///< current packet contains an EOS/EOB NAL + int last_eos; ///< last packet contains an EOS/EOB NAL + + enum VVCNALUnitType vcl_unit_type; + int no_output_before_recovery_flag; ///< NoOutputBeforeRecoveryFlag + int gdr_recovery_point_poc; ///< recoveryPointPocVal + + /** + * Sequence counters for decoded and output frames, so that old + * frames are output first after a POC reset + */ + uint16_t seq_decode; + uint16_t seq_output; + + struct AVExecutor *executor; + + VVCFrameContext *fcs; + int nb_fcs; + + uint64_t nb_frames; ///< processed frames + int nb_delayed; ///< delayed frames +} VVCContext ; + +#endif /* AVCODEC_VVCDEC_H */ From patchwork Sun Nov 12 10:35:14 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 44624 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:92a5:b0:181:818d:5e7f with SMTP id q37csp728260pzg; Sun, 12 Nov 2023 02:36:58 -0800 (PST) X-Google-Smtp-Source: AGHT+IF5yIf+eskmzZDt9O07eH/4FjZCQXi6TCXX7q1KXO9cOJaY/C+JtTFbmiorKeSzxEeoxYHx X-Received: by 2002:aa7:d50f:0:b0:543:4fca:cc91 with SMTP id y15-20020aa7d50f000000b005434fcacc91mr2667108edq.20.1699785418571; Sun, 12 Nov 2023 02:36:58 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id u20-20020a50a414000000b00545578b47b0si1499867edb.226.2023.11.12.02.36.57; Sun, 12 Nov 2023 02:36:58 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@outlook.com header.s=selector1 header.b=uCmtFBzx; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 1082868CC6D; Sun, 12 Nov 2023 12:36:20 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from APC01-SG2-obe.outbound.protection.outlook.com (mail-sgaapc01olkn2035.outbound.protection.outlook.com [40.92.53.35]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 18CE668CBF5 for ; Sun, 12 Nov 2023 12:36:13 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=cZR8XlgQJZKK86iSZ7SOLV7kfXF6EiUiZPHFcMSB4L/favT6gT3T1ew+j4tYzWS5Z4JOwrPGv9kL4t5N2lKZyYKt5JLGolI5yCso9KZiMbIFciECLI+nOtcp86mPNpme1P/bgsvh/bljj2k8cyvHmW+autOO/fv2sJdFq4zQZy5G8wDjHQN0bhdTV5tM/mcSvfXWTXD87vqdGORZTdNyYAD5hILkDN4FaLzeNLeCUFgMhUuE+RbzTHXEFoG1H2HOP2RTzn8tB4elQumo0otP7w+ndDSASBkciYs+9dPS9vb3JCGG2VJx2PKcfwa+83rl/KbMk9eZsHo3DqTl9Fl8cg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=eHYCP6DGtbs3gHlgpArNyoE43B4DLAw2NIF95f4r9wE=; b=g+cf6M9iS7f9MBrBsgwSODI0aSysTJ3ZNpMChwwTPsSXBccxJrYsAwivImpVLG4Wu0v1rhC54OhxizcZdDQW+KKQiJCUI15+ftUiwSryZfXZueVXJlM5W+L5immbjmjfrDkF/HwweYMfKUM8G8oRKHZoOdNlYMvWiuaidAxV1TTu0BbEBK/jyKhUukLHuEj7Id8GAviiTp8hEp8N7jGOsln4GVu6F+fxn1bFhPT3sShrEOvnfrgUDZ3kCZI++kxUjY/P0V+Rn7frzpL/UDD8E4Ctb6ZeC0MURuQaebjl+YglY+bJSPoPCrffbmRhH+A6i7YM/8YXUl0yK+8uNQrk6g== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=outlook.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=eHYCP6DGtbs3gHlgpArNyoE43B4DLAw2NIF95f4r9wE=; b=uCmtFBzxufUBwOrzH7x5vl6g/ah0TKFdxZDcyIN+E5VORCaxXYb6enoyojHYNDbElam+3DmqRcdie84V6ZBJ00oVukbRlzSh6lNuyt8pkUz8KqIYCFhE7CT0yrju33ISeNcQyNyafv/EqMKllY0DRkX0PaavwneQilAwV4en6xLWSM0Iei6WbicylCPIt/xFoItudTqlfVO5yalvCc3FLV0cVMRNBaNexG/9g/67pXhY9BTILEiun3iNgiwPTQjijWBU8eLO5WTyLd9IFLu88pB3349McIyDqsDn5bgPpKnj4QRRQKbYQHbMbC1LdPU981z/QfiJISEJGXNGHsZPPg== Received: from TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) by TYSPR06MB6768.apcprd06.prod.outlook.com (2603:1096:400:475::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6954.29; Sun, 12 Nov 2023 10:35:55 +0000 Received: from TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f]) by TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f%4]) with mapi id 15.20.6954.028; Sun, 12 Nov 2023 10:35:55 +0000 From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Nov 2023 18:35:14 +0800 Message-ID: X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231112103526.11245-1-nuomi2021@gmail.com> References: <20231112103526.11245-1-nuomi2021@gmail.com> X-TMN: [utufhlSB4hfNUD0ZjX7IEebkp3mWvc28] X-ClientProxiedBy: TYWP286CA0030.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:262::20) To TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) X-Microsoft-Original-Message-ID: <20231112103526.11245-2-nuomi2021@gmail.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 2 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: TYSPR06MB6433:EE_|TYSPR06MB6768:EE_ X-MS-Office365-Filtering-Correlation-Id: a84aee27-f09a-4b2a-d266-08dbe36b263b X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: IXnaWuTOhn5qxLXHqu2Zw4COb682JdVHnkB9urbqG5eFM3TnF1NKeP4DhfkjVW4sUX7oUhQalU8haM2AL6sB/He5YY3rp/DBKvyf/DCRHLqkEMXx1GsGtyFwlu0vLABR214o0ttavg5qBX6yyX/3BEkvjZOAQWWvVmK60mlZq2dT2aEMReTAKpyP26SSID1RP+qT4vLgRtcyb5KJW/l34xsg4hujt3r8szQ+975ly4tOW52PmVx44Dy/d7cirvWmrWezIFVjWB8FAqff27whwrMIE+Uwspvv7ucgzhWcKqFTJKKGuHiwvFTDu59AXpUJ2PZDc6f73GonUuK8Aqev/QpQrB39y/g4mG0zSkscmbrOrqEisUVSem8LpTl5qvzM95urJ9V4RYjHogLNaB+VKvk3QFLWQ1CeshRvvGoIjEDS8JLdRaNQwkvnXYcOW3fxY80bK3t50Hwc0Ln4u/UcbvG0HeqcJ4jiReqDvQ/tgZzH8RVH4Njz3aslKbgG8FVJMMKCR24VVS5K+K1tP6z5zW0pcNdT30+VRnaJyPvn3wvYck2vYsx7CrQKFC49zTLQ18lbXeGFqvqiBlmVLS8DJxsEOcjLrnMpej7+0iHP+wnAnlE6gKTVU1pkd9u1d40A X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?q?5X6MCvn4oUco6CM51/5Di/w841W+?= =?utf-8?q?C8gydx9yGSQgwavvCq1HiXZ4Gamq3zDjy1Fqao0kSLog/HGxqNK7luSFGKep/Sdz7?= =?utf-8?q?ns0czkJu0ehatzBndZNuQ27vFPKNSaEDb0Gmt2XR1ZmiqFk4PVC7dWYx9GrXULOUD?= =?utf-8?q?LatJCv8BuJS/B5+Q/HoDpONISfyW5s7jgmZ90fFaaCkOCKp05n0bJHA6/gYGzMzde?= =?utf-8?q?b7a1MSfvlyEeV7RzneGFKJVRWCcpK8OYG4vDgFH/5OiNWCsXFEY2+n8KsXSP4JenJ?= =?utf-8?q?SWMBXFHJlEDfH7fZPHI2a74Q/lDUs04wv0xRbmCNgwVMbdwg27bRNAoasYZ//7a+j?= =?utf-8?q?xkZDOd02MX0xbSr1JLZAGGnpdEnGHwT3Ux92GnzgwUF+pOLxOq4PuZdC2xkPYmbV7?= =?utf-8?q?7fnUOiHrzv9rjlaQcUl1eIhsFDWIoPPkC7vmOjhlhxWzsqQ2URh31IqBGYB9aXBhT?= =?utf-8?q?+d32n9/3gzE8gnq5sVF6X3Bcs0OdPQ7Bjtj/zDxhZaC0g/hyRLZS42S8m3sk91LnL?= =?utf-8?q?kZ+Qf7nEOfU7aIY6Pd+KY44TU5ntCaGf0nItcTVW3JbMd/yfaBGyPgglNdIJAL7uX?= =?utf-8?q?lXSOKdOyXbn6wlwJsdXGEBJQjATGpPcLa7qeIaOgC0+rEVDBHU0BjT9tsNrAh1ZXz?= =?utf-8?q?pW/BA0EuzjzUxVsLMg+llKiZnYd3nswgRF1CVb77gqT2iHMZbvJwCDFjOH0VVJC81?= =?utf-8?q?sKXzk5i9a/xHxkMNF5C7UoNNQ2kSXmTa8xhDBB65DdUkGCapqjVQZK3R1aUVkvs20?= =?utf-8?q?+Ax0Kx1OzJn1097sxRuaa/lhK6UzOu2ZLktQaES5y1Pnp/VFhiyuR2PLRpgSUL2Qv?= =?utf-8?q?Pw1rBh0214nz4BibsEUkPWXpuibAGmb/olKBTh35Oxiiqw4wODCvzyM3gkDQvtDHg?= =?utf-8?q?1nJbtYGQajFKNLxqHRFB/to+1k2ey6KN248y6j5lcTBD1V2gr2iZ+dF/7AVAUPU26?= =?utf-8?q?4VwqjFcnuYSF7sde8C4tWgEpcB09zLxxGeYLpLMtF4gfwfkYDBnGS/O3vnjqAhh7e?= =?utf-8?q?2HEjty00qJAjLfLBdjoC0IKdake+PHjWOTP1wfojdLJK3MB7+xplPquFdmW4hLsFK?= =?utf-8?q?2PcQV2dTjZROdslpQgD6pbtv7h8KqpfETao+VC9E8lYA/CAo4JC2ci7C6YzOIF7ma?= =?utf-8?q?443o6E+p9swr1Wtf9A+/rOSTq4GepqBjgtOPtkjeJwCoZjyByCBBZwKc+rB2KpaPi?= =?utf-8?q?cg6tkXSmrHtqHuPwG?= X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: a84aee27-f09a-4b2a-d266-08dbe36b263b X-MS-Exchange-CrossTenant-AuthSource: TYSPR06MB6433.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Nov 2023 10:35:55.2157 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYSPR06MB6768 Subject: [FFmpeg-devel] [PATCH v5 02/14] vvcdec: add vvc_data X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Nuo Mi Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: M239izG5Ju5c --- libavcodec/vvc/Makefile | 1 + libavcodec/vvc/vvc_data.c | 3486 +++++++++++++++++++++++++++++++++++++ libavcodec/vvc/vvc_data.h | 80 + 3 files changed, 3567 insertions(+) create mode 100644 libavcodec/vvc/vvc_data.c create mode 100644 libavcodec/vvc/vvc_data.h diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile index bd14dbc1df..8a5c66ab13 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -2,3 +2,4 @@ clean:: $(RM) $(CLEANSUFFIXES:%=libavcodec/vvc/%) OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ + vvc/vvc_data.o \ diff --git a/libavcodec/vvc/vvc_data.c b/libavcodec/vvc/vvc_data.c new file mode 100644 index 0000000000..0c9376d098 --- /dev/null +++ b/libavcodec/vvc/vvc_data.c @@ -0,0 +1,3486 @@ +/* + * VVC shared tables + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "libavutil/avassert.h" + +#include "vvc_data.h" + +const uint8_t ff_vvc_diag_scan_x[5][5][16*16] = { + { + //1x1 + { 0, }, + //1x2 + { 0, 0, }, + //1x4 + { 0, 0, 0, 0, }, + //1x8 + { 0, 0, 0, 0, 0, 0, 0, 0, }, + //1x16 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + }, + { + //2x1 + { 0, 1, }, + //2x2 + { 0, 0, 1, 1, }, + //2x4 + { 0, 0, 1, 0, 1, 0, 1, 1, }, + //2x8 + { 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, }, + //2x16 + { + 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, + }, + }, + { + //4x1 + { 0, 1, 2, 3, }, + //4x2 + { 0, 0, 1, 1, 2, 2, 3, 3, }, + //4x4 + { 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 2, 3, 3, }, + //4x8 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, + 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 2, 3, 3, + }, + //4x16 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, + 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, + 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, + 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 2, 3, 3, + }, + }, + { + //8x1 + { 0, 1, 2, 3, 4, 5, 6, 7, }, + //8x2 + { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, }, + //8x4 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, + 4, 5, 3, 4, 5, 6, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7, + }, + //8x8 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, + 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, + 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, + 7, 3, 4, 5, 6, 7, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7, + }, + //8x16 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, + 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, + 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, + 7, 3, 4, 5, 6, 7, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7, + }, + }, + { + //16x1 + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, }, + //16x2 + { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, + }, + //16x4 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, + 4, 5, 3, 4, 5, 6, 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, + 8, 9, 7, 8, 9, 10, 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, + 12, 13, 11, 12, 13, 14, 12, 13, 14, 15, 13, 14, 15, 14, 15, 15, + }, + //16x8 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, + 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, + 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8, 2, 3, 4, 5, + 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 10, 4, 5, 6, 7, + 8, 9, 10, 11, 5, 6, 7, 8, 9, 10, 11, 12, 6, 7, 8, 9, + 10, 11, 12, 13, 7, 8, 9, 10, 11, 12, 13, 14, 8, 9, 10, 11, + 12, 13, 14, 15, 9, 10, 11, 12, 13, 14, 15, 10, 11, 12, 13, 14, + 15, 11, 12, 13, 14, 15, 12, 13, 14, 15, 13, 14, 15, 14, 15, 15, + }, + //16x16 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, + 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 7, 8, 9, 10, 11, 12, 13, 14, 15, 8, 9, 10, 11, + 12, 13, 14, 15, 9, 10, 11, 12, 13, 14, 15, 10, 11, 12, 13, 14, + 15, 11, 12, 13, 14, 15, 12, 13, 14, 15, 13, 14, 15, 14, 15, 15, + }, + }, +}; + +const uint8_t ff_vvc_diag_scan_y[5][5][16*16] = { + { + //1x1 + { 0, }, + //1x2 + { 0, 1, }, + //1x4 + { 0, 1, 2, 3, }, + //1x8 + { 0, 1, 2, 3, 4, 5, 6, 7, }, + //1x16 + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, }, + }, + { + //2x1 + { 0, 0, }, + //2x2 + { 0, 1, 0, 1, }, + //2x4 + { 0, 1, 0, 2, 1, 3, 2, 3, }, + //2x8 + { 0, 1, 0, 2, 1, 3, 2, 4, 3, 5, 4, 6, 5, 7, 6, 7, }, + //2x16 + { + 0, 1, 0, 2, 1, 3, 2, 4, 3, 5, 4, 6, 5, 7, 6, 8, + 7, 9, 8, 10, 9, 11, 10, 12, 11, 13, 12, 14, 13, 15, 14, 15, + }, + }, + { + //4x1 + { 0, 0, 0, 0, }, + //4x2 + { 0, 1, 0, 1, 0, 1, 0, 1, }, + //4x4 + { 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 3, 2, 3, }, + //4x8 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 5, 4, + 3, 2, 6, 5, 4, 3, 7, 6, 5, 4, 7, 6, 5, 7, 6, 7, + }, + //4x16 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 5, 4, + 3, 2, 6, 5, 4, 3, 7, 6, 5, 4, 8, 7, 6, 5, 9, 8, + 7, 6, 10, 9, 8, 7, 11, 10, 9, 8, 12, 11, 10, 9, 13, 12, + 11, 10, 14, 13, 12, 11, 15, 14, 13, 12, 15, 14, 13, 15, 14, 15, + }, + }, + { + //8x1 + { 0, 0, 0, 0, 0, 0, 0, 0, }, + //8x2 + { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, }, + //8x4 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, + 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 3, 2, 3, + }, + //8x8 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0, 5, + 4, 3, 2, 1, 0, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, + 2, 7, 6, 5, 4, 3, 7, 6, 5, 4, 7, 6, 5, 7, 6, 7, + }, + //8x16 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0, 5, + 4, 3, 2, 1, 0, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 8, 7, 6, 5, 4, 3, 2, 1, 9, 8, 7, 6, + 5, 4, 3, 2, 10, 9, 8, 7, 6, 5, 4, 3, 11, 10, 9, 8, + 7, 6, 5, 4, 12, 11, 10, 9, 8, 7, 6, 5, 13, 12, 11, 10, + 9, 8, 7, 6, 14, 13, 12, 11, 10, 9, 8, 7, 15, 14, 13, 12, + 11, 10, 9, 8, 15, 14, 13, 12, 11, 10, 9, 15, 14, 13, 12, 11, + 10, 15, 14, 13, 12, 11, 15, 14, 13, 12, 15, 14, 13, 15, 14, 15, + }, + }, + { + //16x1 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + //16x2 + { + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + }, + //16x4 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, + 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, + 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, + 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 3, 2, 3, + }, + //16x8 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0, 5, + 4, 3, 2, 1, 0, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, + 2, 7, 6, 5, 4, 3, 7, 6, 5, 4, 7, 6, 5, 7, 6, 7, + }, + //16x16 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0, 5, + 4, 3, 2, 1, 0, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9, 8, 7, + 6, 5, 4, 3, 2, 1, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, + 1, 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 12, 11, + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 13, 12, 11, 10, 9, + 8, 7, 6, 5, 4, 3, 2, 1, 0, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 15, 14, 13, 12, 11, 10, 9, 8, 7, + 6, 5, 4, 3, 2, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, + 4, 3, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 15, 14, + 13, 12, 11, 10, 9, 8, 7, 6, 5, 15, 14, 13, 12, 11, 10, 9, + 8, 7, 6, 15, 14, 13, 12, 11, 10, 9, 8, 7, 15, 14, 13, 12, + 11, 10, 9, 8, 15, 14, 13, 12, 11, 10, 9, 15, 14, 13, 12, 11, + 10, 15, 14, 13, 12, 11, 15, 14, 13, 12, 15, 14, 13, 15, 14, 15, + }, + }, +}; + +const uint8_t ff_vvc_scaling_pred_8[8 * 8] = { + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, +}; + +const uint8_t ff_vvc_scaling_pred_16[8 * 8] = { + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, +}; + +const int ff_vvc_scaling_list0[8 * 8] = { + 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, 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, 0, 0, 0, 0, 0, 0, +}; + +static const uint8_t mip_matrix_4x4[16][16][4] = { + { + { 32, 30, 90, 28 }, + { 32, 32, 72, 28 }, + { 34, 77, 53, 30 }, + { 51, 124, 36, 37 }, + { 31, 31, 95, 37 }, + { 33, 31, 70, 50 }, + { 52, 80, 25, 60 }, + { 78, 107, 1, 65 }, + { 31, 29, 37, 95 }, + { 38, 34, 19, 101 }, + { 73, 85, 0, 81 }, + { 92, 99, 0, 65 }, + { 34, 29, 14, 111 }, + { 48, 48, 7, 100 }, + { 80, 91, 0, 74 }, + { 89, 97, 0, 64 } + }, + { + { 31, 23, 34, 29 }, + { 31, 43, 34, 31 }, + { 30, 95, 34, 32 }, + { 29, 100, 35, 33 }, + { 31, 23, 34, 29 }, + { 31, 43, 34, 31 }, + { 30, 95, 34, 32 }, + { 29, 99, 35, 33 }, + { 31, 24, 35, 29 }, + { 31, 44, 34, 31 }, + { 30, 95, 35, 32 }, + { 29, 99, 35, 33 }, + { 31, 24, 35, 30 }, + { 31, 44, 35, 31 }, + { 30, 95, 35, 32 }, + { 29, 99, 35, 33 } + }, + { + { 32, 32, 36, 58 }, + { 32, 29, 26, 66 }, + { 36, 37, 23, 61 }, + { 79, 84, 3, 37 }, + { 32, 32, 30, 69 }, + { 33, 29, 24, 71 }, + { 44, 16, 21, 70 }, + { 96, 18, 0, 57 }, + { 32, 31, 24, 74 }, + { 33, 30, 23, 71 }, + { 36, 24, 24, 71 }, + { 59, 9, 16, 68 }, + { 32, 32, 23, 75 }, + { 33, 30, 24, 70 }, + { 32, 30, 25, 71 }, + { 36, 26, 25, 70 } + }, + { + { 32, 33, 34, 32 }, + { 32, 30, 22, 38 }, + { 29, 46, 25, 38 }, + { 53, 123, 28, 22 }, + { 32, 33, 30, 37 }, + { 32, 30, 21, 38 }, + { 32, 40, 24, 38 }, + { 64, 116, 26, 17 }, + { 32, 32, 23, 49 }, + { 32, 30, 21, 39 }, + { 34, 39, 24, 37 }, + { 72, 109, 23, 16 }, + { 33, 31, 17, 60 }, + { 32, 31, 21, 39 }, + { 35, 41, 24, 37 }, + { 72, 106, 22, 18 } + }, + { + { 34, 25, 89, 20 }, + { 38, 32, 47, 24 }, + { 40, 86, 29, 27 }, + { 38, 98, 32, 29 }, + { 34, 31, 94, 40 }, + { 44, 25, 83, 27 }, + { 54, 72, 43, 16 }, + { 47, 94, 33, 22 }, + { 33, 31, 36, 94 }, + { 43, 23, 51, 76 }, + { 62, 55, 64, 25 }, + { 57, 89, 38, 15 }, + { 32, 32, 28, 101 }, + { 38, 26, 33, 94 }, + { 55, 38, 68, 47 }, + { 59, 80, 52, 16 } + }, + { + { 28, 30, 68, 29 }, + { 23, 48, 23, 48 }, + { 39, 98, 16, 42 }, + { 84, 86, 20, 17 }, + { 25, 31, 52, 74 }, + { 38, 68, 5, 70 }, + { 95, 78, 7, 21 }, + { 127, 54, 12, 0 }, + { 30, 47, 14, 107 }, + { 79, 76, 0, 53 }, + { 127, 59, 7, 1 }, + { 127, 51, 9, 0 }, + { 50, 71, 1, 96 }, + { 109, 69, 7, 25 }, + { 127, 56, 9, 0 }, + { 123, 53, 13, 0 } + }, + { + { 40, 20, 72, 18 }, + { 48, 29, 44, 18 }, + { 53, 81, 35, 18 }, + { 48, 96, 33, 22 }, + { 45, 23, 79, 49 }, + { 61, 21, 56, 49 }, + { 72, 52, 32, 48 }, + { 65, 69, 20, 50 }, + { 41, 27, 29, 96 }, + { 49, 22, 28, 94 }, + { 52, 22, 28, 93 }, + { 49, 27, 27, 92 }, + { 37, 29, 26, 98 }, + { 39, 28, 28, 97 }, + { 38, 28, 30, 97 }, + { 38, 29, 30, 95 } + }, + { + { 33, 27, 43, 27 }, + { 32, 29, 31, 31 }, + { 31, 73, 33, 31 }, + { 35, 104, 34, 28 }, + { 32, 30, 63, 22 }, + { 33, 26, 33, 29 }, + { 33, 57, 33, 30 }, + { 37, 100, 35, 27 }, + { 32, 31, 85, 25 }, + { 34, 25, 39, 25 }, + { 35, 39, 32, 28 }, + { 40, 91, 35, 25 }, + { 32, 30, 77, 50 }, + { 34, 26, 54, 22 }, + { 37, 31, 34, 27 }, + { 45, 75, 34, 23 } + }, + { + { 34, 25, 77, 19 }, + { 36, 34, 56, 24 }, + { 41, 83, 39, 30 }, + { 47, 96, 28, 35 }, + { 34, 31, 70, 65 }, + { 38, 29, 53, 77 }, + { 43, 36, 37, 83 }, + { 48, 39, 28, 83 }, + { 33, 31, 31, 98 }, + { 33, 31, 30, 99 }, + { 34, 30, 31, 98 }, + { 36, 29, 31, 96 }, + { 32, 32, 30, 97 }, + { 32, 32, 31, 96 }, + { 31, 33, 33, 96 }, + { 32, 33, 34, 94 } + }, + { + { 30, 30, 93, 19 }, + { 31, 59, 67, 34 }, + { 31, 79, 36, 59 }, + { 30, 67, 17, 79 }, + { 30, 38, 68, 69 }, + { 29, 40, 43, 91 }, + { 26, 35, 32, 101 }, + { 23, 32, 30, 101 }, + { 26, 34, 30, 101 }, + { 23, 33, 30, 102 }, + { 20, 32, 31, 102 }, + { 18, 33, 32, 102 }, + { 23, 33, 31, 100 }, + { 20, 34, 32, 100 }, + { 18, 35, 33, 100 }, + { 18, 35, 33, 100 } + }, + { + { 31, 54, 90, 26 }, + { 32, 60, 53, 61 }, + { 34, 49, 37, 84 }, + { 34, 39, 35, 89 }, + { 35, 38, 41, 88 }, + { 35, 35, 32, 96 }, + { 35, 31, 33, 96 }, + { 35, 32, 35, 94 }, + { 34, 34, 30, 97 }, + { 35, 32, 33, 95 }, + { 35, 32, 34, 94 }, + { 35, 34, 34, 93 }, + { 34, 34, 34, 93 }, + { 35, 34, 34, 93 }, + { 35, 34, 34, 92 }, + { 36, 34, 35, 91 } + }, + { + { 32, 29, 54, 24 }, + { 31, 32, 34, 29 }, + { 31, 43, 34, 29 }, + { 32, 67, 36, 28 }, + { 31, 34, 69, 37 }, + { 31, 35, 46, 33 }, + { 30, 35, 39, 33 }, + { 30, 42, 39, 36 }, + { 31, 35, 39, 88 }, + { 30, 38, 41, 84 }, + { 30, 39, 40, 81 }, + { 39, 46, 38, 78 }, + { 31, 36, 34, 96 }, + { 34, 38, 37, 93 }, + { 55, 42, 38, 82 }, + { 89, 53, 38, 65 } + }, + { + { 32, 33, 43, 29 }, + { 32, 30, 29, 33 }, + { 31, 47, 31, 33 }, + { 33, 100, 31, 31 }, + { 32, 33, 74, 25 }, + { 32, 32, 34, 31 }, + { 32, 33, 30, 33 }, + { 32, 68, 30, 32 }, + { 32, 31, 91, 40 }, + { 32, 32, 58, 26 }, + { 31, 31, 30, 32 }, + { 31, 42, 30, 33 }, + { 32, 31, 49, 85 }, + { 32, 31, 83, 35 }, + { 31, 33, 48, 29 }, + { 31, 36, 32, 33 } + }, + { + { 31, 29, 81, 35 }, + { 32, 28, 34, 50 }, + { 31, 75, 16, 43 }, + { 34, 103, 29, 32 }, + { 32, 32, 53, 78 }, + { 31, 28, 36, 88 }, + { 30, 52, 18, 73 }, + { 52, 88, 17, 35 }, + { 32, 32, 35, 94 }, + { 30, 31, 35, 95 }, + { 36, 29, 31, 92 }, + { 100, 43, 16, 40 }, + { 32, 32, 35, 93 }, + { 30, 32, 38, 93 }, + { 55, 18, 37, 83 }, + { 127, 0, 30, 40 } + }, + { + { 31, 22, 47, 30 }, + { 31, 48, 25, 34 }, + { 30, 95, 31, 32 }, + { 32, 103, 33, 32 }, + { 30, 24, 57, 31 }, + { 30, 47, 26, 34 }, + { 31, 95, 31, 32 }, + { 43, 97, 35, 25 }, + { 29, 26, 44, 63 }, + { 37, 38, 24, 47 }, + { 74, 63, 28, 20 }, + { 110, 58, 34, 3 }, + { 46, 22, 5, 108 }, + { 93, 5, 9, 77 }, + { 127, 0, 17, 52 }, + { 127, 0, 15, 50 } + }, + { + { 32, 27, 68, 24 }, + { 35, 23, 35, 28 }, + { 35, 64, 29, 29 }, + { 37, 104, 33, 28 }, + { 32, 32, 91, 40 }, + { 36, 23, 67, 36 }, + { 49, 23, 39, 28 }, + { 60, 67, 30, 20 }, + { 32, 32, 36, 95 }, + { 35, 29, 38, 93 }, + { 50, 16, 30, 84 }, + { 72, 16, 15, 65 }, + { 32, 32, 27, 100 }, + { 33, 32, 29, 100 }, + { 37, 29, 30, 98 }, + { 48, 21, 29, 90 } + } +}; + +static const uint8_t mip_matrix_8x8[8][16][8] = { + { + { 30, 63, 46, 37, 25, 33, 33, 34 }, + { 30, 60, 66, 38, 32, 31, 32, 33 }, + { 29, 45, 74, 42, 32, 32, 32, 33 }, + { 30, 39, 62, 58, 32, 33, 32, 33 }, + { 30, 66, 55, 39, 32, 30, 30, 36 }, + { 29, 54, 69, 40, 33, 31, 31, 33 }, + { 28, 48, 71, 43, 32, 33, 32, 33 }, + { 28, 41, 72, 46, 32, 34, 32, 33 }, + { 30, 66, 56, 40, 32, 33, 28, 33 }, + { 29, 55, 69, 39, 33, 33, 30, 32 }, + { 27, 46, 72, 43, 33, 33, 32, 33 }, + { 27, 42, 69, 48, 32, 34, 32, 33 }, + { 30, 63, 55, 40, 32, 33, 35, 30 }, + { 29, 56, 66, 40, 33, 33, 33, 30 }, + { 27, 47, 69, 44, 33, 33, 33, 32 }, + { 27, 42, 65, 50, 32, 34, 32, 33 } + }, + { + { 32, 33, 30, 31, 74, 30, 31, 32 }, + { 33, 56, 28, 30, 41, 29, 32, 32 }, + { 33, 77, 52, 26, 29, 34, 30, 32 }, + { 33, 37, 80, 41, 31, 34, 30, 32 }, + { 32, 32, 33, 31, 59, 76, 28, 31 }, + { 33, 31, 31, 30, 78, 40, 28, 32 }, + { 33, 47, 28, 29, 53, 27, 31, 31 }, + { 33, 61, 44, 28, 34, 32, 31, 31 }, + { 32, 31, 34, 30, 26, 64, 76, 27 }, + { 32, 31, 34, 29, 45, 86, 36, 29 }, + { 33, 27, 34, 29, 73, 55, 25, 32 }, + { 33, 33, 34, 30, 62, 33, 30, 31 }, + { 32, 31, 34, 30, 30, 29, 58, 74 }, + { 32, 31, 35, 29, 27, 53, 77, 35 }, + { 32, 30, 36, 29, 40, 80, 44, 31 }, + { 33, 28, 37, 30, 58, 60, 31, 33 } + }, + { + { 32, 51, 27, 32, 27, 50, 29, 32 }, + { 32, 95, 42, 29, 29, 42, 30, 32 }, + { 32, 27, 99, 34, 31, 41, 29, 32 }, + { 32, 34, 21, 104, 31, 42, 30, 32 }, + { 32, 45, 30, 32, 9, 88, 40, 30 }, + { 32, 77, 38, 30, 9, 76, 38, 30 }, + { 32, 38, 78, 33, 14, 67, 37, 30 }, + { 32, 30, 30, 87, 20, 59, 38, 31 }, + { 33, 37, 32, 32, 27, 18, 106, 34 }, + { 34, 44, 34, 31, 25, 17, 108, 31 }, + { 36, 39, 45, 31, 24, 15, 108, 30 }, + { 37, 31, 31, 54, 25, 14, 101, 32 }, + { 36, 33, 32, 30, 29, 37, 13, 110 }, + { 39, 32, 32, 29, 27, 37, 15, 108 }, + { 44, 33, 31, 27, 25, 37, 16, 106 }, + { 47, 30, 31, 32, 25, 34, 19, 102 } + }, + { + { 32, 48, 35, 35, 47, 68, 31, 31 }, + { 32, 33, 59, 40, 27, 71, 33, 30 }, + { 32, 29, 47, 65, 24, 62, 37, 30 }, + { 33, 33, 31, 81, 26, 50, 42, 32 }, + { 32, 30, 40, 38, 30, 70, 55, 31 }, + { 32, 20, 46, 50, 26, 55, 64, 31 }, + { 33, 30, 29, 66, 25, 41, 72, 33 }, + { 36, 34, 27, 69, 26, 31, 67, 39 }, + { 33, 28, 36, 40, 30, 26, 85, 47 }, + { 36, 27, 33, 50, 31, 20, 79, 53 }, + { 43, 30, 26, 57, 28, 17, 67, 62 }, + { 51, 27, 28, 55, 22, 23, 49, 70 }, + { 38, 29, 32, 39, 28, 30, 22, 104 }, + { 51, 31, 28, 43, 24, 31, 17, 102 }, + { 69, 23, 30, 40, 15, 38, 10, 95 }, + { 77, 13, 35, 38, 8, 43, 8, 90 } + }, + { + { 32, 38, 32, 33, 101, 40, 29, 32 }, + { 32, 40, 37, 32, 100, 36, 30, 32 }, + { 32, 37, 46, 35, 94, 33, 30, 31 }, + { 33, 34, 30, 62, 81, 35, 30, 31 }, + { 32, 32, 33, 32, 22, 102, 39, 29 }, + { 32, 31, 33, 33, 26, 104, 34, 28 }, + { 33, 33, 33, 33, 31, 103, 32, 28 }, + { 33, 32, 34, 36, 37, 94, 33, 28 }, + { 32, 33, 32, 32, 34, 24, 99, 36 }, + { 32, 34, 33, 33, 33, 30, 98, 32 }, + { 33, 33, 34, 33, 31, 37, 95, 29 }, + { 33, 33, 33, 36, 30, 46, 85, 31 }, + { 32, 33, 32, 33, 30, 34, 23, 104 }, + { 32, 34, 33, 33, 31, 32, 30, 98 }, + { 32, 33, 34, 34, 31, 29, 39, 91 }, + { 33, 33, 32, 37, 32, 30, 47, 82 } + }, + { + { 32, 52, 48, 31, 38, 76, 26, 32 }, + { 33, 19, 62, 50, 25, 50, 51, 31 }, + { 33, 30, 20, 74, 29, 29, 54, 51 }, + { 34, 35, 23, 56, 31, 25, 41, 76 }, + { 33, 25, 38, 39, 28, 39, 83, 35 }, + { 35, 28, 25, 47, 31, 23, 57, 74 }, + { 37, 35, 22, 38, 31, 27, 30, 101 }, + { 38, 32, 33, 29, 30, 31, 27, 103 }, + { 34, 32, 27, 37, 32, 25, 41, 92 }, + { 38, 33, 28, 32, 30, 31, 18, 111 }, + { 40, 32, 33, 27, 29, 33, 18, 111 }, + { 40, 32, 34, 27, 28, 33, 23, 105 }, + { 35, 32, 30, 33, 31, 33, 20, 107 }, + { 38, 31, 33, 30, 29, 33, 21, 106 }, + { 40, 32, 33, 29, 29, 34, 22, 105 }, + { 40, 32, 33, 30, 29, 34, 24, 101 } + }, + { + { 32, 28, 31, 33, 92, 33, 30, 31 }, + { 33, 30, 28, 33, 71, 26, 32, 30 }, + { 33, 60, 26, 33, 47, 28, 33, 30 }, + { 33, 63, 44, 36, 37, 31, 33, 30 }, + { 33, 30, 31, 33, 43, 90, 33, 29 }, + { 33, 28, 29, 34, 71, 71, 26, 30 }, + { 33, 30, 26, 33, 86, 45, 28, 30 }, + { 33, 38, 29, 32, 74, 32, 33, 29 }, + { 33, 32, 30, 32, 29, 41, 95, 27 }, + { 34, 31, 29, 33, 26, 71, 73, 22 }, + { 34, 31, 29, 33, 37, 88, 46, 25 }, + { 33, 32, 28, 34, 55, 75, 36, 28 }, + { 34, 31, 30, 32, 33, 27, 43, 89 }, + { 35, 32, 28, 33, 33, 23, 77, 59 }, + { 34, 33, 28, 33, 30, 35, 91, 37 }, + { 34, 34, 28, 34, 33, 53, 74, 31 } + }, + { + { 33, 49, 26, 32, 26, 52, 28, 31 }, + { 33, 71, 72, 24, 30, 32, 34, 31 }, + { 32, 23, 70, 68, 32, 32, 32, 32 }, + { 31, 33, 21, 106, 33, 32, 32, 33 }, + { 34, 47, 32, 29, 5, 86, 44, 26 }, + { 34, 44, 89, 28, 28, 37, 33, 30 }, + { 32, 27, 46, 89, 33, 31, 31, 32 }, + { 30, 33, 20, 107, 33, 33, 32, 33 }, + { 35, 39, 42, 27, 26, 24, 92, 35 }, + { 34, 27, 87, 43, 30, 34, 38, 31 }, + { 31, 31, 32, 100, 32, 33, 30, 32 }, + { 29, 32, 22, 106, 33, 33, 32, 33 }, + { 35, 29, 47, 32, 32, 32, 17, 100 }, + { 34, 24, 69, 60, 34, 33, 28, 44 }, + { 31, 33, 31, 99, 32, 33, 32, 31 }, + { 29, 33, 25, 103, 33, 33, 32, 35 } + } +}; + +static const uint8_t mip_matrix_16x16[6][64][7] = { + { + { 42, 37, 33, 27, 44, 33, 35 }, + { 71, 39, 34, 24, 36, 35, 36 }, + { 77, 46, 35, 33, 30, 34, 36 }, + { 64, 60, 35, 33, 31, 32, 36 }, + { 49, 71, 38, 32, 32, 31, 36 }, + { 42, 66, 50, 33, 31, 32, 36 }, + { 40, 52, 67, 33, 31, 32, 35 }, + { 38, 43, 75, 33, 32, 32, 35 }, + { 56, 40, 33, 26, 43, 38, 36 }, + { 70, 49, 34, 30, 28, 38, 38 }, + { 65, 57, 36, 34, 28, 33, 39 }, + { 59, 60, 39, 33, 30, 31, 38 }, + { 55, 60, 43, 33, 30, 31, 38 }, + { 51, 61, 47, 33, 30, 32, 37 }, + { 46, 62, 51, 34, 30, 32, 37 }, + { 42, 60, 55, 33, 31, 32, 37 }, + { 60, 42, 34, 30, 37, 43, 38 }, + { 68, 52, 35, 35, 22, 37, 40 }, + { 62, 58, 37, 34, 28, 31, 40 }, + { 58, 59, 41, 33, 30, 30, 39 }, + { 56, 59, 44, 34, 30, 31, 38 }, + { 53, 60, 45, 33, 30, 31, 38 }, + { 49, 65, 45, 33, 30, 31, 38 }, + { 45, 64, 47, 33, 31, 32, 38 }, + { 59, 44, 35, 31, 34, 43, 41 }, + { 66, 53, 36, 35, 25, 31, 43 }, + { 61, 58, 38, 34, 29, 30, 40 }, + { 59, 57, 41, 33, 30, 31, 39 }, + { 57, 58, 43, 33, 30, 31, 39 }, + { 54, 61, 43, 33, 31, 31, 39 }, + { 51, 64, 43, 33, 31, 31, 39 }, + { 48, 64, 45, 33, 32, 31, 39 }, + { 57, 45, 35, 30, 35, 40, 44 }, + { 65, 54, 37, 33, 33, 24, 44 }, + { 63, 56, 38, 34, 30, 29, 39 }, + { 61, 56, 41, 34, 30, 32, 39 }, + { 58, 58, 42, 33, 31, 31, 39 }, + { 54, 62, 41, 33, 31, 31, 39 }, + { 51, 65, 42, 33, 31, 31, 39 }, + { 48, 63, 43, 33, 32, 31, 39 }, + { 55, 46, 35, 30, 36, 38, 47 }, + { 65, 53, 37, 32, 36, 26, 40 }, + { 65, 54, 38, 33, 31, 30, 38 }, + { 63, 55, 39, 33, 30, 32, 38 }, + { 59, 58, 40, 33, 31, 31, 39 }, + { 54, 64, 40, 33, 31, 30, 40 }, + { 49, 66, 40, 32, 32, 30, 41 }, + { 48, 64, 42, 32, 32, 30, 41 }, + { 54, 46, 35, 30, 34, 39, 49 }, + { 64, 52, 36, 32, 34, 34, 35 }, + { 65, 53, 37, 33, 32, 32, 37 }, + { 63, 55, 38, 33, 31, 31, 39 }, + { 59, 60, 38, 33, 31, 31, 40 }, + { 54, 64, 38, 33, 32, 30, 40 }, + { 49, 66, 39, 33, 32, 29, 41 }, + { 47, 64, 42, 32, 33, 29, 42 }, + { 51, 46, 35, 31, 33, 37, 54 }, + { 61, 51, 36, 32, 33, 38, 36 }, + { 63, 53, 37, 32, 32, 34, 37 }, + { 62, 55, 37, 33, 32, 32, 39 }, + { 58, 59, 37, 33, 32, 31, 40 }, + { 53, 63, 38, 33, 32, 31, 40 }, + { 49, 64, 40, 33, 33, 30, 41 }, + { 46, 62, 42, 33, 33, 30, 42 } + }, + { + { 39, 34, 33, 58, 44, 31, 32 }, + { 60, 38, 32, 40, 51, 30, 31 }, + { 73, 49, 31, 39, 48, 32, 31 }, + { 60, 73, 30, 39, 46, 33, 32 }, + { 43, 87, 35, 38, 45, 33, 32 }, + { 35, 78, 54, 36, 45, 33, 32 }, + { 33, 47, 86, 35, 44, 33, 32 }, + { 31, 17, 114, 34, 44, 34, 33 }, + { 43, 37, 32, 53, 70, 30, 31 }, + { 53, 50, 30, 42, 72, 31, 30 }, + { 52, 66, 30, 39, 70, 32, 30 }, + { 46, 78, 35, 37, 68, 34, 30 }, + { 43, 75, 48, 37, 66, 34, 30 }, + { 40, 62, 68, 35, 65, 35, 30 }, + { 33, 37, 97, 33, 62, 37, 31 }, + { 26, 14, 122, 32, 59, 38, 33 }, + { 40, 39, 33, 34, 87, 37, 30 }, + { 45, 54, 32, 34, 84, 41, 29 }, + { 41, 70, 35, 33, 83, 40, 29 }, + { 37, 73, 44, 32, 82, 40, 30 }, + { 37, 65, 60, 31, 81, 41, 29 }, + { 35, 48, 82, 30, 79, 43, 29 }, + { 28, 27, 108, 28, 76, 45, 30 }, + { 19, 11, 127, 27, 70, 46, 32 }, + { 38, 40, 34, 27, 73, 62, 28 }, + { 39, 54, 35, 30, 73, 62, 28 }, + { 33, 65, 41, 29, 75, 59, 28 }, + { 30, 65, 53, 27, 76, 58, 29 }, + { 29, 53, 72, 26, 77, 58, 29 }, + { 27, 35, 95, 24, 77, 60, 28 }, + { 19, 19, 117, 23, 74, 61, 30 }, + { 9, 16, 127, 23, 68, 60, 34 }, + { 35, 40, 35, 29, 44, 89, 30 }, + { 33, 51, 39, 29, 49, 86, 30 }, + { 28, 57, 49, 28, 53, 83, 30 }, + { 24, 52, 65, 26, 56, 82, 30 }, + { 22, 39, 86, 24, 58, 82, 30 }, + { 18, 22, 108, 23, 59, 82, 31 }, + { 10, 13, 125, 22, 58, 80, 33 }, + { 0, 19, 127, 22, 56, 74, 40 }, + { 33, 40, 36, 31, 28, 90, 45 }, + { 29, 46, 44, 29, 31, 92, 43 }, + { 24, 45, 58, 28, 34, 91, 43 }, + { 19, 37, 78, 26, 37, 91, 43 }, + { 15, 22, 99, 25, 38, 91, 42 }, + { 11, 11, 118, 24, 39, 90, 44 }, + { 2, 11, 127, 23, 41, 85, 48 }, + { 0, 17, 127, 23, 43, 75, 55 }, + { 31, 37, 39, 30, 28, 54, 82 }, + { 27, 37, 52, 28, 30, 58, 79 }, + { 22, 30, 70, 27, 32, 58, 79 }, + { 15, 19, 91, 26, 33, 58, 79 }, + { 10, 8, 111, 25, 34, 58, 79 }, + { 5, 2, 125, 25, 35, 57, 80 }, + { 0, 9, 127, 25, 36, 53, 84 }, + { 0, 13, 127, 25, 39, 47, 88 }, + { 28, 29, 46, 28, 39, 2, 123 }, + { 24, 24, 62, 27, 41, 1, 125 }, + { 19, 14, 81, 25, 43, 0, 126 }, + { 13, 4, 101, 24, 44, 0, 127 }, + { 6, 0, 116, 23, 45, 0, 127 }, + { 0, 0, 126, 23, 45, 1, 127 }, + { 0, 4, 127, 25, 44, 2, 127 }, + { 0, 9, 127, 25, 44, 3, 127 } + }, + { + { 30, 32, 32, 42, 34, 32, 32 }, + { 63, 26, 34, 16, 38, 32, 32 }, + { 98, 26, 34, 25, 34, 33, 32 }, + { 75, 61, 30, 31, 32, 33, 32 }, + { 36, 94, 32, 30, 33, 32, 32 }, + { 26, 76, 58, 30, 33, 32, 32 }, + { 30, 39, 91, 31, 32, 33, 31 }, + { 32, 23, 105, 32, 32, 32, 32 }, + { 34, 30, 33, 31, 52, 29, 32 }, + { 66, 24, 34, 11, 41, 33, 32 }, + { 97, 28, 34, 24, 34, 33, 32 }, + { 71, 65, 30, 30, 32, 33, 32 }, + { 34, 92, 35, 30, 33, 32, 32 }, + { 26, 70, 64, 29, 34, 32, 32 }, + { 30, 37, 94, 30, 33, 32, 31 }, + { 32, 23, 105, 31, 33, 33, 31 }, + { 37, 29, 33, 8, 79, 27, 32 }, + { 71, 22, 35, 5, 50, 32, 32 }, + { 98, 29, 34, 23, 34, 34, 32 }, + { 66, 70, 30, 31, 31, 33, 32 }, + { 31, 92, 38, 30, 33, 32, 32 }, + { 26, 66, 68, 29, 34, 32, 31 }, + { 30, 34, 97, 30, 34, 33, 31 }, + { 31, 22, 106, 30, 34, 33, 31 }, + { 40, 28, 34, 0, 76, 46, 28 }, + { 76, 21, 35, 0, 55, 35, 32 }, + { 97, 32, 34, 21, 37, 33, 33 }, + { 61, 75, 29, 30, 32, 32, 32 }, + { 29, 92, 40, 29, 33, 32, 32 }, + { 26, 62, 73, 29, 34, 32, 31 }, + { 29, 32, 99, 30, 34, 33, 30 }, + { 31, 22, 107, 30, 34, 33, 31 }, + { 42, 27, 34, 1, 48, 79, 25 }, + { 80, 20, 35, 0, 48, 47, 31 }, + { 94, 36, 32, 17, 40, 33, 33 }, + { 55, 80, 29, 27, 35, 31, 32 }, + { 27, 90, 43, 28, 34, 32, 31 }, + { 26, 58, 76, 29, 33, 33, 30 }, + { 29, 30, 101, 29, 34, 34, 30 }, + { 31, 21, 108, 29, 35, 34, 30 }, + { 44, 26, 34, 6, 30, 80, 40 }, + { 81, 21, 35, 0, 41, 52, 35 }, + { 90, 41, 31, 14, 41, 35, 33 }, + { 51, 82, 29, 24, 37, 32, 32 }, + { 27, 87, 47, 27, 35, 32, 31 }, + { 26, 54, 79, 29, 34, 33, 30 }, + { 29, 29, 102, 28, 34, 33, 30 }, + { 31, 21, 108, 28, 35, 33, 31 }, + { 47, 26, 34, 7, 34, 44, 75 }, + { 80, 24, 34, 0, 41, 41, 50 }, + { 84, 45, 31, 12, 40, 36, 36 }, + { 49, 81, 31, 22, 37, 33, 32 }, + { 28, 81, 51, 26, 35, 33, 31 }, + { 28, 51, 81, 28, 34, 33, 30 }, + { 29, 30, 101, 28, 35, 33, 31 }, + { 31, 22, 107, 28, 35, 33, 32 }, + { 48, 27, 34, 10, 40, 16, 97 }, + { 75, 27, 34, 3, 42, 26, 66 }, + { 77, 47, 33, 12, 40, 32, 43 }, + { 49, 75, 36, 21, 37, 33, 35 }, + { 32, 72, 55, 25, 36, 33, 32 }, + { 30, 49, 81, 27, 35, 33, 31 }, + { 30, 32, 98, 28, 35, 32, 32 }, + { 31, 24, 104, 28, 35, 32, 33 } + }, + { + { 36, 29, 33, 43, 47, 29, 31 }, + { 74, 20, 35, 19, 47, 34, 32 }, + { 92, 35, 32, 29, 31, 40, 34 }, + { 53, 80, 26, 33, 28, 36, 37 }, + { 24, 91, 41, 31, 31, 31, 38 }, + { 25, 57, 74, 31, 32, 30, 37 }, + { 32, 28, 99, 32, 32, 29, 36 }, + { 34, 20, 105, 33, 32, 30, 35 }, + { 50, 26, 34, 33, 74, 30, 31 }, + { 75, 28, 33, 23, 46, 47, 33 }, + { 64, 58, 29, 30, 26, 46, 40 }, + { 31, 85, 37, 31, 27, 33, 44 }, + { 22, 67, 64, 30, 31, 28, 42 }, + { 29, 35, 93, 31, 32, 27, 40 }, + { 33, 20, 105, 32, 33, 27, 37 }, + { 34, 19, 106, 33, 32, 29, 36 }, + { 51, 29, 33, 25, 72, 51, 30 }, + { 61, 42, 31, 30, 31, 60, 39 }, + { 40, 70, 34, 32, 24, 41, 50 }, + { 22, 72, 54, 30, 31, 27, 50 }, + { 25, 44, 83, 30, 33, 25, 44 }, + { 32, 23, 102, 32, 33, 26, 40 }, + { 34, 18, 107, 32, 33, 28, 37 }, + { 34, 19, 105, 33, 32, 30, 35 }, + { 45, 35, 32, 30, 39, 79, 33 }, + { 43, 53, 33, 35, 24, 53, 55 }, + { 27, 67, 45, 32, 29, 27, 61 }, + { 22, 53, 72, 30, 33, 22, 52 }, + { 28, 31, 95, 31, 33, 25, 43 }, + { 32, 20, 105, 32, 33, 27, 38 }, + { 34, 18, 107, 32, 32, 29, 36 }, + { 34, 20, 105, 33, 31, 31, 35 }, + { 38, 40, 32, 35, 23, 72, 54 }, + { 31, 55, 39, 34, 29, 32, 73 }, + { 22, 57, 60, 31, 35, 18, 64 }, + { 25, 39, 86, 31, 35, 22, 49 }, + { 30, 24, 101, 32, 33, 27, 40 }, + { 33, 19, 106, 32, 32, 30, 36 }, + { 34, 18, 107, 33, 31, 31, 35 }, + { 34, 20, 104, 33, 31, 32, 34 }, + { 33, 42, 35, 34, 28, 39, 82 }, + { 26, 51, 50, 33, 34, 18, 80 }, + { 23, 46, 74, 31, 35, 20, 59 }, + { 27, 32, 93, 32, 34, 26, 44 }, + { 31, 22, 103, 32, 32, 30, 37 }, + { 33, 19, 106, 33, 31, 31, 35 }, + { 34, 19, 106, 33, 31, 32, 34 }, + { 35, 21, 103, 34, 31, 32, 34 }, + { 29, 41, 41, 33, 34, 20, 92 }, + { 24, 44, 62, 34, 35, 18, 73 }, + { 24, 37, 83, 34, 33, 25, 52 }, + { 28, 28, 97, 33, 32, 30, 40 }, + { 32, 23, 103, 33, 31, 32, 36 }, + { 34, 20, 105, 34, 30, 33, 34 }, + { 35, 20, 104, 34, 30, 33, 33 }, + { 35, 22, 102, 34, 30, 33, 34 }, + { 27, 38, 51, 34, 34, 20, 86 }, + { 26, 37, 71, 35, 34, 24, 64 }, + { 27, 33, 87, 35, 32, 30, 47 }, + { 30, 28, 96, 34, 31, 32, 39 }, + { 32, 24, 100, 35, 30, 32, 36 }, + { 34, 23, 101, 34, 30, 33, 34 }, + { 35, 23, 101, 34, 30, 32, 34 }, + { 34, 24, 99, 35, 30, 33, 34 } + }, + { + { 39, 30, 31, 67, 33, 34, 31 }, + { 72, 21, 32, 43, 39, 33, 31 }, + { 100, 23, 32, 35, 39, 34, 31 }, + { 75, 63, 24, 32, 38, 34, 32 }, + { 32, 98, 26, 29, 37, 35, 32 }, + { 22, 77, 55, 29, 36, 35, 31 }, + { 31, 37, 90, 31, 35, 35, 32 }, + { 35, 22, 100, 33, 33, 36, 33 }, + { 47, 29, 32, 74, 54, 32, 31 }, + { 71, 24, 32, 60, 50, 36, 30 }, + { 86, 31, 30, 46, 48, 37, 30 }, + { 65, 63, 25, 34, 46, 39, 30 }, + { 33, 85, 32, 28, 43, 40, 30 }, + { 26, 64, 60, 27, 39, 41, 30 }, + { 33, 33, 87, 29, 35, 41, 31 }, + { 37, 23, 93, 32, 33, 41, 32 }, + { 41, 32, 32, 45, 84, 32, 32 }, + { 55, 31, 32, 50, 70, 40, 30 }, + { 62, 37, 31, 45, 61, 45, 29 }, + { 53, 55, 31, 36, 55, 48, 29 }, + { 38, 63, 40, 29, 48, 50, 28 }, + { 34, 49, 60, 27, 43, 51, 29 }, + { 38, 30, 78, 28, 38, 50, 31 }, + { 40, 24, 83, 30, 36, 48, 33 }, + { 35, 33, 33, 29, 75, 58, 29 }, + { 39, 35, 33, 34, 68, 59, 29 }, + { 41, 39, 34, 36, 61, 62, 29 }, + { 41, 43, 37, 33, 54, 64, 28 }, + { 41, 43, 45, 30, 48, 65, 29 }, + { 42, 36, 56, 27, 44, 63, 30 }, + { 42, 30, 65, 27, 41, 60, 33 }, + { 42, 28, 68, 28, 37, 56, 36 }, + { 33, 34, 33, 31, 42, 88, 30 }, + { 31, 36, 34, 31, 44, 84, 31 }, + { 31, 37, 35, 32, 43, 83, 31 }, + { 35, 35, 39, 32, 40, 82, 31 }, + { 40, 32, 44, 31, 38, 81, 31 }, + { 44, 30, 48, 30, 37, 78, 33 }, + { 44, 30, 52, 28, 37, 72, 36 }, + { 43, 30, 55, 29, 35, 66, 40 }, + { 32, 33, 33, 34, 25, 85, 48 }, + { 30, 34, 34, 33, 25, 88, 44 }, + { 30, 34, 36, 34, 25, 90, 41 }, + { 33, 32, 38, 34, 25, 90, 40 }, + { 38, 29, 41, 34, 26, 88, 40 }, + { 42, 29, 41, 33, 27, 85, 41 }, + { 43, 30, 42, 31, 28, 80, 43 }, + { 42, 31, 45, 31, 30, 72, 47 }, + { 32, 33, 33, 33, 26, 54, 79 }, + { 31, 32, 34, 35, 20, 68, 68 }, + { 32, 32, 35, 36, 17, 76, 62 }, + { 34, 31, 36, 36, 17, 79, 59 }, + { 37, 29, 37, 36, 18, 78, 58 }, + { 39, 29, 37, 35, 20, 77, 58 }, + { 41, 30, 37, 34, 22, 74, 58 }, + { 40, 31, 40, 32, 26, 68, 59 }, + { 33, 31, 34, 33, 29, 31, 98 }, + { 34, 30, 34, 35, 23, 45, 88 }, + { 34, 31, 34, 36, 20, 54, 82 }, + { 35, 31, 34, 36, 18, 59, 78 }, + { 36, 31, 34, 37, 19, 60, 76 }, + { 38, 30, 34, 36, 20, 61, 74 }, + { 39, 31, 35, 35, 22, 60, 73 }, + { 39, 31, 37, 34, 24, 59, 71 } + }, + { + { 30, 33, 32, 55, 32, 32, 32 }, + { 47, 30, 31, 29, 36, 32, 32 }, + { 81, 28, 32, 28, 34, 32, 32 }, + { 85, 46, 29, 32, 32, 33, 32 }, + { 54, 82, 26, 32, 32, 33, 32 }, + { 30, 90, 38, 31, 32, 33, 32 }, + { 30, 56, 73, 31, 33, 32, 32 }, + { 37, 21, 102, 32, 32, 32, 32 }, + { 33, 32, 31, 68, 39, 31, 31 }, + { 38, 32, 31, 43, 34, 33, 31 }, + { 63, 30, 31, 29, 34, 32, 32 }, + { 82, 37, 30, 29, 33, 32, 32 }, + { 71, 63, 27, 31, 32, 33, 32 }, + { 44, 86, 30, 30, 33, 33, 32 }, + { 33, 72, 55, 30, 32, 32, 31 }, + { 37, 37, 86, 31, 32, 33, 31 }, + { 34, 33, 32, 60, 61, 29, 32 }, + { 36, 33, 31, 56, 38, 32, 31 }, + { 51, 30, 31, 38, 33, 33, 32 }, + { 75, 31, 31, 30, 33, 33, 32 }, + { 80, 47, 29, 30, 32, 33, 31 }, + { 60, 73, 27, 30, 33, 33, 31 }, + { 41, 78, 41, 30, 33, 32, 31 }, + { 38, 53, 68, 30, 32, 33, 31 }, + { 33, 33, 32, 43, 77, 35, 30 }, + { 35, 33, 31, 55, 54, 29, 32 }, + { 43, 32, 31, 46, 39, 31, 32 }, + { 64, 30, 31, 35, 34, 33, 32 }, + { 79, 37, 30, 31, 32, 33, 31 }, + { 73, 57, 28, 30, 32, 33, 31 }, + { 54, 73, 33, 30, 32, 33, 31 }, + { 43, 64, 52, 30, 32, 33, 31 }, + { 33, 33, 32, 34, 68, 58, 28 }, + { 34, 33, 31, 45, 70, 33, 31 }, + { 38, 33, 31, 48, 52, 29, 32 }, + { 54, 31, 31, 40, 39, 31, 32 }, + { 73, 32, 31, 34, 34, 33, 31 }, + { 77, 45, 29, 31, 32, 32, 32 }, + { 65, 63, 30, 31, 31, 33, 31 }, + { 51, 66, 42, 30, 32, 33, 31 }, + { 33, 32, 32, 34, 44, 81, 31 }, + { 34, 33, 31, 38, 66, 52, 28 }, + { 36, 33, 30, 44, 62, 34, 31 }, + { 47, 31, 31, 43, 48, 30, 32 }, + { 64, 31, 31, 38, 38, 32, 32 }, + { 75, 38, 30, 33, 34, 32, 32 }, + { 71, 53, 30, 31, 32, 33, 32 }, + { 59, 61, 37, 30, 32, 33, 32 }, + { 33, 32, 31, 35, 31, 71, 54 }, + { 34, 33, 31, 37, 49, 70, 33 }, + { 36, 33, 31, 41, 60, 48, 30 }, + { 43, 32, 31, 43, 54, 35, 31 }, + { 56, 31, 31, 40, 44, 32, 32 }, + { 68, 35, 30, 36, 37, 32, 32 }, + { 70, 45, 30, 33, 34, 33, 32 }, + { 63, 55, 35, 31, 33, 33, 32 }, + { 33, 32, 31, 33, 34, 36, 87 }, + { 34, 32, 31, 36, 38, 62, 52 }, + { 36, 33, 31, 39, 50, 57, 36 }, + { 41, 33, 31, 41, 53, 43, 33 }, + { 50, 33, 31, 41, 48, 36, 32 }, + { 59, 35, 31, 37, 41, 34, 32 }, + { 65, 42, 31, 35, 36, 33, 32 }, + { 62, 49, 35, 33, 34, 34, 33 } + } +}; + +const uint8_t* ff_vvc_get_mip_matrix(const int size_id, const int mode_id) +{ + av_assert0(size_id < 3); + if (size_id == 0) + return &mip_matrix_4x4[mode_id][0][0]; + if (size_id == 1) + return &mip_matrix_8x8[mode_id][0][0]; + return &mip_matrix_16x16[mode_id][0][0]; +} + +// DCT-8 +#define DEFINE_DCT8_P4_MATRIX(a,b,c,d) \ +{ \ + { a, b, c, d }, \ + { b, 0, -b, -b }, \ + { c, -b, -d, a }, \ + { d, -b, a, -c }, \ +} + +#define DEFINE_DCT8_P8_MATRIX(a,b,c,d,e,f,g,h) \ +{ \ + { a, b, c, d, e, f, g, h }, \ + { b, e, h, -g, -d, -a, -c, -f }, \ + { c, h, -e, -a, -f, g, b, d }, \ + { d, -g, -a, -h, c, e, -f, -b }, \ + { e, -d, -f, c, g, -b, -h, a }, \ + { f, -a, g, e, -b, h, d, -c }, \ + { g, -c, b, -f, -h, d, -a, e }, \ + { h, -f, d, -b, a, -c, e, -g }, \ +} + +#define DEFINE_DCT8_P16_MATRIX(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \ +{ \ + { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p }, \ + { b, e, h, k, n, 0, -n, -k, -h, -e, -b, -b, -e, -h, -k, -n }, \ + { c, h, m, -p, -k, -f, -a, -e, -j, -o, n, i, d, b, g, l }, \ + { d, k, -p, -i, -b, -f, -m, n, g, a, h, o, -l, -e, -c, -j }, \ + { e, n, -k, -b, -h, 0, h, b, k, -n, -e, -e, -n, k, b, h }, \ + { f, 0, -f, -f, 0, f, f, 0, -f, -f, 0, f, f, 0, -f, -f }, \ + { g, -n, -a, -m, h, f, -o, -b, -l, i, e, -p, -c, -k, j, d }, \ + { h, -k, -e, n, b, 0, -b, -n, e, k, -h, -h, k, e, -n, -b }, \ + { i, -h, -j, g, k, -f, -l, e, m, -d, -n, c, o, -b, -p, a }, \ + { j, -e, -o, a, -n, -f, i, k, -d, -p, b, -m, -g, h, l, -c }, \ + { k, -b, n, h, -e, 0, e, -h, -n, b, -k, -k, b, -n, -h, e }, \ + { l, -b, i, o, -e, f, -p, -h, c, -m, -k, a, -j, -n, d, -g }, \ + { m, -e, d, -l, -n, f, -c, k, o, -g, b, -j, -p, h, -a, i }, \ + { n, -h, b, -e, k, 0, -k, e, -b, h, -n, -n, h, -b, e, -k }, \ + { o, -k, g, -c, b, -f, j, -n, -p, l, -h, d, -a, e, -i, m }, \ + { p, -n, l, -j, h, -f, d, -b, a, -c, e, -g, i, -k, m, -o }, \ +} + +#define DEFINE_DCT8_P32_MATRIX(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F) \ +{ \ + { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F }, \ + { b, e, h, k, n, q, t, w, z, C, F, -E, -B, -y, -v, -s, -p, -m, -j, -g, -d, -a, -c, -f, -i, -l, -o, -r, -u, -x, -A, -D }, \ + { c, h, m, r, w, B, 0, -B, -w, -r, -m, -h, -c, -c, -h, -m, -r, -w, -B, 0, B, w, r, m, h, c, c, h, m, r, w, B }, \ + { d, k, r, y, F, -A, -t, -m, -f, -b, -i, -p, -w, -D, C, v, o, h, a, g, n, u, B, -E, -x, -q, -j, -c, -e, -l, -s, -z }, \ + { e, n, w, F, -y, -p, -g, -c, -l, -u, -D, A, r, i, a, j, s, B, -C, -t, -k, -b, -h, -q, -z, E, v, m, d, f, o, x }, \ + { f, q, B, -A, -p, -e, -g, -r, -C, z, o, d, h, s, D, -y, -n, -c, -i, -t, -E, x, m, b, j, u, F, -w, -l, -a, -k, -v }, \ + { g, t, 0, -t, -g, -g, -t, 0, t, g, g, t, 0, -t, -g, -g, -t, 0, t, g, g, t, 0, -t, -g, -g, -t, 0, t, g, g, t }, \ + { h, w, -B, -m, -c, -r, 0, r, c, m, B, -w, -h, -h, -w, B, m, c, r, 0, -r, -c, -m, -B, w, h, h, w, -B, -m, -c, -r }, \ + { i, z, -w, -f, -l, -C, t, c, o, F, -q, -a, -r, E, n, d, u, -B, -k, -g, -x, y, h, j, A, -v, -e, -m, -D, s, b, p }, \ + { j, C, -r, -b, -u, z, g, m, F, -o, -e, -x, w, d, p, -E, -l, -h, -A, t, a, s, -B, -i, -k, -D, q, c, v, -y, -f, -n }, \ + { k, F, -m, -i, -D, o, g, B, -q, -e, -z, s, c, x, -u, -a, -v, w, b, t, -y, -d, -r, A, f, p, -C, -h, -n, E, j, l }, \ + { l, -E, -h, -p, A, d, t, -w, -a, -x, s, e, B, -o, -i, -F, k, m, -D, -g, -q, z, c, u, -v, -b, -y, r, f, C, -n, -j }, \ + { m, -B, -c, -w, r, h, 0, -h, -r, w, c, B, -m, -m, B, c, w, -r, -h, 0, h, r, -w, -c, -B, m, m, -B, -c, -w, r, h }, \ + { n, -y, -c, -D, i, s, -t, -h, E, d, x, -o, -m, z, b, C, -j, -r, u, g, -F, -e, -w, p, l, -A, -a, -B, k, q, -v, -f }, \ + { o, -v, -h, C, a, D, -g, -w, n, p, -u, -i, B, b, E, -f, -x, m, q, -t, -j, A, c, F, -e, -y, l, r, -s, -k, z, d }, \ + { p, -s, -m, v, j, -y, -g, B, d, -E, -a, -F, c, C, -f, -z, i, w, -l, -t, o, q, -r, -n, u, k, -x, -h, A, e, -D, -b }, \ + { q, -p, -r, o, s, -n, -t, m, u, -l, -v, k, w, -j, -x, i, y, -h, -z, g, A, -f, -B, e, C, -d, -D, c, E, -b, -F, a }, \ + { r, -m, -w, h, B, -c, 0, c, -B, -h, w, m, -r, -r, m, w, -h, -B, c, 0, -c, B, h, -w, -m, r, r, -m, -w, h, B, -c }, \ + { s, -j, -B, a, -C, -i, t, r, -k, -A, b, -D, -h, u, q, -l, -z, c, -E, -g, v, p, -m, -y, d, -F, -f, w, o, -n, -x, e }, \ + { t, -g, 0, g, -t, -t, g, 0, -g, t, t, -g, 0, g, -t, -t, g, 0, -g, t, t, -g, 0, g, -t, -t, g, 0, -g, t, t, -g }, \ + { u, -d, B, n, -k, -E, g, -r, -x, a, -y, -q, h, -F, -j, o, A, -c, v, t, -e, C, m, -l, -D, f, -s, -w, b, -z, -p, i }, \ + { v, -a, w, u, -b, x, t, -c, y, s, -d, z, r, -e, A, q, -f, B, p, -g, C, o, -h, D, n, -i, E, m, -j, F, l, -k }, \ + { w, -c, r, B, -h, m, 0, -m, h, -B, -r, c, -w, -w, c, -r, -B, h, -m, 0, m, -h, B, r, -c, w, w, -c, r, B, -h, m }, \ + { x, -f, m, -E, -q, b, -t, -B, j, -i, A, u, -c, p, F, -n, e, -w, -y, g, -l, D, r, -a, s, C, -k, h, -z, -v, d, -o }, \ + { y, -i, h, -x, -z, j, -g, w, A, -k, f, -v, -B, l, -e, u, C, -m, d, -t, -D, n, -c, s, E, -o, b, -r, -F, p, -a, q }, \ + { z, -l, c, -q, E, u, -g, h, -v, -D, p, -b, m, -A, -y, k, -d, r, -F, -t, f, -i, w, C, -o, a, -n, B, x, -j, e, -s }, \ + { A, -o, c, -j, v, F, -t, h, -e, q, -C, -y, m, -a, l, -x, -D, r, -f, g, -s, E, w, -k, b, -n, z, B, -p, d, -i, u }, \ + { B, -r, h, -c, m, -w, 0, w, -m, c, -h, r, -B, -B, r, -h, c, -m, w, 0, -w, m, -c, h, -r, B, B, -r, h, -c, m, -w }, \ + { C, -u, m, -e, d, -l, t, -B, -D, v, -n, f, -c, k, -s, A, E, -w, o, -g, b, -j, r, -z, -F, x, -p, h, -a, i, -q, y }, \ + { D, -x, r, -l, f, -a, g, -m, s, -y, E, C, -w, q, -k, e, -b, h, -n, t, -z, F, B, -v, p, -j, d, -c, i, -o, u, -A }, \ + { E, -A, w, -s, o, -k, g, -c, b, -f, j, -n, r, -v, z, -D, -F, B, -x, t, -p, l, -h, d, -a, e, -i, m, -q, u, -y, C }, \ + { F, -D, B, -z, x, -v, t, -r, p, -n, l, -j, h, -f, d, -b, a, -c, e, -g, i, -k, m, -o, q, -s, u, -w, y, -A, C, -E }, \ +} + +const int8_t ff_vvc_dct8_4x4[4][4] = DEFINE_DCT8_P4_MATRIX(84, 74, 55, 29); +const int8_t ff_vvc_dct8_8x8[8][8] = DEFINE_DCT8_P8_MATRIX(86, 85, 78, 71, 60, 46, 32, 17); +const int8_t ff_vvc_dct8_16x16[16][16] = DEFINE_DCT8_P16_MATRIX(88, 88, 87, 85, 81, 77, 73, 68, 62, 55, 48, 40, 33, 25, 17, 8); +const int8_t ff_vvc_dct8_32x32[32][32] = DEFINE_DCT8_P32_MATRIX(90, 90, 89, 88, 87, 86, 85, 84, 82, 80, 78, 77, 74, 72, 68, 66, 63, 60, 56, 53, 50, 46, 42, 38, 34, 30, 26, 21, 17, 13, 9, 4); + +// DST-7 +#define DEFINE_DST7_P4_MATRIX(a,b,c,d) \ +{ \ + { a, b, c, d }, \ + { c, c, 0, -c }, \ + { d, -a, -c, b }, \ + { b, -d, c, -a }, \ +} + +#define DEFINE_DST7_P8_MATRIX(a,b,c,d,e,f,g,h) \ +{ \ + { a, b, c, d, e, f, g, h }, \ + { c, f, h, e, b, -a, -d, -g }, \ + { e, g, b, -c, -h, -d, a, f }, \ + { g, c, -d, -f, a, h, b, -e }, \ + { h, -a, -g, b, f, -c, -e, d }, \ + { f, -e, -a, g, -d, -b, h, -c }, \ + { d, -h, e, -a, -c, g, -f, b }, \ + { b, -d, f, -h, g, -e, c, -a }, \ +} + +#define DEFINE_DST7_P16_MATRIX(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \ +{ \ + { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p }, \ + { c, f, i, l, o, o, l, i, f, c, 0, -c, -f, -i, -l, -o }, \ + { e, j, o, m, h, c, -b, -g, -l, -p, -k, -f, -a, d, i, n }, \ + { g, n, l, e, -b, -i, -p, -j, -c, d, k, o, h, a, -f, -m }, \ + { i, o, f, -c, -l, -l, -c, f, o, i, 0, -i, -o, -f, c, l }, \ + { k, k, 0, -k, -k, 0, k, k, 0, -k, -k, 0, k, k, 0, -k }, \ + { m, g, -f, -n, -a, l, h, -e, -o, -b, k, i, -d, -p, -c, j }, \ + { o, c, -l, -f, i, i, -f, -l, c, o, 0, -o, -c, l, f, -i }, \ + { p, -a, -o, b, n, -c, -m, d, l, -e, -k, f, j, -g, -i, h }, \ + { n, -e, -i, j, d, -o, a, m, -f, -h, k, c, -p, b, l, -g }, \ + { l, -i, -c, o, -f, -f, o, -c, -i, l, 0, -l, i, c, -o, f }, \ + { j, -m, c, g, -p, f, d, -n, i, a, -k, l, -b, -h, o, -e }, \ + { h, -p, i, -a, -g, o, -j, b, f, -n, k, -c, -e, m, -l, d }, \ + { f, -l, o, -i, c, c, -i, o, -l, f, 0, -f, l, -o, i, -c }, \ + { d, -h, l, -p, m, -i, e, -a, -c, g, -k, o, -n, j, -f, b }, \ + { b, -d, f, -h, j, -l, n, -p, o, -m, k, -i, g, -e, c, -a }, \ +} + +#define DEFINE_DST7_P32_MATRIX(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F) \ +{ \ + { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F }, \ + { c, f, i, l, o, r, u, x, A, D, F, C, z, w, t, q, n, k, h, e, b, -a, -d, -g, -j, -m, -p, -s, -v, -y, -B, -E }, \ + { e, j, o, t, y, D, D, y, t, o, j, e, 0, -e, -j, -o, -t, -y, -D, -D, -y, -t, -o, -j, -e, 0, e, j, o, t, y, D }, \ + { g, n, u, B, D, w, p, i, b, -e, -l, -s, -z, -F, -y, -r, -k, -d, c, j, q, x, E, A, t, m, f, -a, -h, -o, -v, -C }, \ + { i, r, A, C, t, k, b, -g, -p, -y, -E, -v, -m, -d, e, n, w, F, x, o, f, -c, -l, -u, -D, -z, -q, -h, a, j, s, B }, \ + { k, v, F, u, j, -a, -l, -w, -E, -t, -i, b, m, x, D, s, h, -c, -n, -y, -C, -r, -g, d, o, z, B, q, f, -e, -p, -A }, \ + { m, z, z, m, 0, -m, -z, -z, -m, 0, m, z, z, m, 0, -m, -z, -z, -m, 0, m, z, z, m, 0, -m, -z, -z, -m, 0, m, z }, \ + { o, D, t, e, -j, -y, -y, -j, e, t, D, o, 0, -o, -D, -t, -e, j, y, y, j, -e, -t, -D, -o, 0, o, D, t, e, -j, -y }, \ + { q, E, n, -c, -t, -B, -k, f, w, y, h, -i, -z, -v, -e, l, C, s, b, -o, -F, -p, a, r, D, m, -d, -u, -A, -j, g, x }, \ + { s, A, h, -k, -D, -p, c, v, x, e, -n, -F, -m, f, y, u, b, -q, -C, -j, i, B, r, -a, -t, -z, -g, l, E, o, -d, -w }, \ + { u, w, b, -s, -y, -d, q, A, f, -o, -C, -h, m, E, j, -k, -F, -l, i, D, n, -g, -B, -p, e, z, r, -c, -x, -t, a, v }, \ + { w, s, -d, -A, -o, h, E, k, -l, -D, -g, p, z, c, -t, -v, a, x, r, -e, -B, -n, i, F, j, -m, -C, -f, q, y, b, -u }, \ + { y, o, -j, -D, -e, t, t, -e, -D, -j, o, y, 0, -y, -o, j, D, e, -t, -t, e, D, j, -o, -y, 0, y, o, -j, -D, -e, t }, \ + { A, k, -p, -v, e, F, f, -u, -q, j, B, a, -z, -l, o, w, -d, -E, -g, t, r, -i, -C, -b, y, m, -n, -x, c, D, h, -s }, \ + { C, g, -v, -n, o, u, -h, -B, a, D, f, -w, -m, p, t, -i, -A, b, E, e, -x, -l, q, s, -j, -z, c, F, d, -y, -k, r }, \ + { E, c, -B, -f, y, i, -v, -l, s, o, -p, -r, m, u, -j, -x, g, A, -d, -D, a, F, b, -C, -e, z, h, -w, -k, t, n, -q }, \ + { F, -a, -E, b, D, -c, -C, d, B, -e, -A, f, z, -g, -y, h, x, -i, -w, j, v, -k, -u, l, t, -m, -s, n, r, -o, -q, p }, \ + { D, -e, -y, j, t, -o, -o, t, j, -y, -e, D, 0, -D, e, y, -j, -t, o, o, -t, -j, y, e, -D, 0, D, -e, -y, j, t, -o }, \ + { B, -i, -s, r, j, -A, -a, C, -h, -t, q, k, -z, -b, D, -g, -u, p, l, -y, -c, E, -f, -v, o, m, -x, -d, F, -e, -w, n }, \ + { z, -m, -m, z, 0, -z, m, m, -z, 0, z, -m, -m, z, 0, -z, m, m, -z, 0, z, -m, -m, z, 0, -z, m, m, -z, 0, z, -m }, \ + { x, -q, -g, E, -j, -n, A, -c, -u, t, d, -B, m, k, -D, f, r, -w, -a, y, -p, -h, F, -i, -o, z, -b, -v, s, e, -C, l }, \ + { v, -u, -a, w, -t, -b, x, -s, -c, y, -r, -d, z, -q, -e, A, -p, -f, B, -o, -g, C, -n, -h, D, -m, -i, E, -l, -j, F, -k }, \ + { t, -y, e, o, -D, j, j, -D, o, e, -y, t, 0, -t, y, -e, -o, D, -j, -j, D, -o, -e, y, -t, 0, t, -y, e, o, -D, j }, \ + { r, -C, k, g, -y, v, -d, -n, F, -o, -c, u, -z, h, j, -B, s, -a, -q, D, -l, -f, x, -w, e, m, -E, p, b, -t, A, -i }, \ + { p, -F, q, -a, -o, E, -r, b, n, -D, s, -c, -m, C, -t, d, l, -B, u, -e, -k, A, -v, f, j, -z, w, -g, -i, y, -x, h }, \ + { n, -B, w, -i, -e, s, -F, r, -d, -j, x, -A, m, a, -o, C, -v, h, f, -t, E, -q, c, k, -y, z, -l, -b, p, -D, u, -g }, \ + { l, -x, C, -q, e, g, -s, E, -v, j, b, -n, z, -A, o, -c, -i, u, -F, t, -h, -d, p, -B, y, -m, a, k, -w, D, -r, f }, \ + { j, -t, D, -y, o, -e, -e, o, -y, D, -t, j, 0, -j, t, -D, y, -o, e, e, -o, y, -D, t, -j, 0, j, -t, D, -y, o, -e }, \ + { h, -p, x, -F, y, -q, i, -a, -g, o, -w, E, -z, r, -j, b, f, -n, v, -D, A, -s, k, -c, -e, m, -u, C, -B, t, -l, d }, \ + { f, -l, r, -x, D, -C, w, -q, k, -e, -a, g, -m, s, -y, E, -B, v, -p, j, -d, -b, h, -n, t, -z, F, -A, u, -o, i, -c }, \ + { d, -h, l, -p, t, -x, B, -F, C, -y, u, -q, m, -i, e, -a, -c, g, -k, o, -s, w, -A, E, -D, z, -v, r, -n, j, -f, b }, \ + { b, -d, f, -h, j, -l, n, -p, r, -t, v, -x, z, -B, D, -F, E, -C, A, -y, w, -u, s, -q, o, -m, k, -i, g, -e, c, -a }, \ +} + +const int8_t ff_vvc_dst7_4x4[4][4] = DEFINE_DST7_P4_MATRIX (29, 55, 74, 84); +const int8_t ff_vvc_dst7_8x8[8][8] = DEFINE_DST7_P8_MATRIX (17, 32, 46, 60, 71, 78, 85, 86); +const int8_t ff_vvc_dst7_16x16[16][16] = DEFINE_DST7_P16_MATRIX( 8, 17, 25, 33, 40, 48, 55, 62, 68, 73, 77, 81, 85, 87, 88, 88); +const int8_t ff_vvc_dst7_32x32[32][32] = DEFINE_DST7_P32_MATRIX( 4, 9, 13, 17, 21, 26, 30, 34, 38, 42, 46, 50, 53, 56, 60, 63, 66, 68, 72, 74, 77, 78, 80, 82, 84, 85, 86, 87, 88, 89, 90, 90); + +const int8_t ff_vvc_lfnst_8x8[4][2][16][48] = { + { //0 + { + { -117, 28, 18, 2, 4, 1, 2, 1, 32, -18, -2, 0, -1, 0, 0, 0, 14, -1, -3, 0, -1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, -1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 }, + { -29, -91, 47, 1, 9, 0, 3, 0, -54, 26, -8, 3, 0, 1, 0, 0, 33, 5, -9, -1, -2, 0, -1, 0, -3, 3, 0, 0, 0, 0, 0, 0, 7, 2, -2, 0, -1, 1, 0, 0, 2, 1, -1, 0, 0, 0, 0, 0 }, + { -10, 62, -11, -8, -2, -2, -1, -1, -95, 3, 32, 0, 4, 0, 2, 0, 32, -30, -4, 4, -1, 1, 0, 0, 6, 2, -5, 0, 0, 0, 0, 0, 6, -3, 0, 0, 2, 0, -1, 0, 2, -1, 0, 0, 1, 0, 0, 0 }, + { -15, 15, -10, -2, 1, 0, 1, 0, 10, 112, -20, -17, -4, -4, -1, -2, -20, -26, 31, 1, 0, 0, 0, 0, 2, -16, -1, 6, 0, 1, 0, 0, 1, -4, 0, 0, 0, -3, 0, 1, 0, -1, 0, 0, 0, -2, 0, 0 }, + { 32, 39, 92, -44, 4, -10, 1, -4, 26, 12, -15, 13, -5, 2, -2, 0, 29, -16, -22, 8, 0, 1, 0, 1, -20, 6, 4, -3, 1, 0, 0, 0, 1, -4, -3, 2, -4, 1, 0, 0, 1, -1, -2, 1, -2, 0, 0, 0 }, + { -10, 1, 50, -15, 2, -3, 1, -1, -28, -15, 14, 6, 1, 1, 1, 0, -99, -4, 9, 5, 5, 2, 2, 1, 44, -10, -11, 1, -2, 0, -1, 0, -5, 4, -3, 0, 8, -1, -2, 0, -2, 1, -1, 0, 4, 0, -1, 0 }, + { 1, -33, -11, -14, 7, -2, 2, 0, 29, -12, 37, -7, -4, 0, -1, 0, 6, -99, 3, 26, -1, 5, 0, 2, 14, 30, -27, -2, 1, -1, 0, -1, -6, 6, 6, -3, 1, 3, -3, 0, -1, 1, 1, 0, 0, 1, -1, 0 }, + { 0, 6, -6, 21, -4, 2, 0, 0, -20, -24, -104, 30, 5, 5, 1, 2, -7, -46, 10, -14, 7, 0, 1, 0, 9, 21, 7, -6, -2, -1, 0, -1, 2, 2, 5, -2, 0, 3, 4, -1, 0, 0, 1, 0, 0, 1, 2, -1 }, + { -13, -13, -37, -101, 29, -11, 8, -3, -12, -15, -20, 2, -11, 5, -2, 1, -12, 10, 26, 12, -6, 0, -1, 0, -32, -2, 11, 3, 3, -1, 1, 0, 11, -5, -1, 6, -4, 2, 1, 0, 3, -1, 1, 2, -1, 0, 0, 0 }, + { 6, 1, -14, -36, 9, -3, 2, 0, 10, 9, -18, -1, -3, 1, 0, 0, 38, 26, -13, -1, -5, -1, -1, 0, 102, 3, -14, -1, -5, -1, -2, 0, -29, 10, 10, 0, 10, -4, -1, 1, -7, 1, 2, 1, 2, -1, 0, 0 }, + { -12, -2, -26, -12, -9, 2, -1, 1, -3, 30, 4, 34, -4, 0, -1, 0, -30, 3, -92, 14, 19, 0, 3, 0, -11, 34, 21, -33, 1, -2, 0, -1, -9, -4, 18, 3, 2, 0, 0, -2, -1, -1, 3, 0, 0, 0, 0, -1 }, + { 0, -3, 0, -4, -15, 6, -3, 1, -7, -15, -28, -86, 19, -5, 4, -1, -5, -17, -41, 42, -6, 2, -1, 1, -1, -40, 37, 13, -4, 2, -1, 1, -10, 13, -1, -4, 4, -4, 3, 4, -2, 2, -1, -1, 1, -1, 1, 2 }, + { -1, 9, 13, 5, 14, -2, 2, -1, -8, 3, -4, -62, 4, 1, 1, 0, -12, 23, 16, -11, -17, 0, -1, 0, -11, 97, -3, -3, 0, -6, 0, -2, -21, -5, 23, 0, 2, -2, -1, 6, -3, -3, 1, 0, 0, 0, 0, 2 }, + { 6, 2, -3, 2, 10, -1, 2, 0, 8, 3, -1, -20, 0, 1, 0, 0, -4, 4, -16, 0, -2, 0, 1, 0, 34, 23, 6, -7, -4, -2, -1, 0, 108, -5, -30, 6, -27, 10, 7, -2, 11, -3, -1, 1, -4, 1, 0, 1 }, + { 6, 9, -2, 35, 110, -22, 11, -4, -2, 0, -3, 1, -18, 12, -3, 2, -5, -4, -22, 8, -25, 3, 0, 0, -3, -21, 2, -3, 9, -2, 1, 0, -7, 1, 3, -5, 3, 0, -1, 0, 0, 1, 0, -1, 1, 0, 0, 0 }, + { -1, 7, -2, 9, -11, 5, -1, 1, -7, 2, -22, 4, -13, 0, -1, 0, 0, 28, 0, 76, 4, -6, 0, -2, -13, 5, -76, -4, 33, -1, 3, 0, 9, 18, -3, -35, -4, -1, 6, 1, 1, 2, 0, -3, -1, 0, 2, 0 }, + }, + { + { -108, 48, 9, 1, 1, 1, 0, 0, 44, -6, -9, -1, -1, 0, -1, 0, 9, -9, -1, 1, 0, 0, 0, 0, 3, -1, 1, 0, 0, 0, 0, 0, 1, -1, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0 }, + { 55, 66, -37, -5, -6, -1, -2, 0, 67, -30, -20, 4, -2, 0, -1, 0, -31, -19, 14, 4, 1, 1, 1, 0, -6, 3, 5, -2, 0, 0, 0, 0, -7, -1, 1, 0, -1, 1, 1, 0, -2, -1, 1, 0, 0, 0, 0, 0 }, + { 2, 86, -21, -13, -4, -2, -1, -1, -88, 5, 6, 4, 5, 1, 1, 0, 14, -5, 0, 3, 0, 0, 0, 0, 10, -5, -2, 0, -1, 0, 0, 0, 6, -5, 0, 1, 2, -1, 0, 0, 1, -1, 0, 0, 1, 0, 0, 0 }, + { -24, -21, -38, 19, 0, 4, -1, 2, -23, -89, 31, 20, 2, 3, 1, 1, -30, 26, 36, -8, -2, -2, 0, -1, 14, 18, -7, -9, -1, -1, 0, 0, 1, 3, -2, -1, 3, 2, -2, -1, 0, 1, 0, 0, 1, 1, -1, 0 }, + { 9, 20, 98, -26, -3, -5, 0, -2, -9, -26, 15, -16, 2, 0, 1, 0, -61, -3, -2, 3, 7, 1, 1, 0, 12, 16, -6, -1, 0, -1, 0, 0, 2, 0, -8, 1, 3, 1, -1, 1, 0, -1, -2, 0, 1, 0, -1, 0 }, + { -21, -7, -37, 10, 2, 2, -1, 1, -10, 69, -5, -7, -2, -2, 0, -1, -93, 2, 19, 0, 3, 0, 2, 0, 17, 4, 0, 0, -1, 0, 0, 0, 5, -4, -2, 0, 4, -2, 0, 1, 0, 0, 0, 0, 2, -1, 0, 0 }, + { -10, -25, 4, -17, 8, -2, 2, -1, -27, -17, -71, 25, 8, 2, 1, 1, -4, -66, 28, 36, -5, 3, 0, 1, -10, 20, 33, -13, -8, 0, 0, -1, 3, 6, -3, -7, -1, 3, 3, -1, 1, 0, -1, 0, 0, 1, 1, -1 }, + { 2, 5, 10, 64, -9, 4, -3, 1, -4, 8, 62, 3, -17, 1, -2, 0, -3, -75, 5, -14, 1, 4, 0, 1, -36, 3, 18, -4, 4, 0, 1, 0, 1, 14, -2, -8, -2, 1, -3, 0, 2, 2, -1, -2, 0, 1, -1, 0 }, + { -11, -15, -28, -97, 6, -1, 4, -1, 7, 3, 57, -15, 10, -2, 0, -1, -1, -27, 13, 6, 1, -1, 0, 0, -34, -6, 0, 3, 4, 1, 2, 0, -2, 8, 1, 5, -2, 0, -3, 1, 1, 1, 0, 2, -1, 0, -1, 0 }, + { 9, 13, 24, -6, 7, -2, 1, -1, 16, 39, 20, 47, -2, -2, -2, 0, 28, 23, 76, -5, -25, -3, -3, -1, 6, 36, -7, -39, -4, -1, 0, -1, 2, -4, -18, -3, -1, -1, -2, -2, 1, -2, -2, 0, 0, 0, -1, -1 }, + { -7, 11, 12, 7, 2, -1, 0, -1, -14, -1, -24, 11, 2, 0, 0, 0, -20, 48, 11, -13, -5, -2, 0, -1, -105, -19, 17, 0, 6, 2, 3, 0, -14, 8, 8, 2, 1, 2, -1, -2, 3, 0, -1, 0, 0, 0, 0, 0 }, + { 0, 0, 7, -6, 23, -3, 3, -1, 5, 1, 18, 96, 13, -9, -1, -1, -21, -7, -42, 14, -24, -3, 0, 0, 11, -47, -7, 3, -5, 9, 1, 2, 0, -1, 19, -1, 1, 0, -1, -6, -1, 1, 2, 0, 1, 0, 0, -2 }, + { -2, -6, -1, -10, 0, 1, 1, 0, -7, -2, -28, 20, -15, 4, -3, 1, -2, -32, -2, -66, 3, 7, 1, 2, -11, 13, -70, 5, 43, -2, 3, 0, 8, -14, -3, 43, -1, 2, 7, -1, 1, -2, 1, 3, -1, 1, 1, 0 }, + { -1, 6, -16, 0, 24, -3, 1, -1, 2, 6, 6, 16, 18, -7, 1, -1, -3, 11, -63, 9, 4, -5, 2, -1, -22, 94, -4, -6, -4, -4, 1, -2, 10, 23, -19, -5, 0, -6, -4, 6, 3, -2, 1, 1, 0, -1, 0, 0 }, + { -5, -6, -3, -19, -104, 18, -4, 3, 0, 6, 0, 35, -41, 20, -2, 2, -2, 10, -18, 16, 21, 3, -2, 0, -2, 11, 6, -10, 6, -3, -1, 0, -1, 5, -1, -6, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, -1 }, + { -1, -2, 0, 23, -9, 0, -2, 0, 1, 1, 8, -1, 29, 1, 1, 0, 3, -6, 13, 76, 30, -11, -1, -2, -26, -8, -69, 7, -9, -7, 3, -1, -10, -34, -25, 13, -1, 0, 11, 5, 1, -1, 1, -2, 0, 0, 2, 0 }, + } + }, + { //1 + { + { 110, -49, -3, -4, -1, -1, 0, -1, -38, -1, 10, 0, 2, 0, 1, 0, -9, 13, 1, -2, 0, 0, 0, 0, -4, 2, -3, 0, 0, 0, 0, 0, -2, 2, 0, 1, -1, 1, 0, 0, -1, 1, 0, 0, -1, 0, 0, 0 }, + { -43, -19, 17, -1, 3, 0, 1, 0, -98, 46, 14, -1, 2, 0, 1, 0, 26, 26, -15, -3, -2, -1, -1, 0, 11, -7, -9, 2, 0, 0, 0, 0, 9, -3, -1, 2, 3, -3, 0, 0, 4, -1, 0, 0, 2, -1, 0, 0 }, + { -19, 17, -7, 3, -2, 1, -1, 0, -32, -59, 29, 3, 4, 0, 2, 0, -72, 43, 34, -9, 3, -2, 1, -1, 13, 36, -18, -10, 0, -2, 0, -1, 3, 0, -12, 3, 6, 1, -3, 2, 1, -1, -2, 0, 3, 1, -1, 1 }, + { -35, -103, 39, 1, 7, 0, 2, 0, 38, -13, 25, -6, 1, -1, 0, 0, -1, 7, 6, -7, 1, -1, 0, 0, -13, 14, 2, -4, 2, -1, 0, 0, -2, 11, -6, -2, -2, 4, -3, 0, 0, 3, -2, 0, -1, 1, -1, 0 }, + { 9, 5, -6, -1, -1, 0, -1, 0, 42, 4, 21, -11, 1, -3, 1, -1, 21, 70, -32, -21, 0, -4, -1, -1, 34, -26, -57, 11, 4, 2, 0, 1, -4, -32, 5, 24, 1, -6, 12, 4, -3, -2, 4, -2, 0, -1, 0, 0 }, + { -5, -5, -28, 9, -3, 2, -1, 1, -20, -78, 22, 16, 1, 3, 0, 1, 80, -6, 25, -5, -4, -1, -1, 0, 6, -24, 7, -9, 0, 0, 0, 0, -7, 3, 13, -4, -3, 5, 1, -5, -2, 3, 1, -2, -1, 2, -1, -2 }, + { 14, 17, 27, -12, 1, -3, 1, -1, 8, 19, -13, 4, -2, 1, -1, 0, 48, -1, 48, -15, -4, -2, -1, -1, 1, 60, -28, -42, 5, -6, 1, -2, 11, -11, -51, 11, -2, -10, -2, 13, 2, -6, -4, 4, -2, -3, 2, 2 }, + { 7, 35, 17, -4, -1, 0, 0, 0, 3, 8, 54, -17, 1, -2, 1, -1, 10, 14, -11, -34, 4, -4, 1, -1, -80, -7, -6, 2, 15, 0, 3, 0, -16, 46, 1, 3, 2, 7, -24, 0, 2, -2, -5, 8, 1, -1, -2, 2 }, + { -13, -27, -101, 24, -8, 6, -3, 2, 11, 43, 6, 28, -6, 3, -1, 1, -3, 14, 21, -12, -7, -2, -1, -1, -23, 10, -4, -12, 3, 0, 1, 0, 2, 9, -10, 0, 1, -5, -4, 4, 2, -2, 2, 2, 0, -2, 1, 0 }, + { -11, -13, -3, -10, 3, -1, 1, 0, -19, -19, -37, 8, 4, 2, 0, 1, -12, -30, 3, -9, 5, 0, 1, 0, -56, -9, -47, 8, 21, 1, 4, 1, -11, -30, 10, 59, -2, 8, 41, 8, 2, 5, 6, -7, -1, 3, 5, -2 }, + { -4, -10, -24, -11, 3, -2, 0, -1, -6, -37, -45, -17, 8, -2, 2, -1, 17, 14, -58, 14, 15, 0, 2, 0, -10, 34, -7, 28, 4, -1, 1, 0, 23, 34, -31, 4, 10, -22, -30, 22, 4, -15, 9, 20, 2, -5, 9, 4 }, + { -2, 1, 13, -17, 3, -5, 1, -2, 3, 0, -55, 22, 6, 1, 1, 0, 8, 74, 21, 40, -14, 0, -2, 0, -36, -8, 11, -13, -23, 1, -3, 0, -36, 6, 16, -14, 2, 19, -4, -12, -1, 0, -7, -3, 0, 2, -2, -1 }, + { 3, 1, 5, -15, 1, -2, 1, -1, 7, 4, -7, 29, -1, 2, -1, 1, 8, 3, 12, -14, -9, -1, -1, 0, 4, 29, -15, 31, 10, 4, 1, 1, 61, 22, 55, 14, 13, 3, -9, -65, 1, -11, -21, -7, 0, 0, -1, 3 }, + { -4, -8, -1, -50, 6, -4, 2, -2, -1, 5, -22, 20, 6, 1, 0, 0, -16, -15, 18, -29, -11, 2, -2, 1, 40, -45, -19, -22, 31, 2, 4, 1, -25, 41, 0, 12, 9, 7, -42, 12, -3, -14, 2, 28, 5, 1, 6, 2 }, + { 5, -1, 26, 102, -13, 12, -4, 4, -4, -2, -40, -7, -23, 3, -5, 1, -1, 5, 8, -23, 7, 2, 1, 1, 10, -11, -13, -3, 12, -3, 2, 0, -9, 23, 4, 9, 14, 9, -14, -4, 0, -12, -7, 6, 3, 0, 6, 3 }, + { -5, -6, -27, -22, -12, 0, -3, 0, -5, 8, -20, -83, 0, 0, 0, 0, 9, 7, 24, -20, 41, 3, 6, 1, 15, 20, 12, 11, 17, -9, 1, -2, -26, -1, 18, -1, -12, 32, 3, -18, -5, 10, -25, -5, -2, 1, -8, 10 }, + }, + { + { 80, -49, 6, -4, 1, -1, 1, -1, -72, 36, 4, 0, 1, 0, 0, 0, 26, 0, -12, 2, -2, 1, -1, 0, -7, -9, 6, 1, 0, 0, 0, 0, 3, 5, -1, -2, -2, -2, -1, 1, 1, 1, 0, 0, -1, -1, 0, 0 }, + { -72, -6, 17, 0, 3, 0, 1, 0, -23, 58, -21, 2, -3, 1, -1, 0, 55, -46, -1, 6, -2, 1, -1, 0, -22, 7, 17, -7, 2, -1, 1, 0, 9, 5, -12, 1, -3, -4, 4, 2, 4, 1, -2, -1, -1, -1, 1, 0 }, + { -50, 19, -15, 4, -1, 1, -1, 1, -58, -2, 30, -3, 4, -1, 2, 0, 6, 57, -34, 0, -2, 0, -1, 0, 34, -48, -2, 14, -4, 3, -1, 1, -10, 7, 21, -10, 6, 1, -11, 0, -1, -1, 4, 2, 3, 0, -2, -1 }, + { -33, -43, 28, -7, 4, -2, 2, -1, -38, 11, -8, 4, 1, 1, 0, 0, -55, 24, 26, -5, 2, -1, 1, 0, 15, 46, -40, -1, -1, 0, -1, 0, 17, -38, 1, 17, -3, 11, 15, -11, 3, -1, -10, 1, 0, 1, 3, 2 }, + { 10, 66, -21, -3, -3, 0, -1, 0, -53, -41, -2, 16, -1, 4, -1, 1, 36, -5, 41, -20, 3, -3, 1, -1, -30, 26, -32, -3, 7, -2, 2, -1, 15, -8, 1, 17, -1, -2, 4, -8, 2, 0, -1, 3, 0, 0, 0, -1 }, + { 18, 14, 13, -9, 2, -2, 1, -1, 34, 32, -31, 12, -5, 2, -2, 1, 40, 4, -4, -9, -3, -2, -1, -1, 27, -31, -43, 19, -2, 3, -1, 1, 7, -49, 52, 10, -11, 22, 7, -26, -1, -6, -9, 6, -2, 2, 4, -2 }, + { 21, 66, -1, 9, -4, 2, -1, 1, -21, 41, -30, -10, 0, -2, 0, -1, -35, -17, -3, 26, -6, 5, -2, 2, 56, 3, 18, -25, -1, -2, -1, -1, -15, -13, -27, 9, 9, -6, 20, 5, -3, 2, -6, -9, 3, -3, 1, 5 }, + { 1, -6, -24, 17, -5, 3, -2, 1, 24, 10, 39, -21, 5, -4, 2, -1, 33, 32, -30, 4, -3, -1, -1, 0, -4, 13, -16, -10, 0, -1, 0, 0, 24, -26, -37, 33, 5, -32, 55, -5, -7, 22, -14, -22, 1, -9, -3, 13 }, + { 9, 33, -24, 1, 4, 0, 1, 0, 6, 50, 26, 1, -10, 0, -2, 0, -27, 1, -28, -21, 16, -5, 3, -2, -23, 36, -2, 40, -17, 4, -3, 1, 43, -13, 4, -41, -19, -2, -24, 17, 11, -4, 8, 4, -3, -3, -3, -3 }, + { -7, -9, -32, 14, -3, 3, -1, 1, -23, -28, 0, -5, -1, 0, 0, 0, -36, -59, -24, 14, 4, 2, 1, 1, -23, -26, 23, 26, -3, 5, 0, 2, 10, -26, 38, 7, -12, 11, 42, -22, -5, 20, -14, -15, -1, -2, 1, 6 }, + { 6, 30, 69, -18, 5, -4, 3, -1, -3, -11, -34, -16, 9, -4, 2, -1, -16, 35, -35, 30, -9, 3, -2, 1, -57, -13, 6, 4, -5, 5, -1, 1, 28, 10, 4, 7, 0, -15, 7, -10, -1, 7, -2, 2, 1, -3, 0, 0 }, + { 1, -8, 24, -3, 7, -2, 2, -1, -6, -51, -6, -4, -5, 0, -1, 0, 38, -1, 0, 25, 6, 2, 1, 1, 47, 20, 35, 1, -27, 1, -5, 0, 37, -37, -9, -47, -28, 5, 0, 18, 8, 6, 0, -8, -4, -3, -3, 1 }, + { 4, 10, 4, 17, -9, 4, -2, 1, 5, 14, 32, -15, 9, -3, 2, -1, 7, 13, 19, 15, -8, 1, -1, 0, 3, 25, 30, -18, 1, -2, 0, -1, 11, 24, 22, -11, -3, 37, -13, -58, -5, 12, -63, 26, 9, -15, 11, 8 }, + { -3, -9, -23, 10, -10, 3, -3, 1, -5, -14, -16, -27, 13, -5, 2, -1, -1, -13, -30, 11, -5, 2, -1, 0, -5, -8, -22, -16, 10, 0, 1, 0, 0, -29, -27, 6, -27, -10, -30, 9, -3, -10, -7, 77, 9, -13, 45, -8 }, + { 2, 11, 22, 2, 9, -2, 2, 0, -6, -7, 20, -32, -3, -4, 0, -1, 13, -5, -28, 6, 18, -4, 3, -1, -26, 27, -14, 6, -20, 0, -2, 0, -76, -26, -4, -7, 12, 51, 5, 24, 7, -17, -16, -12, -5, 4, 2, 13 }, + { 2, -3, 8, 14, -5, 3, -1, 1, -2, -11, 5, -18, 8, -3, 2, -1, 12, -23, -19, 22, 2, 0, 1, 0, 23, 41, -7, 35, -10, 4, -1, 1, 5, 7, 23, 5, 69, -38, -8, -32, -15, -31, 24, 11, 2, 18, 11, -15 }, + } + }, + { //2 + { + { -121, 33, 4, 4, 1, 2, 0, 1, -1, -1, 1, 0, 0, 0, 0, 0, 24, -5, -1, -1, 0, 0, 0, 0, 5, -1, 0, 0, 0, 0, 0, 0, 3, -1, 0, 0, 2, -1, 0, 0, 2, -1, 0, 0, 1, 0, 0, 0 }, + { 0, -2, 0, 0, 0, 0, 0, 0, 121, -23, -7, -3, -2, -1, -1, 0, 17, 1, -2, 0, 0, 0, 0, 0, -27, 4, 2, 0, 0, 0, 0, 0, -12, 2, 1, 0, -5, 1, 0, 0, -1, 0, 0, 0, -2, 0, 0, 0 }, + { -20, 19, -5, 2, -1, 1, 0, 0, 16, 3, -2, 0, 0, 0, 0, 0, -120, 14, 8, 1, 3, 1, 1, 0, -18, -2, 3, 0, 1, 0, 0, 0, 17, -3, -1, 0, 6, -1, -1, 0, 2, 0, 0, 0, 2, 0, 0, 0 }, + { 32, 108, -43, 10, -9, 3, -3, 1, 4, 19, -7, 1, -1, 0, 0, 0, 11, -30, 9, -2, 1, -1, 0, 0, 0, -8, 2, 0, 0, 0, 0, 0, -7, -1, 2, 0, -3, -1, 1, 0, -2, -2, 1, 0, 0, 0, 0, 0 }, + { -3, 0, -1, 0, 0, 0, 0, 0, -29, 11, -2, 1, 0, 0, 0, 0, 12, 7, -1, 0, 0, 0, 0, 0, -117, 12, 9, 1, 3, 0, 1, 0, -32, -3, 3, 0, 12, -2, -1, 0, 7, 0, 0, 0, 1, 0, 0, 0 }, + { -4, -12, -3, 1, -1, 0, 0, 0, 19, 105, -31, 7, -6, 1, -2, 0, 9, 46, -6, 0, 0, 0, 0, 0, 8, -29, 9, -3, 1, 0, 0, 0, -3, -19, 3, 0, -4, -6, 1, 0, 0, 0, 0, 0, 0, -1, 0, 0 }, + { 7, 1, 2, 0, 0, 0, 0, 0, 4, 3, -2, 0, 0, 0, 0, 0, 22, -8, 1, -1, 0, 0, 0, 0, -28, -9, 4, 0, 1, 0, 0, 0, 117, -10, -8, 0, 32, 1, -4, 0, 3, 1, -1, 0, -3, 1, 0, 0 }, + { -8, -31, 14, -4, 3, -1, 1, 0, 9, 43, 0, 1, -1, 0, 0, 0, -13, -105, 17, -2, 2, 0, 0, 0, -8, -25, -3, 0, 0, 0, 0, 0, -7, 32, -5, 1, -1, 4, 0, 0, 2, -1, 0, 0, 1, 0, -1, 0 }, + { -15, -43, -100, 23, -12, 6, -4, 2, -6, -17, -48, 10, -5, 2, -1, 1, 1, -5, 19, -6, 3, -1, 1, 0, 2, 7, 15, -3, 1, -1, 0, 0, 4, 10, 5, -1, 0, 3, 1, 0, -2, 1, 2, 0, -1, 1, 1, 0 }, + { -3, 1, 2, 0, 0, 0, 0, 0, -6, 3, 1, 0, 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 0, -20, 8, -2, 0, 0, 0, 0, 0, 30, 13, -3, 0, -116, 6, 10, 0, -35, -5, 4, 0, -3, -1, 0, 0 }, + { -1, -6, -3, 2, -1, 0, 0, 0, -6, -35, 9, 0, 2, 0, 0, 0, 1, -6, 11, -2, 2, 0, 1, 0, -9, -100, 17, -1, 1, 0, 0, 0, -10, -63, 1, 2, -17, 3, -4, 0, -1, 9, -1, 0, 3, 4, -1, 0 }, + { -5, -14, -48, 2, -5, 1, -2, 0, 10, 24, 99, -17, 10, -4, 3, -1, 4, 14, 32, 0, 2, 0, 1, 0, -4, 0, -39, 6, -4, 1, -1, 0, 2, -3, -4, 0, 2, -2, -2, 0, 0, 0, -1, 0, 0, -1, -1, 0 }, + { -2, 0, 2, 0, 0, 0, 0, 0, -2, 0, 1, 0, 0, 0, 0, 0, -1, -1, 1, -1, 0, 0, 0, 0, -1, -4, 2, 0, 0, 0, 0, 0, -8, -2, -1, 1, 30, 4, -4, 1, -102, 4, 8, -1, -69, -2, 6, -1 }, + { -2, -10, -4, 0, 0, 0, 0, 0, 3, 11, -1, -1, 0, 0, 0, 0, -6, -40, -15, 6, -2, 1, 0, 0, 5, 57, -6, 2, 0, 0, 0, 0, 1, -95, 18, -6, -10, -34, -2, 0, -4, 17, -2, 0, 0, 2, 1, 0 }, + { -2, -3, -25, -2, -3, 0, -1, 0, -1, -3, -1, 4, -2, 2, 0, 1, -7, -8, -97, 17, -9, 3, -3, 1, -8, -26, -61, -1, -3, -1, -1, -1, 2, 10, 24, -7, 5, 9, 19, -1, 0, 1, 4, 0, -2, 0, 1, 0 }, + { 4, -4, 28, 103, -42, 24, -9, 7, 1, 2, 4, 0, 3, -1, 0, 0, -1, 0, -9, -42, 17, -9, 3, -2, -1, 1, -14, 6, -4, 2, -1, 0, -1, -2, -4, 4, 0, 3, 1, -1, 0, 2, 0, -2, 2, 0, 0, 0 }, + }, + { + { 87, -41, 3, -4, 1, -1, 0, -1, -73, 28, 2, 1, 1, 1, 0, 0, 30, -5, -6, 1, -1, 0, 0, 0, -8, -3, 3, 0, 0, 0, 0, 0, 3, 2, -1, 0, -2, -1, 0, 0, 1, 1, 0, 0, -1, 0, 0, 0 }, + { -75, 4, 7, 0, 2, 0, 1, 0, -41, 36, -7, 3, -1, 1, 0, 0, 72, -29, -2, 0, -1, 0, -1, 0, -37, 6, 7, -2, 1, 0, 0, 0, 12, 3, -4, 0, -3, -2, 1, 0, 4, 0, 0, 0, -1, 0, 0, 0 }, + { 26, -44, 22, -6, 4, -2, 1, -1, 77, 24, -22, 2, -4, 0, -1, 0, 7, -38, 10, 0, 1, 0, 0, 0, -51, 27, 4, -3, 2, -1, 1, 0, 31, -5, -8, 3, -14, 0, 5, -1, 6, 1, -3, 0, -4, -1, 1, 0 }, + { -39, -68, 37, -7, 6, -2, 2, 0, -9, 56, -21, 1, -2, 0, -1, 0, -45, 4, -3, 6, -1, 2, 0, 1, 49, -13, 3, -3, -1, 0, 0, 0, -19, 2, 0, 0, 5, 1, 1, 0, -2, 0, -1, 0, 1, 0, 0, 0 }, + { 10, -20, 2, 0, 1, 0, 0, 0, 50, -1, 8, -5, 1, -1, 0, 0, 66, 17, -24, 4, -3, 1, -1, 0, 13, -49, 15, 1, 0, 0, 0, 0, -53, 34, 6, -5, 30, -7, -11, 3, -11, -2, 5, 1, 4, 2, -1, -1 }, + { -21, -45, 8, -2, 3, -1, 1, 0, -7, -30, 26, -8, 3, -1, 1, -1, -9, 69, -33, 5, -2, 0, -1, 0, -44, -31, 10, 7, -2, 2, 0, 1, 49, 7, 2, -6, -23, -3, -2, 2, 9, 4, 0, 0, -2, -1, -1, 0 }, + { -4, -2, -55, 28, -8, 5, -3, 2, -2, 37, 43, -19, 1, -2, 1, -1, -47, -34, -27, 5, 4, -1, 1, 0, -39, -2, 27, 4, -2, 1, 0, 0, -11, 32, -8, -7, 27, -12, -6, 6, -13, 0, 4, -3, 3, -1, -2, 1 }, + { 2, 19, 47, -23, 6, -4, 2, -1, -23, -22, -44, 17, -2, 2, -1, 0, -33, 3, 22, -2, -4, 1, -1, 0, -58, -17, 6, -6, 7, -1, 1, 0, -23, 40, -2, 5, 43, -11, -8, -1, -18, -4, 5, 2, 4, 3, 0, -1 }, + { -19, -62, -9, 3, 0, 0, 0, 0, -12, -56, 27, -7, 3, -1, 1, 0, 7, -8, 16, -6, 4, -2, 1, -1, -15, 54, -23, 2, -1, 0, 0, 0, -42, -25, 4, 6, 34, 8, 2, -2, -15, -1, 0, -1, 3, 2, 0, 1 }, + { 1, 9, -5, 0, -1, 0, 0, 0, 0, 22, -1, 2, 0, 1, 0, 0, -13, 17, 0, -2, 0, -1, 0, 0, -46, -10, -10, 4, -1, 1, 0, 0, -80, -27, 20, -4, -66, 23, -2, -2, 20, -3, -2, 3, -14, 2, 3, -1 }, + { 5, 17, -9, 0, -2, 1, 0, 0, 13, 54, -2, 7, -1, 1, 0, 0, 4, 51, -3, -6, -1, -1, 0, 0, -20, 6, -34, 9, -2, 2, -1, 0, 16, -52, 28, 1, 59, 15, -8, -5, -28, -7, 2, 2, 10, 3, 0, -1 }, + { 7, 27, 56, -2, 10, -3, 3, -1, -2, -6, 8, -28, 3, -4, 1, -1, -1, -4, -68, 35, -5, 5, -2, 1, 0, 35, 43, -4, -6, 1, -1, 0, -14, -38, -12, -10, 9, 5, 7, 6, -9, 7, -4, -3, 4, -4, 0, 3 }, + { 0, 0, 19, -4, 3, -2, 2, -1, -3, -13, 10, -4, 1, 0, 0, 0, -6, -37, -18, -5, 2, -2, 1, -1, 6, -6, -7, 25, -6, 4, -1, 1, 16, 10, 55, -24, 15, 46, -52, 1, 35, -43, 10, 12, -23, 13, 5, -8 }, + { -3, 0, -27, -80, 40, -16, 6, -4, 4, 3, 31, 61, -22, 7, -1, 1, -4, -7, -26, -6, -10, 6, -4, 1, 3, 8, 14, -18, 15, -5, 2, -1, -2, -4, -1, 13, 0, 2, -4, -3, 3, -1, 2, 1, -2, 0, -2, -1 }, + { 1, 2, -8, 6, -1, 1, 0, 0, 2, 8, -5, -1, 0, 0, 0, 0, 1, 24, 3, 5, -1, 1, 0, 0, -3, 12, 6, -10, 1, -1, 0, 0, -9, -1, -25, 10, 45, -11, 18, 2, 86, 1, -13, -4, -65, -6, 7, 2 }, + { -4, -18, -57, 8, -8, 1, -3, 0, -5, -20, -69, 7, -6, 2, -2, 1, 1, 4, 0, 33, -7, 5, -2, 1, 0, -9, 53, -22, 3, -1, 0, 0, 4, -27, -2, -9, 5, 36, -13, 5, -7, -17, 1, 2, 4, 6, 4, -1 }, + } + }, + { //3 + { + { -115, 37, 9, 2, 2, 1, 1, 0, 10, -29, 8, 0, 1, 0, 1, 0, 23, -8, -8, 1, -1, 0, 0, 0, 3, 3, -2, -1, 0, 0, 0, 0, 4, 0, 0, -1, 1, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0 }, + { 15, 51, -18, 0, -3, 0, -1, 0, -95, 7, 34, -3, 5, -1, 2, 0, 23, -47, 1, 6, 0, 1, 0, 1, 8, 5, -12, 0, -1, 0, 0, 0, 3, -3, 1, -1, 2, 1, -2, 0, 1, -1, 0, 0, 1, 1, -1, 0 }, + { 29, -22, 16, -6, 3, -2, 1, -1, -4, -80, 12, 15, 0, 3, 0, 1, 45, 7, -59, 7, -2, 1, -1, 0, -15, 41, -3, -16, 2, -3, 0, -1, 1, 0, 7, -2, -3, 6, 1, -2, 0, 0, 1, 0, -1, 2, 0, -1 }, + { -36, -98, 25, 5, 4, 1, 2, 1, -59, 11, -17, 1, 1, 1, 0, 0, 6, -13, 7, -3, 0, 0, 0, 0, 14, -4, -14, 3, -1, 0, 0, 0, 2, 8, -3, -5, 2, 0, 0, 0, 0, 3, 0, -1, 1, 0, 0, 0 }, + { -6, 18, 3, -3, -1, 0, 0, 0, -50, -5, -38, 12, 0, 2, 0, 1, 3, 67, -7, -40, 3, -6, 1, -3, -12, -13, 65, -3, -10, 0, -1, 0, 9, -20, -5, 22, -2, 0, 0, -1, 2, -3, -2, 3, -1, 0, 1, 0 }, + { 4, 15, 52, -13, 5, -3, 2, -1, -17, -45, 16, 24, -2, 4, -1, 2, -87, -8, -14, 7, 8, 1, 2, 0, 23, -35, -6, -3, 1, 1, 0, 0, 2, 5, -17, 0, 3, -1, -1, -5, 0, 1, -4, 0, 1, 0, 0, -2 }, + { -20, -7, -43, 4, 0, 1, -1, 1, -7, 35, 0, 12, -4, 1, -1, 0, -51, -2, -57, 5, 15, 0, 4, 0, 7, 39, 5, -55, 1, -7, 1, -3, 1, -10, 41, 2, 4, -3, -2, 3, -1, -2, 7, 1, 1, -1, -1, 0 }, + { 4, 29, 1, 26, -5, 4, -2, 1, -17, -7, -73, 6, 6, 2, 1, 1, -5, 21, -3, 5, -1, -3, 0, -1, -11, 2, -52, -3, 27, -2, 5, 0, 0, 27, 8, -58, 2, -5, 25, 3, 0, 3, 0, -5, 0, -2, 7, 0 }, + { 12, 13, 10, 2, -1, 3, -1, 1, 17, -2, -46, 12, 7, 0, 2, 0, 16, -45, -9, -53, 6, 1, 1, 0, 70, 16, 8, -4, -37, 1, -7, 0, -12, 29, 3, 21, 4, 0, 5, -1, -3, 4, 1, 4, 2, 0, 1, 0 }, + { 5, 20, 90, -17, 4, -3, 2, -1, 6, 66, 8, 28, -7, 3, -1, 1, 29, 5, -19, 12, 9, -1, 1, 0, -10, 14, -1, -13, 7, 0, 1, 0, 0, -6, 13, -4, 0, -4, 1, 5, 0, -1, -1, 1, 0, -1, 0, 0 }, + { -3, -4, -34, -12, 2, -1, -1, 0, 5, 25, 11, 43, -10, 4, -2, 1, 23, 20, -40, 12, 21, -3, 4, -1, 25, -28, -10, 5, 8, 6, 0, 2, -4, 21, -64, -8, -5, 19, 10, -48, 3, -1, 10, -3, 0, 4, 3, -6 }, + { -1, -3, 2, 19, -2, 4, -1, 2, 9, 3, -35, 22, 11, 1, 2, 0, -7, -65, -19, -22, 11, 4, 2, 1, -75, -18, 3, -1, -10, 2, 0, 1, 2, -35, -27, 4, 1, 8, -17, -19, 3, 0, 3, -6, 0, 2, -1, -2 }, + { 10, -4, -6, 12, 5, 1, 1, 0, 11, -9, -12, -2, -7, 0, -1, 0, 33, -10, -4, 18, 18, -4, 4, -1, 28, -72, 1, -49, 15, 2, 2, 1, 56, -23, 22, -1, 4, -1, -15, 26, 6, 4, -10, 0, 0, 2, -3, 2 }, + { 4, 6, 14, 53, -4, 4, 0, 2, 0, -1, -20, -13, 3, 2, -1, 1, -3, 1, -5, 35, -16, -6, -1, -2, 46, 29, 13, 21, 37, -5, 4, -1, -10, -53, -18, 8, 9, 12, -41, -25, -2, 2, 13, -16, 4, 1, -5, 1 }, + { 2, 9, 13, 37, 19, 6, 2, 2, -9, -3, -9, -28, -20, -4, -3, -1, 1, 18, 9, 28, 24, 6, 2, 2, -20, -5, -25, -33, -36, 9, -2, 2, -13, 42, 1, 57, -22, -2, -25, -28, 5, 6, 19, -12, -5, -3, -2, 4 }, + { 3, -3, 12, 84, -12, 8, -2, 3, 6, 13, 50, -1, 45, 1, 7, 0, -2, 18, -22, -37, -13, 14, 0, 3, 1, -12, -3, 2, -15, -8, 1, -1, 19, 14, -4, -12, -4, 5, 17, 8, 2, -4, -4, 4, -2, 2, 1, 0 }, + }, + { + { 109, -26, -8, -3, -2, -1, -1, 0, -50, 28, 2, 1, 0, 0, 0, 0, -18, -8, 6, 0, 1, 0, 1, 0, 6, -2, -3, 0, 0, 0, 0, 0, -3, 2, 1, -1, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0 }, + { -39, 31, -5, 2, -1, 1, 0, 0, -95, 6, 18, 0, 4, 0, 1, 0, 32, -49, 5, 1, 1, 0, 0, 0, 27, -1, -14, 2, -2, 1, -1, 0, 3, 5, -3, -2, 4, 1, -1, -1, 2, 0, 0, 0, 2, 0, 0, 0 }, + { 29, -3, -2, -2, 0, 0, 0, 0, 0, -41, 9, 0, 2, 0, 1, 0, 86, 4, -33, 2, -6, 1, -2, 0, -32, 58, 1, -7, 0, -2, 0, -1, -14, -8, 20, 0, -2, -3, 0, 4, -1, -1, 0, 0, -1, 1, 0, 0 }, + { 18, 96, -23, 2, -5, 1, -2, 0, -10, 6, 10, -2, 1, -1, 1, 0, -14, 26, 2, -4, 1, -1, 0, 0, -43, -9, 35, -2, 4, -1, 1, 0, 14, -40, 1, 10, 2, 1, -10, 1, 2, -4, -1, -1, 0, 0, -1, 0 }, + { -29, -60, 16, -2, 3, -1, 1, 0, -52, 9, -17, 5, -2, 1, -1, 1, 13, 56, -2, -9, 0, -2, 0, -1, -34, -18, 41, 0, 3, 0, 1, 0, 19, -36, -10, 13, 3, 6, -14, -1, 3, 1, -1, -3, 1, 1, -1, -1 }, + { -23, -5, -15, 5, -2, 1, -1, 1, 2, 79, -13, -4, -2, -1, -1, 0, -9, 1, 5, -1, 1, 0, 0, 0, -4, 49, 2, -14, 1, -3, 0, -1, -31, -14, 56, -1, 13, -37, -4, 20, -2, 2, -10, 0, 2, -4, 0, -1 }, + { -7, -3, 12, -3, 3, -1, 1, 0, -31, -62, 8, 7, 0, 2, 0, 1, -75, 9, -45, 5, -1, 1, -1, 0, 14, 35, 0, -23, 2, -5, 1, -2, 1, -8, 32, -1, 7, -12, -4, 10, 0, 2, -6, -1, 2, 0, 0, -2 }, + { 1, -26, 5, 0, 1, 0, 1, 0, 24, -3, 43, -6, 4, -2, 1, -1, -7, -64, 9, 14, 0, 3, 0, 1, -12, -4, 5, 3, -1, 1, 0, 0, 8, -59, -3, 26, 14, 6, -58, 6, -5, 17, -7, -18, 3, 3, -1, -5 }, + { 11, 14, 6, -3, 1, -1, 1, 0, 10, -7, -9, 3, -2, 1, -1, 0, 22, 21, 1, -21, 2, -4, 1, -2, 92, 1, 53, 0, -9, 1, -2, 0, -21, -11, 1, 40, -5, -4, -24, 5, -4, 5, -6, -5, 0, 0, 0, -3 }, + { -10, -11, -47, 3, -4, 1, -1, 0, 5, 28, 11, -2, -1, 0, 0, 0, -12, -2, -38, 2, 0, 1, 0, 0, 16, 38, 11, -16, -1, -3, 0, -2, 12, -9, -22, 7, -8, 60, 4, -36, -6, -15, 54, 7, 3, -7, -8, 14 }, + { -8, -24, -99, 11, -10, 3, -4, 1, -5, -36, 19, -26, 4, -5, 1, -2, 0, 25, 41, 5, -3, 1, 0, 0, 10, -5, -7, 12, 2, 1, 0, 0, -1, 1, 9, -3, -3, -14, -3, 12, 2, 4, -13, -2, -1, 3, 2, -4 }, + { -5, 1, -1, 0, 1, 0, 0, 0, -10, -14, -6, 8, 0, 1, 0, 0, -17, -2, 7, -5, 3, -1, 0, 0, -16, 13, 3, 31, -1, 6, 0, 2, -93, -15, -46, -3, 23, -19, 0, -47, 8, 4, 8, 3, 2, 3, 0, 0 }, + { 1, 12, -20, 21, -4, 5, -2, 2, -5, -2, -75, 9, -1, 2, -1, 1, -1, -2, -16, -4, 0, -1, 0, 0, -7, 7, -31, 0, 3, 0, 0, 0, 4, 11, -12, 4, -12, 14, -50, -1, -8, 32, -4, -54, 2, 0, 30, -15 }, + { 2, -9, -18, 8, -3, 3, -1, 1, 3, -25, -62, -6, 0, -2, 0, -1, -6, -61, 14, -51, 2, -6, 0, -2, -19, 0, 40, -7, -17, 0, -3, 0, 13, -4, 11, 9, 17, 0, 24, 5, 1, -12, 4, 28, 0, 0, -15, 8 }, + { 4, 9, 39, 18, 0, 2, 0, 1, -6, -16, -22, -37, 5, -5, 1, -2, -5, 15, 63, 9, -16, 0, -3, 0, 18, 42, -18, 27, 15, 1, 3, 1, 12, -34, 9, -24, 4, 28, -2, 4, -11, -4, 30, 2, 5, -13, -4, 18 }, + { -7, -2, 15, -6, 1, -1, 1, -1, -11, -3, 22, -14, 0, -2, 1, -1, -18, -7, 30, -9, -4, 0, -1, 0, -35, 23, 23, 10, -17, 1, -3, 0, -19, 53, 6, 48, -65, 12, -12, 11, -8, -16, 10, -21, -2, -12, 6, 2 }, + } + } +}; + +const int8_t ff_vvc_lfnst_4x4[4][2][16][16] = { + { //0 + { + { 108, -44, -15, 1, -44, 19, 7, -1, -11, 6, 2, -1, 0, -1, -1, 0 }, + { -40, -97, 56, 12, -11, 29, -12, -3, 18, 18, -15, -3, -1, -3, 2, 1 }, + { 25, -31, -1, 7, 100, -16, -29, 1, -54, 21, 14, -4, -7, 2, 4, 0 }, + { -32, -39, -92, 51, -6, -16, 36, -8, 3, 22, 18, -15, 4, 1, -5, 2 }, + { 8, -9, 33, -8, -16, -102, 36, 23, -4, 38, -27, -5, 5, 16, -8, -6 }, + { -25, 5, 16, -3, -38, 14, 11, -3, -97, 7, 26, 1, 55, -10, -19, 3 }, + { 8, 9, 16, 1, 37, 36, 94, -38, -7, 3, -47, 11, -6, -13, -17, 10 }, + { 2, 34, -5, 1, -7, 24, -25, -3, 8, 99, -28, -29, 6, -43, 21, 11 }, + { -16, -27, -39, -109, 6, 10, 16, 24, 3, 19, 10, 24, -4, -7, -2, -3 }, + { -9, -10, -34, 4, -9, -5, -29, 5, -33, -26, -96, 33, 14, 4, 39, -14 }, + { -13, 1, 4, -9, -30, -17, -3, -64, -35, 11, 17, 19, -86, 6, 36, 14 }, + { 8, -7, -5, -15, 7, -30, -28, -87, 31, 4, 4, 33, 61, -5, -17, 22 }, + { -2, 13, -6, -4, -2, 28, -13, -14, -3, 37, -15, -3, -2, 107, -36, -24 }, + { 4, 9, 11, 31, 4, 9, 16, 19, 12, 33, 32, 94, 12, 0, 34, -45 }, + { 2, -2, 8, -16, 8, 5, 28, -17, 6, -7, 18, -45, 40, 36, 97, -8 }, + { 0, -2, 0, -10, -1, -7, -3, -35, -1, -7, -2, -32, -6, -33, -16, -112 }, + }, + { + { 119, -30, -22, -3, -23, -2, 3, 2, -16, 3, 6, 0, -3, 2, 1, 0 }, + { -27, -101, 31, 17, -47, 2, 22, 3, 19, 30, -7, -9, 5, 3, -5, -1 }, + { 0, 58, 22, -15, -102, 2, 38, 2, 10, -13, -5, 4, 14, -1, -9, 0 }, + { 23, 4, 66, -11, 22, 89, -2, -26, 13, -8, -38, -1, -9, -20, -2, 8 }, + { -19, -5, -89, 2, -26, 76, -11, -17, 20, 13, 18, -4, 1, -15, 3, 5 }, + { -10, -1, -1, 6, 23, 25, 87, -7, -74, 4, 39, -5, 0, -1, -20, -1 }, + { -17, -28, 12, -8, -32, 14, -53, -6, -68, -67, 17, 29, 2, 6, 25, 4 }, + { 1, -24, -23, 1, 17, -7, 52, 9, 50, -92, -15, 27, -15, -10, -6, 3 }, + { -6, -17, -2, -111, 7, -17, 8, -42, 9, 18, 16, 25, -4, 2, -1, 11 }, + { 9, 5, 35, 0, 6, 21, -9, 34, 44, -3, 102, 11, -7, 13, 11, -20 }, + { 4, -5, -5, -10, 15, 19, -2, 6, 6, -12, -13, 6, 95, 69, -29, -24 }, + { -6, -4, -9, -39, 1, 22, 0, 102, -19, 19, -32, 30, -16, -14, -8, -23 }, + { 4, -4, 7, 8, 4, -13, -18, 5, 0, 0, 21, 22, 58, -88, -54, 28 }, + { -4, -7, 0, -24, -7, 0, -25, 3, -3, -30, 8, -76, -34, 4, -80, -26 }, + { 0, 6, 0, 30, -6, 1, -13, -23, 1, 20, -2, 80, -44, 37, -68, 1 }, + { 0, 0, -1, 5, -1, -7, 1, -34, -2, 3, -6, 19, 5, -38, 11, -115 }, + } + }, + { //1 + { + { -111, 39, 4, 3, 44, 11, -12, -1, 7, -16, -5, 2, 3, -1, 4, 2 }, + { -47, -27, 15, -1, -92, 43, 20, -2, 20, 39, -16, -5, 10, -5, -13, 2 }, + { -35, -23, 4, 4, -17, -72, 32, 6, -59, 18, 50, -6, 0, 40, 0, -13 }, + { 13, 93, -27, -4, -48, 13, -34, 4, -52, 11, 1, 10, 3, 16, -3, 1 }, + { -11, -27, 1, 2, -47, -4, -36, 10, -2, -85, 14, 29, -20, -2, 57, 4 }, + { 0, -35, 32, -2, 26, 60, -3, -17, -82, 1, -30, 0, -37, 21, 3, 12 }, + { -17, -46, -92, 14, 7, -10, -39, 29, -17, 27, -28, 17, 1, -15, -13, 17 }, + { 4, -10, -23, 4, 16, 58, -17, 26, 30, 21, 67, 2, -13, 59, 13, -40 }, + { 5, -20, 32, -5, 8, -3, -46, -7, -4, 2, -15, 24, 100, 44, 0, 5 }, + { -4, -1, 38, -18, -7, -42, -63, -6, 33, 34, -23, 15, -65, 33, -20, 2 }, + { -2, -10, 35, -19, 5, 8, -44, 14, -25, 25, 58, 17, 7, -84, -16, -18 }, + { 5, 13, 18, 34, 11, -4, 18, 18, 5, 58, -3, 42, -2, -10, 85, 38 }, + { -5, -7, -34, -83, 2, -1, -4, -73, 4, 20, 15, -12, 4, -3, 44, 12 }, + { 0, 4, -2, -60, 5, 9, 42, 34, 5, -14, 9, 80, -5, 13, -38, 37 }, + { -1, 2, 7, -57, 3, -7, 9, 68, -9, 6, -49, -20, 6, -4, 36, -64 }, + { -1, 0, -12, 23, 1, -4, 17, -53, -3, 4, -21, 72, -4, -8, -3, -83 }, + }, + { + { 88, -55, 6, -3, -66, 27, 9, -2, 11, 11, -13, 1, -2, -7, 1, 2 }, + { -58, -20, 27, -2, -27, 75, -29, 0, 47, -42, -11, 11, -9, -3, 19, -4 }, + { -51, 23, -22, 5, -63, 3, 37, -5, 1, 64, -35, -4, 29, -31, -11, 13 }, + { -27, -76, 49, -2, 40, 14, 9, -17, -56, 36, -25, 6, 14, 3, -6, 8 }, + { 19, -4, -36, 22, 52, 7, 36, -23, 28, -17, -64, 15, -5, -44, 48, 9 }, + { 29, 50, 13, -10, 1, 34, -59, 1, -51, 4, -16, 30, 52, -33, 24, -5 }, + { -12, -21, -74, 43, -13, 39, 18, -5, -58, -35, 27, -5, 19, 26, 6, -5 }, + { 19, 38, -10, -5, 28, 66, 0, -5, -4, 19, -30, -26, -40, 28, -60, 37 }, + { -6, 27, 18, -5, -37, -18, 12, -25, -44, -10, -38, 37, -66, 45, 40, -7 }, + { -13, -28, -45, -39, 0, -5, -39, 69, -23, 16, -12, -18, -50, -31, 24, 13 }, + { -1, 8, 24, -51, -15, -9, 44, 10, -28, -70, -12, -39, 24, -18, -4, 51 }, + { -8, -22, -17, 33, -18, -45, -57, -27, 0, -31, -30, 29, -2, -13, -53, 49 }, + { 1, 12, 32, 51, -8, 8, -2, -31, -22, 4, 46, -39, -49, -67, 14, 17 }, + { 4, 5, 24, 60, -5, -14, -23, 38, 9, 8, -34, -59, 24, 47, 42, 28 }, + { -1, -5, -20, -34, 4, 4, -15, -46, 18, 31, 42, 10, 10, 27, 49, 78 }, + { -3, -7, -22, -34, -5, -11, -36, -69, -1, -3, -25, -73, 5, 4, 4, -49 }, + } + }, + { //2 + { + { -112, 47, -2, 2, -34, 13, 2, 0, 15, -7, 1, 0, 8, -3, -1, 0 }, + { 29, -7, 1, -1, -108, 40, 2, 0, -45, 13, 4, -1, 8, -5, 1, 0 }, + { -36, -87, 69, -10, -17, -33, 26, -2, 7, 14, -11, 2, 6, 8, -7, 0 }, + { 28, -5, 2, -2, -29, 13, -2, 0, 103, -36, -4, 1, 48, -16, -4, 1 }, + { -12, -24, 15, -3, 26, 80, -61, 9, 15, 54, -36, 2, 0, -4, 6, -2 }, + { 18, 53, 69, -74, 14, 24, 28, -30, -6, -7, -11, 12, -5, -7, -6, 8 }, + { 5, -1, 2, 0, -26, 6, 0, 1, 45, -9, -1, 0, -113, 28, 8, -1 }, + { -13, -32, 18, -2, 15, 34, -27, 7, -25, -80, 47, -1, -16, -50, 28, 2 }, + { -4, -13, -10, 19, 18, 46, 60, -48, 16, 33, 60, -48, 1, 0, 5, -2 }, + { 15, 33, 63, 89, 8, 15, 25, 40, -4, -8, -15, -8, -2, -6, -9, -7 }, + { -8, -24, -27, 15, 12, 41, 26, -29, -17, -50, -39, 27, 0, 35, -67, 26 }, + { -2, -6, -24, 13, -1, -8, 37, -22, 3, 18, -51, 22, -23, -95, 17, 17 }, + { -3, -7, -16, -21, 10, 24, 46, 75, 8, 20, 38, 72, 1, 2, 1, 7 }, + { 2, 6, 10, -3, -5, -16, -31, 12, 7, 24, 41, -16, -16, -41, -89, 49 }, + { 4, 8, 21, 40, -4, -11, -28, -57, 5, 14, 31, 70, 7, 18, 32, 52 }, + { 0, 1, 4, 11, -2, -4, -13, -34, 3, 7, 20, 47, -6, -19, -42, -101 }, + }, + { + { -99, 39, -1, 2, 65, -20, -5, 0, -15, -2, 5, -1, 0, 3, -1, 0 }, + { 58, 42, -33, 3, 33, -63, 23, -1, -55, 32, 3, -5, 21, -2, -8, 3 }, + { -15, 71, -44, 5, -58, -29, 25, 3, 62, -7, -4, -4, -19, 4, 0, 1 }, + { 46, 5, 4, -6, 71, -12, -15, 5, 52, -38, 13, -2, -63, 23, 3, -3 }, + { -14, -54, -29, 29, 25, -9, 61, -29, 27, 44, -48, 5, -27, -21, 12, 7 }, + { -3, 3, 69, -42, -11, -50, -26, 26, 24, 63, -19, -5, -18, -22, 12, 0 }, + { 17, 16, -2, 1, 38, 18, -12, 0, 62, 1, -14, 5, 89, -42, 8, -2 }, + { 15, 54, -8, 6, 6, 60, -26, -8, -30, 17, -38, 22, -43, -45, 42, -7 }, + { -6, -17, -55, -28, 9, 30, -8, 58, 4, 34, 41, -52, -16, -36, -20, 16 }, + { -2, -1, -9, -79, 7, 11, 48, 44, -13, -34, -55, 6, 12, 23, 20, -11 }, + { 7, 29, 14, -6, 12, 53, 10, -11, 14, 59, -15, -3, 5, 71, -54, 13 }, + { -5, -24, -53, 15, -3, -15, -61, 26, 6, 30, -16, 23, 13, 56, 44, -35 }, + { 4, 8, 21, 52, -1, -1, -5, 29, -7, -17, -44, -84, 8, 20, 31, 39 }, + { -2, -11, -25, -4, -4, -21, -53, 2, -5, -26, -64, 19, -8, -19, -73, 39 }, + { -3, -5, -23, -57, -2, -4, -24, -75, 1, 3, 9, -25, 6, 15, 41, 61 }, + { 1, 1, 7, 18, 1, 2, 16, 47, 2, 5, 24, 67, 3, 9, 25, 88 }, + } + }, + { //3 + { + { -114, 37, 3, 2, -22, -23, 14, 0, 21, -17, -5, 2, 5, 2, -4, -1 }, + { -19, -41, 19, -2, 85, -60, -11, 7, 17, 31, -34, 2, -11, 19, 2, -8 }, + { 36, -25, 18, -2, -42, -53, 35, 5, 46, -60, -25, 19, 8, 21, -33, -1 }, + { -27, -80, 44, -3, -58, 1, -29, 19, -41, 18, -12, -7, 12, -17, 7, -6 }, + { -11, -21, 37, -10, 44, -4, 47, -12, -37, -41, 58, 18, 10, -46, -16, 31 }, + { 15, 47, 10, -6, -16, -44, 42, 10, -80, 25, -40, 21, -23, -2, 3, -14 }, + { 13, 25, 79, -39, -13, 10, 31, -4, 49, 45, 12, -8, 3, -1, 43, 7 }, + { 16, 11, -26, 13, -13, -74, -20, -1, 5, -6, 29, -47, 26, -49, 54, 2 }, + { -8, -34, -26, 7, -26, -19, 29, -37, 1, 22, 46, -9, -81, 37, 14, 20 }, + { -6, -30, -42, -12, -3, 5, 57, -52, -2, 37, -12, 6, 74, 10, 6, -15 }, + { 5, 9, -6, 42, -15, -18, -9, 26, 15, 58, 14, 43, 23, -10, -37, 75 }, + { -5, -23, -23, 36, 3, 22, 36, 40, 27, -4, -16, 56, -25, -46, 56, -24 }, + { 1, 3, 23, 73, 8, 5, 34, 46, -12, 2, 35, -38, 26, 52, 2, -31 }, + { -3, -2, -21, -52, 1, -10, -17, 44, -19, -20, 30, 45, 27, 61, 49, 21 }, + { -2, -7, -33, -56, -4, -6, 21, 63, 15, 31, 32, -22, -10, -26, -52, -38 }, + { -5, -12, -18, -12, 8, 22, 38, 36, -5, -15, -51, -63, -5, 0, 15, 73 }, + }, + { + { -102, 22, 7, 2, 66, -25, -6, -1, -15, 14, 1, -1, 2, -2, 1, 0 }, + { 12, 93, -27, -6, -27, -64, 36, 6, 13, 5, -23, 0, -2, 6, 5, -3 }, + { -59, -24, 17, 1, -62, -2, -3, 2, 83, -12, -17, -2, -24, 14, 7, -2 }, + { -33, 23, -36, 11, -21, 50, 35, -16, -23, -78, 16, 19, 22, 15, -30, -5 }, + { 0, -38, -81, 30, 27, 5, 51, -32, 24, 36, -16, 12, -24, -8, 9, 1 }, + { 28, 38, 8, -9, 62, 32, -13, 2, 51, -32, 15, 5, -66, 28, 0, -1 }, + { 11, -35, 21, -17, 30, -18, 31, 18, -11, -36, -80, 12, 16, 49, 13, -32 }, + { -13, 23, 22, -36, -12, 64, 39, 25, -19, 23, -36, 9, -30, -58, 33, -7 }, + { -9, -20, -55, -83, 3, -2, 1, 62, 8, 2, 27, -28, 7, 15, -11, 5 }, + { -6, 24, -38, 23, -8, 40, -49, 0, -7, 9, -25, -44, 23, 39, 70, -3 }, + { 12, 17, 17, 0, 32, 27, 21, 2, 67, 11, -6, -10, 89, -22, -12, 16 }, + { 2, -9, 8, 45, 7, -8, 27, 35, -9, -31, -17, -87, -23, -22, -19, 44 }, + { -1, -9, 28, -24, -1, -10, 49, -30, -8, -7, 40, 1, 4, 33, 65, 67 }, + { 5, -12, -24, -17, 13, -34, -32, -16, 14, -67, -7, 9, 7, -74, 49, 1 }, + { 2, -6, 11, 45, 3, -10, 33, 55, 8, -5, 59, 4, 7, -4, 44, -66 }, + { -1, 1, -14, 36, -1, 2, -20, 69, 0, 0, -15, 72, 3, 4, 5, 65 }, + } + } +}; + +const uint8_t ff_vvc_lfnst_tr_set_index[95] = +{ + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 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, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +}; + +uint8_t ff_vvc_default_scale_m[64 * 64]; + +//< AlfFixFiltCoeff +const int16_t ff_vvc_alf_fix_filt_coeff[64][12] = { + { 0, 0, 2, -3, 1, -4, 1, 7, -1, 1, -1, 5 }, + { 0, 0, 0, 0, 0, -1, 0, 1, 0, 0, -1, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1 }, + { 2, 2, -7, -3, 0, -5, 13, 22, 12, -3, -3, 17 }, + { -1, 0, 6, -8, 1, -5, 1, 23, 0, 2, -5, 10 }, + { 0, 0, -1, -1, 0, -1, 2, 1, 0, 0, -1, 4 }, + { 0, 0, 3, -11, 1, 0, -1, 35, 5, 2, -9, 9 }, + { 0, 0, 8, -8, -2, -7, 4, 4, 2, 1, -1, 25 }, + { 0, 0, 1, -1, 0, -3, 1, 3, -1, 1, -1, 3 }, + { 0, 0, 3, -3, 0, -6, 5, -1, 2, 1, -4, 21 }, + { -7, 1, 5, 4, -3, 5, 11, 13, 12, -8, 11, 12 }, + { -5, -3, 6, -2, -3, 8, 14, 15, 2, -7, 11, 16 }, + { 2, -1, -6, -5, -2, -2, 20, 14, -4, 0, -3, 25 }, + { 3, 1, -8, -4, 0, -8, 22, 5, -3, 2, -10, 29 }, + { 2, 1, -7, -1, 2, -11, 23, -5, 0, 2, -10, 29 }, + { -6, -3, 8, 9, -4, 8, 9, 7, 14, -2, 8, 9 }, + { 2, 1, -4, -7, 0, -8, 17, 22, 1, -1, -4, 23 }, + { 3, 0, -5, -7, 0, -7, 15, 18, -5, 0, -5, 27 }, + { 2, 0, 0, -7, 1, -10, 13, 13, -4, 2, -7, 24 }, + { 3, 3, -13, 4, -2, -5, 9, 21, 25, -2, -3, 12 }, + { -5, -2, 7, -3, -7, 9, 8, 9, 16, -2, 15, 12 }, + { 0, -1, 0, -7, -5, 4, 11, 11, 8, -6, 12, 21 }, + { 3, -2, -3, -8, -4, -1, 16, 15, -2, -3, 3, 26 }, + { 2, 1, -5, -4, -1, -8, 16, 4, -2, 1, -7, 33 }, + { 2, 1, -4, -2, 1, -10, 17, -2, 0, 2, -11, 33 }, + { 1, -2, 7, -15, -16, 10, 8, 8, 20, 11, 14, 11 }, + { 2, 2, 3, -13, -13, 4, 8, 12, 2, -3, 16, 24 }, + { 1, 4, 0, -7, -8, -4, 9, 9, -2, -2, 8, 29 }, + { 1, 1, 2, -4, -1, -6, 6, 3, -1, -1, -3, 30 }, + { -7, 3, 2, 10, -2, 3, 7, 11, 19, -7, 8, 10 }, + { 0, -2, -5, -3, -2, 4, 20, 15, -1, -3, -1, 22 }, + { 3, -1, -8, -4, -1, -4, 22, 8, -4, 2, -8, 28 }, + { 0, 3, -14, 3, 0, 1, 19, 17, 8, -3, -7, 20 }, + { 0, 2, -1, -8, 3, -6, 5, 21, 1, 1, -9, 13 }, + { -4, -2, 8, 20, -2, 2, 3, 5, 21, 4, 6, 1 }, + { 2, -2, -3, -9, -4, 2, 14, 16, 3, -6, 8, 24 }, + { 2, 1, 5, -16, -7, 2, 3, 11, 15, -3, 11, 22 }, + { 1, 2, 3, -11, -2, -5, 4, 8, 9, -3, -2, 26 }, + { 0, -1, 10, -9, -1, -8, 2, 3, 4, 0, 0, 29 }, + { 1, 2, 0, -5, 1, -9, 9, 3, 0, 1, -7, 20 }, + { -2, 8, -6, -4, 3, -9, -8, 45, 14, 2, -13, 7 }, + { 1, -1, 16, -19, -8, -4, -3, 2, 19, 0, 4, 30 }, + { 1, 1, -3, 0, 2, -11, 15, -5, 1, 2, -9, 24 }, + { 0, 1, -2, 0, 1, -4, 4, 0, 0, 1, -4, 7 }, + { 0, 1, 2, -5, 1, -6, 4, 10, -2, 1, -4, 10 }, + { 3, 0, -3, -6, -2, -6, 14, 8, -1, -1, -3, 31 }, + { 0, 1, 0, -2, 1, -6, 5, 1, 0, 1, -5, 13 }, + { 3, 1, 9, -19, -21, 9, 7, 6, 13, 5, 15, 21 }, + { 2, 4, 3, -12, -13, 1, 7, 8, 3, 0, 12, 26 }, + { 3, 1, -8, -2, 0, -6, 18, 2, -2, 3, -10, 23 }, + { 1, 1, -4, -1, 1, -5, 8, 1, -1, 2, -5, 10 }, + { 0, 1, -1, 0, 0, -2, 2, 0, 0, 1, -2, 3 }, + { 1, 1, -2, -7, 1, -7, 14, 18, 0, 0, -7, 21 }, + { 0, 1, 0, -2, 0, -7, 8, 1, -2, 0, -3, 24 }, + { 0, 1, 1, -2, 2, -10, 10, 0, -2, 1, -7, 23 }, + { 0, 2, 2, -11, 2, -4, -3, 39, 7, 1, -10, 9 }, + { 1, 0, 13, -16, -5, -6, -1, 8, 6, 0, 6, 29 }, + { 1, 3, 1, -6, -4, -7, 9, 6, -3, -2, 3, 33 }, + { 4, 0, -17, -1, -1, 5, 26, 8, -2, 3, -15, 30 }, + { 0, 1, -2, 0, 2, -8, 12, -6, 1, 1, -6, 16 }, + { 0, 0, 0, -1, 1, -4, 4, 0, 0, 0, -3, 11 }, + { 0, 1, 2, -8, 2, -6, 5, 15, 0, 2, -7, 9 }, + { 1, -1, 12, -15, -7, -2, 3, 6, 6, -1, 7, 30 }, +}; + +//< AlfClassToFiltMap +const uint8_t ff_vvc_alf_class_to_filt_map[16][25] = { + { 8, 2, 2, 2, 3, 4, 53, 9, 9, 52, 4, 4, 5, 9, 2, 8, 10, 9, 1, 3, 39, 39, 10, 9, 52 }, + { 11, 12, 13, 14, 15, 30, 11, 17, 18, 19, 16, 20, 20, 4, 53, 21, 22, 23, 14, 25, 26, 26, 27, 28, 10 }, + { 16, 12, 31, 32, 14, 16, 30, 33, 53, 34, 35, 16, 20, 4, 7, 16, 21, 36, 18, 19, 21, 26, 37, 38, 39 }, + { 35, 11, 13, 14, 43, 35, 16, 4, 34, 62, 35, 35, 30, 56, 7, 35, 21, 38, 24, 40, 16, 21, 48, 57, 39 }, + { 11, 31, 32, 43, 44, 16, 4, 17, 34, 45, 30, 20, 20, 7, 5, 21, 22, 46, 40, 47, 26, 48, 63, 58, 10 }, + { 12, 13, 50, 51, 52, 11, 17, 53, 45, 9, 30, 4, 53, 19, 0, 22, 23, 25, 43, 44, 37, 27, 28, 10, 55 }, + { 30, 33, 62, 51, 44, 20, 41, 56, 34, 45, 20, 41, 41, 56, 5, 30, 56, 38, 40, 47, 11, 37, 42, 57, 8 }, + { 35, 11, 23, 32, 14, 35, 20, 4, 17, 18, 21, 20, 20, 20, 4, 16, 21, 36, 46, 25, 41, 26, 48, 49, 58 }, + { 12, 31, 59, 59, 3, 33, 33, 59, 59, 52, 4, 33, 17, 59, 55, 22, 36, 59, 59, 60, 22, 36, 59, 25, 55 }, + { 31, 25, 15, 60, 60, 22, 17, 19, 55, 55, 20, 20, 53, 19, 55, 22, 46, 25, 43, 60, 37, 28, 10, 55, 52 }, + { 12, 31, 32, 50, 51, 11, 33, 53, 19, 45, 16, 4, 4, 53, 5, 22, 36, 18, 25, 43, 26, 27, 27, 28, 10 }, + { 5, 2, 44, 52, 3, 4, 53, 45, 9, 3, 4, 56, 5, 0, 2, 5, 10, 47, 52, 3, 63, 39, 10, 9, 52 }, + { 12, 34, 44, 44, 3, 56, 56, 62, 45, 9, 56, 56, 7, 5, 0, 22, 38, 40, 47, 52, 48, 57, 39, 10, 9 }, + { 35, 11, 23, 14, 51, 35, 20, 41, 56, 62, 16, 20, 41, 56, 7, 16, 21, 38, 24, 40, 26, 26, 42, 57, 39 }, + { 33, 34, 51, 51, 52, 41, 41, 34, 62, 0, 41, 41, 56, 7, 5, 56, 38, 38, 40, 44, 37, 42, 57, 39, 10 }, + { 16, 31, 32, 15, 60, 30, 4, 17, 19, 25, 22, 20, 4, 53, 19, 21, 22, 46, 25, 55, 26, 48, 63, 58, 55 }, +}; + +const uint8_t ff_vvc_alf_aps_class_to_filt_map[25] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, +}; + +const int8_t ff_vvc_inter_luma_filters[VVC_INTER_FILTER_TYPES][VVC_INTER_LUMA_FACTS][VVC_INTER_LUMA_TAPS] = { + { + //1x, hpelIfIdx == 0, Table 27 + { 0, 0, 0, 64, 0, 0, 0, 0 }, + { 0, 1, -3, 63, 4, -2, 1, 0 }, + { -1, 2, -5, 62, 8, -3, 1, 0 }, + { -1, 3, -8, 60, 13, -4, 1, 0 }, + { -1, 4, -10, 58, 17, -5, 1, 0 }, + { -1, 4, -11, 52, 26, -8, 3, -1 }, + { -1, 3, -9, 47, 31, -10, 4, -1 }, + { -1, 4, -11, 45, 34, -10, 4, -1 }, + { -1, 4, -11, 40, 40, -11, 4, -1 }, + { -1, 4, -10, 34, 45, -11, 4, -1 }, + { -1, 4, -10, 31, 47, -9, 3, -1 }, + { -1, 3, -8, 26, 52, -11, 4, -1 }, + { 0, 1, -5, 17, 58, -10, 4, -1 }, + { 0, 1, -4, 13, 60, -8, 3, -1 }, + { 0, 1, -3, 8, 62, -5, 2, -1 }, + { 0, 1, -2, 4, 63, -3, 1, 0 }, + }, + + { + //1x, hpelIfIdx == 1, Table 27 + { 0, 0, 0, 64, 0, 0, 0, 0 }, + { 0, 1, -3, 63, 4, -2, 1, 0 }, + { -1, 2, -5, 62, 8, -3, 1, 0 }, + { -1, 3, -8, 60, 13, -4, 1, 0 }, + { -1, 4, -10, 58, 17, -5, 1, 0 }, + { -1, 4, -11, 52, 26, -8, 3, -1 }, + { -1, 3, -9, 47, 31, -10, 4, -1 }, + { -1, 4, -11, 45, 34, -10, 4, -1 }, + { 0, 3, 9, 20, 20, 9, 3, 0 }, + { -1, 4, -10, 34, 45, -11, 4, -1 }, + { -1, 4, -10, 31, 47, -9, 3, -1 }, + { -1, 3, -8, 26, 52, -11, 4, -1 }, + { 0, 1, -5, 17, 58, -10, 4, -1 }, + { 0, 1, -4, 13, 60, -8, 3, -1 }, + { 0, 1, -3, 8, 62, -5, 2, -1 }, + { 0, 1, -2, 4, 63, -3, 1, 0 }, + }, + + { + //1x, affine, Table 30 + { 0, 0, 0, 64, 0, 0, 0, 0 }, + { 0, 1, -3, 63, 4, -2, 1, 0 }, + { 0, 1, -5, 62, 8, -3, 1, 0 }, + { 0, 2, -8, 60, 13, -4, 1, 0 }, + { 0, 3, -10, 58, 17, -5, 1, 0 }, + { 0, 3, -11, 52, 26, -8, 2, 0 }, + { 0, 2, -9, 47, 31, -10, 3, 0 }, + { 0, 3, -11, 45, 34, -10, 3, 0 }, + { 0, 3, -11, 40, 40, -11, 3, 0 }, + { 0, 3, -10, 34, 45, -11, 3, 0 }, + { 0, 3, -10, 31, 47, -9, 2, 0 }, + { 0, 2, -8, 26, 52, -11, 3, 0 }, + { 0, 1, -5, 17, 58, -10, 3, 0 }, + { 0, 1, -4, 13, 60, -8, 2, 0 }, + { 0, 1, -3, 8, 62, -5, 1, 0 }, + { 0, 1, -2, 4, 63, -3, 1, 0 }, + }, + +}; + +const int8_t ff_vvc_inter_chroma_filters[VVC_INTER_FILTER_TYPES][VVC_INTER_CHROMA_FACTS][VVC_INTER_CHROMA_TAPS] = { + { + //1x, Table 33 + { 0, 64, 0, 0 }, + { -1, 63, 2, 0 }, + { -2, 62, 4, 0 }, + { -2, 60, 7, -1 }, + { -2, 58, 10, -2 }, + { -3, 57, 12, -2 }, + { -4, 56, 14, -2 }, + { -4, 55, 15, -2 }, + { -4, 54, 16, -2 }, + { -5, 53, 18, -2 }, + { -6, 52, 20, -2 }, + { -6, 49, 24, -3 }, + { -6, 46, 28, -4 }, + { -5, 44, 29, -4 }, + { -4, 42, 30, -4 }, + { -4, 39, 33, -4 }, + { -4, 36, 36, -4 }, + { -4, 33, 39, -4 }, + { -4, 30, 42, -4 }, + { -4, 29, 44, -5 }, + { -4, 28, 46, -6 }, + { -3, 24, 49, -6 }, + { -2, 20, 52, -6 }, + { -2, 18, 53, -5 }, + { -2, 16, 54, -4 }, + { -2, 15, 55, -4 }, + { -2, 14, 56, -4 }, + { -2, 12, 57, -3 }, + { -2, 10, 58, -2 }, + { -1, 7, 60, -2 }, + { 0, 4, 62, -2 }, + { 0, 2, 63, -1 }, + }, + { + //1.5x, Table 34 + { 12, 40, 12, 0 }, + { 11, 40, 13, 0 }, + { 10, 40, 15, -1 }, + { 9, 40, 16, -1 }, + { 8, 40, 17, -1 }, + { 8, 39, 18, -1 }, + { 7, 39, 19, -1 }, + { 6, 38, 21, -1 }, + { 5, 38, 22, -1 }, + { 4, 38, 23, -1 }, + { 4, 37, 24, -1 }, + { 3, 36, 25, 0 }, + { 3, 35, 26, 0 }, + { 2, 34, 28, 0 }, + { 2, 33, 29, 0 }, + { 1, 33, 30, 0 }, + { 1, 31, 31, 1 }, + { 0, 30, 33, 1 }, + { 0, 29, 33, 2 }, + { 0, 28, 34, 2 }, + { 0, 26, 35, 3 }, + { 0, 25, 36, 3 }, + { -1, 24, 37, 4 }, + { -1, 23, 38, 4 }, + { -1, 22, 38, 5 }, + { -1, 21, 38, 6 }, + { -1, 19, 39, 7 }, + { -1, 18, 39, 8 }, + { -1, 17, 40, 8 }, + { -1, 16, 40, 9 }, + { -1, 15, 40, 10 }, + { 0, 13, 40, 11 }, + }, + { + //2x, Table 35 + { 17, 30, 17, 0 }, + { 17, 30, 18, -1 }, + { 16, 30, 18, 0 }, + { 16, 30, 18, 0 }, + { 15, 30, 18, 1 }, + { 14, 30, 18, 2 }, + { 13, 29, 19, 3 }, + { 13, 29, 19, 3 }, + { 12, 29, 20, 3 }, + { 11, 28, 21, 4 }, + { 10, 28, 22, 4 }, + { 10, 27, 22, 5 }, + { 9, 27, 23, 5 }, + { 9, 26, 24, 5 }, + { 8, 26, 24, 6 }, + { 7, 26, 25, 6 }, + { 7, 25, 25, 7 }, + { 6, 25, 26, 7 }, + { 6, 24, 26, 8 }, + { 5, 24, 26, 9 }, + { 5, 23, 27, 9 }, + { 5, 22, 27, 10 }, + { 4, 22, 28, 10 }, + { 4, 21, 28, 11 }, + { 3, 20, 29, 12 }, + { 3, 19, 29, 13 }, + { 3, 19, 29, 13 }, + { 2, 18, 30, 14 }, + { 1, 18, 30, 15 }, + { 0, 18, 30, 16 }, + { 0, 18, 30, 16 }, + { -1, 18, 30, 17 }, + }, +}; + +const int8_t ff_vvc_inter_luma_dmvr_filters[VVC_INTER_LUMA_DMVR_FACTS][VVC_INTER_LUMA_DMVR_TAPS] = { + { 16, 0 }, + { 15, 1 }, + { 14, 2 }, + { 13, 3 }, + { 12, 4 }, + { 11, 5 }, + { 10, 6 }, + { 9, 7 }, + { 8, 8 }, + { 7, 9 }, + { 6, 10 }, + { 5, 11 }, + { 4, 12 }, + { 3, 13 }, + { 2, 14 }, + { 1, 15 }, +}; + +#define FILTER_G(fact) { 16 - (fact >> 1), 32 - (fact >> 1), 16 + (fact >> 1), fact >> 1} +// Table 25 – Specification of interpolation filter coefficients fC and fG +const int8_t ff_vvc_intra_luma_filter[VVC_INTRA_LUMA_TYPES][VVC_INTRA_LUMA_FACTS][VVC_INTRA_LUMA_TAPS] = { + { + { 0, 64, 0, 0 }, + { -1, 63, 2, 0 }, + { -2, 62, 4, 0 }, + { -2, 60, 7, -1 }, + { -2, 58, 10, -2 }, + { -3, 57, 12, -2 }, + { -4, 56, 14, -2 }, + { -4, 55, 15, -2 }, + { -4, 54, 16, -2 }, + { -5, 53, 18, -2 }, + { -6, 52, 20, -2 }, + { -6, 49, 24, -3 }, + { -6, 46, 28, -4 }, + { -5, 44, 29, -4 }, + { -4, 42, 30, -4 }, + { -4, 39, 33, -4 }, + { -4, 36, 36, -4 }, + { -4, 33, 39, -4 }, + { -4, 30, 42, -4 }, + { -4, 29, 44, -5 }, + { -4, 28, 46, -6 }, + { -3, 24, 49, -6 }, + { -2, 20, 52, -6 }, + { -2, 18, 53, -5 }, + { -2, 16, 54, -4 }, + { -2, 15, 55, -4 }, + { -2, 14, 56, -4 }, + { -2, 12, 57, -3 }, + { -2, 10, 58, -2 }, + { -1, 7, 60, -2 }, + { 0, 4, 62, -2 }, + { 0, 2, 63, -1 }, + }, + { + FILTER_G(0), + FILTER_G(1), + FILTER_G(2), + FILTER_G(3), + FILTER_G(4), + FILTER_G(5), + FILTER_G(6), + FILTER_G(7), + FILTER_G(8), + FILTER_G(9), + FILTER_G(10), + FILTER_G(11), + FILTER_G(12), + FILTER_G(13), + FILTER_G(14), + FILTER_G(15), + FILTER_G(16), + FILTER_G(17), + FILTER_G(18), + FILTER_G(19), + FILTER_G(20), + FILTER_G(21), + FILTER_G(22), + FILTER_G(23), + FILTER_G(24), + FILTER_G(25), + FILTER_G(26), + FILTER_G(27), + FILTER_G(28), + FILTER_G(29), + FILTER_G(30), + FILTER_G(31), + } +}; + +const uint8_t ff_vvc_gpm_angle_idx[VVC_GPM_NUM_PARTITION] = { + 0, 0, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, + 5, 5, 8, 8, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, + 14, 14, 14, 14, 16, 16, 18, 18, 18, 19, 19, 19, 20, 20, 20, 21, + 21, 21, 24, 24, 27, 27, 27, 28, 28, 28, 29, 29, 29, 30, 30, 30, +}; + +const uint8_t ff_vvc_gpm_distance_idx[VVC_GPM_NUM_PARTITION] = { + 1, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, + 2, 3, 1, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, + 0, 1, 2, 3, 1, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, + 2, 3, 1, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, +}; + +const int8_t ff_vvc_gpm_distance_lut[VVC_GPM_NUM_ANGLES] = { + 8, 8, 8, 8, 4, 4, 2, 1, 0, -1, -2, -4, -4, -8, -8, -8, -8, -8, -8, -8, -4, -4, -2, -1, 0, 1, 2, 4, 4, 8, 8, 8, +}; + +const uint8_t ff_vvc_gpm_angle_to_mirror[VVC_GPM_NUM_ANGLES] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, +}; + +#define INV -1 +const uint8_t ff_vvc_gpm_angle_to_weights_idx[VVC_GPM_NUM_ANGLES] = { + 0, INV, 1, 2, 3, 4, INV, INV, 5, INV, INV, 4, 3, 2, 1, INV, + 0, INV, 1, 2, 3, 4, INV, INV, 5, INV, INV, 4, 3, 2, 1, INV, +}; +#undef INV + +const uint8_t ff_vvc_gpm_weights_offset_x[VVC_GPM_NUM_PARTITION][4][4] = { + { + { 53, 50, 44, 32 }, + { 53, 50, 44, 32 }, + { 53, 50, 44, 32 }, + { 53, 50, 44, 32 }, + }, + { + { 55, 54, 52, 48 }, + { 55, 54, 52, 48 }, + { 55, 54, 52, 48 }, + { 55, 54, 52, 48 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 51, 46, 36, 16 }, + { 51, 46, 36, 16 }, + { 51, 46, 36, 16 }, + { 51, 46, 36, 16 }, + }, + { + { 49, 42, 28, 0 }, + { 49, 42, 28, 0 }, + { 49, 42, 28, 0 }, + { 49, 42, 28, 0 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, +}; + +const uint8_t ff_vvc_gpm_weights_offset_y[VVC_GPM_NUM_PARTITION][4][4] = { + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 53, 53, 53, 53 }, + { 50, 50, 50, 50 }, + { 44, 44, 44, 44 }, + { 32, 32, 32, 32 }, + }, + { + { 55, 55, 55, 55 }, + { 54, 54, 54, 54 }, + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 51, 51, 51 }, + { 46, 46, 46, 46 }, + { 36, 36, 36, 36 }, + { 16, 16, 16, 16 }, + }, + { + { 49, 49, 49, 49 }, + { 42, 42, 42, 42 }, + { 28, 28, 28, 28 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, +}; + +const uint8_t ff_vvc_gpm_weights[6][VVC_GPM_WEIGHT_SIZE * VVC_GPM_WEIGHT_SIZE] = { + { + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + }, + { + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + }, + { + 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + }, + { + 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + }, + { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, + 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, 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, 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, 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, + 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, + 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, +}; diff --git a/libavcodec/vvc/vvc_data.h b/libavcodec/vvc/vvc_data.h new file mode 100644 index 0000000000..e493b9e0e6 --- /dev/null +++ b/libavcodec/vvc/vvc_data.h @@ -0,0 +1,80 @@ +/* + * VVC shared tables + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VVC_DATA_H +#define AVCODEC_VVC_DATA_H + +#include + +extern const uint8_t ff_vvc_diag_scan_x[5][5][16 * 16]; +extern const uint8_t ff_vvc_diag_scan_y[5][5][16 * 16]; + +extern const uint8_t ff_vvc_scaling_pred_8[8 * 8]; +extern const uint8_t ff_vvc_scaling_pred_16[8 * 8]; +extern const int ff_vvc_scaling_list0[8 * 8]; + +extern const int8_t ff_vvc_dct8_4x4[4][4]; +extern const int8_t ff_vvc_dct8_8x8[8][8]; +extern const int8_t ff_vvc_dct8_16x16[16][16]; +extern const int8_t ff_vvc_dct8_32x32[32][32]; +extern const int8_t ff_vvc_dst7_4x4[4][4]; +extern const int8_t ff_vvc_dst7_8x8[8][8]; +extern const int8_t ff_vvc_dst7_16x16[16][16]; +extern const int8_t ff_vvc_dst7_32x32[32][32]; +extern const int8_t ff_vvc_lfnst_4x4[4][2][16][16]; +extern const int8_t ff_vvc_lfnst_8x8[4][2][16][48]; +extern const uint8_t ff_vvc_lfnst_tr_set_index[95]; +extern uint8_t ff_vvc_default_scale_m[64 * 64]; + +#define VVC_INTER_FILTER_TYPES 3 +#define VVC_INTER_LUMA_FACTS 16 +#define VVC_INTER_LUMA_TAPS 8 +#define VVC_INTER_CHROMA_FACTS 32 +#define VVC_INTER_CHROMA_TAPS 4 +#define VVC_INTER_LUMA_DMVR_FACTS 16 +#define VVC_INTER_LUMA_DMVR_TAPS 2 +extern const int8_t ff_vvc_inter_luma_filters[VVC_INTER_FILTER_TYPES][VVC_INTER_LUMA_FACTS][VVC_INTER_LUMA_TAPS]; +extern const int8_t ff_vvc_inter_chroma_filters[VVC_INTER_FILTER_TYPES][VVC_INTER_CHROMA_FACTS][VVC_INTER_CHROMA_TAPS]; +extern const int8_t ff_vvc_inter_luma_dmvr_filters[VVC_INTER_LUMA_DMVR_FACTS][VVC_INTER_LUMA_DMVR_TAPS]; + +#define VVC_INTRA_LUMA_TYPES 2 +#define VVC_INTRA_LUMA_FACTS 32 +#define VVC_INTRA_LUMA_TAPS 4 +extern const int8_t ff_vvc_intra_luma_filter[VVC_INTRA_LUMA_TYPES][VVC_INTRA_LUMA_FACTS][VVC_INTRA_LUMA_TAPS]; + +#define VVC_GPM_NUM_PARTITION 64 +#define VVC_GPM_NUM_ANGLES 32 +#define VVC_GPM_WEIGHT_SIZE 112 +extern const uint8_t ff_vvc_gpm_angle_idx[VVC_GPM_NUM_PARTITION]; +extern const uint8_t ff_vvc_gpm_distance_idx[VVC_GPM_NUM_PARTITION]; +extern const int8_t ff_vvc_gpm_distance_lut[VVC_GPM_NUM_ANGLES]; +extern const uint8_t ff_vvc_gpm_angle_to_mirror[VVC_GPM_NUM_ANGLES]; +extern const uint8_t ff_vvc_gpm_angle_to_weights_idx[VVC_GPM_NUM_ANGLES]; +extern const uint8_t ff_vvc_gpm_weights_offset_x[VVC_GPM_NUM_PARTITION][4][4]; +extern const uint8_t ff_vvc_gpm_weights_offset_y[VVC_GPM_NUM_PARTITION][4][4]; +extern const uint8_t ff_vvc_gpm_weights[6][VVC_GPM_WEIGHT_SIZE * VVC_GPM_WEIGHT_SIZE]; + +extern const int16_t ff_vvc_alf_fix_filt_coeff[64][12]; +extern const uint8_t ff_vvc_alf_class_to_filt_map[16][25]; +extern const uint8_t ff_vvc_alf_aps_class_to_filt_map[25]; + +const uint8_t* ff_vvc_get_mip_matrix(const int size_id, const int mode_idx); + +#endif /* AVCODEC_VVC_DATA_H */ From patchwork Sun Nov 12 10:35:15 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 44622 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:92a5:b0:181:818d:5e7f with SMTP id q37csp728128pzg; Sun, 12 Nov 2023 02:36:35 -0800 (PST) X-Google-Smtp-Source: AGHT+IFdVkgMCaJD4nCDRxg0EiRqRjGjs+nq0uYjy2QMKAWXcBS9fxqGKykjXceSgQBxy+UwKVsT X-Received: by 2002:a17:907:1183:b0:9da:e694:e6a2 with SMTP id uz3-20020a170907118300b009dae694e6a2mr2759454ejb.4.1699785394885; Sun, 12 Nov 2023 02:36:34 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id h6-20020a1709063b4600b009930ef7f05dsi1531669ejf.908.2023.11.12.02.36.34; Sun, 12 Nov 2023 02:36:34 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@outlook.com header.s=selector1 header.b=tupoqkPu; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 8904668CC0C; Sun, 12 Nov 2023 12:36:16 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from APC01-SG2-obe.outbound.protection.outlook.com (mail-sgaapc01olkn2035.outbound.protection.outlook.com [40.92.53.35]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 3D72368CAA2 for ; Sun, 12 Nov 2023 12:36:07 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=M5PPgQInFPr1hRanNBuc/kV958L5DPQ9G1ga+EMGCUmxj5Yj4CQEAvfk3TSAI8K8PDzSI5Bfazemwk5gehhZhc9WxveNc3ehYTdXT08uaaUUsXmcKqb5oAA1Cz7JGMt+7lPmzANXqt89Tl+cRNJbixRujKjx694pZOaiivkPLX7MZ/a77MAFoCYbDC50AXq/OlIGdNFLbMsTG7P9oMrfGxP26ABuEwC4XJpiskzwLF/XQWHCSSZU0bmGY/WKHuvcrXxThdYkMcMBLE5rzIGIIcCHgqlfTZM5v9/4XL6ClXfYxbD6cJpaO2ePv/+RRaeC5my7m6poXr27cLkX3Zn7IA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=78tpXz87DYOuTcsoylj0PT3u26jL//GaZt8BWK0Uz5Y=; b=Fcg0ox9N70+0qa9aQtIE0if+tn4Egc/v5GJ2YYq1hKGZ1zw0arD0ptl30sVVZMvASAsXr5wLBo4wWGVV3M6K4wDqP1tT9bt2bUN9to3kdYl0SAJ1KdRnzfN7suTzC2QDwhbwNCTRotV2OkgLkl4d3PYISccVb7MVvOT8H2qAhl/2qZP8AKKQBtjbifKVc86n2m/ilbPUxMw/Qn2QSA1cdVIbBJRd4FrDzbwUBOPZk8G6oj5Y2wKyn27uchwGd0820bVNLE8j5kvOsoQTO9NgEANLBUkrpB/sBM75vz30pEFZa3p6PiS/D7zW0C/XtbttdNAuEEudalD2Zf5Fops/RQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=outlook.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=78tpXz87DYOuTcsoylj0PT3u26jL//GaZt8BWK0Uz5Y=; b=tupoqkPu9dzMUajkI/LFudVsngbdyQh0wa6pWVUwhdLL8iBcIQVxYUDRBsTzSNFn2CQN/4pPOpstvdaLvUg8saFe8bExkTiBU/MAllQ2vPPW20bxdNLV7JtLVj1wFLXxreEAOIVm0h0F2lIYIayAYbj1yK3wc4fN2SdksVsCNWksj9l9L7C4t2Iikf/k/KyuopL0PQfa72tT5RhbHnGHVzR2UiIrKKRLj5OYWIR170YZI5kdX4v2GEeRRwg4jLRNkXJDK3HA27PP4EbRmhpquAf/+B/Xl+55lsQFdOMXGhXsRMOqx61m5HZLg2J+8/JBQ5BkdUR7HwmiEgiTyUGHSA== Received: from TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) by TYSPR06MB6768.apcprd06.prod.outlook.com (2603:1096:400:475::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6954.29; Sun, 12 Nov 2023 10:35:56 +0000 Received: from TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f]) by TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f%4]) with mapi id 15.20.6954.028; Sun, 12 Nov 2023 10:35:56 +0000 From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Nov 2023 18:35:15 +0800 Message-ID: X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231112103526.11245-1-nuomi2021@gmail.com> References: <20231112103526.11245-1-nuomi2021@gmail.com> X-TMN: [jDVhvx/I1+H/kTwQk74p3kTSoeB9SgKp] X-ClientProxiedBy: TYWP286CA0030.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:262::20) To TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) X-Microsoft-Original-Message-ID: <20231112103526.11245-3-nuomi2021@gmail.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 2 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: TYSPR06MB6433:EE_|TYSPR06MB6768:EE_ X-MS-Office365-Filtering-Correlation-Id: f34fcdf5-44b9-4803-e334-08dbe36b26e9 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: in2sYe8tk7ZtRfq9jaP5H2Y3Eup8HAIZu9V6G6rftEz1c0Yvo974x61rskcWKs0Mw6T0a6iworuDKN0eZOmQPyUGoICwhXVZR2t6YjTy1ADDVzRU7x52EzJ13KcifEm5nbJwSFNnXY0EgXy/DUzUlp/f11sgpx7YbfcsBpiiAk8ejZmaLu6WCSZMvNYwxL9IWkB+aIIq5ys5c2V3YWfArUf0OGvMv4C1m//ouw0iw+YoN7/rI0laAUCGmDo01g6U+Xir8yBL9N8jNxsGeZS/MB8IqbWe9VXAPae+h2iZYcvwX0kHXIBoehzmM3qmBQS7DCdDi/hCpWQ3dEXcj3noKIvcQqhBDniLFy9M2PggUhqqPAiEtHtt6uSSBGZ60rbWUeDoeBKxtfaruok6Eo4UuOT63EEzrYtqlAMMpcR8CoOHwhvbLuwE/Z8bJPTQktpzRvnjh2TAS1nPyyOkAj3QQFXJqFVDKb/XP24GKdZXRuj4Mctu5IdwSFqVIP9f6BmGrag3eE4DlObrEK1WxNIt9NiquL6nJnR65dCbBfvFJqJheHhHsuQRYAnKoiGz5WQf5IPI4eXJcXps2924AK/jfe/NJyH0wvWlSyKkt+z3YyfmGim+BmQs7Wh4wGfbw3jX X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?q?b/zSq+RNpfYxVwNvJDd01P97MaMF?= =?utf-8?q?3VAoJFAt/cXSYjxIygapN/5WuP9GqwLt2vWtKZU5/913XP3PxHR6g/ziSbTUJtDfA?= =?utf-8?q?zybzggPzzUeLOCeUvtzpArhr6pIW9ZrKvYU/pp8+ERhrsMkb7cHpJXnJQlWfFDjsQ?= =?utf-8?q?BCOUdBion1ua/gp3cK/kai7lYtO1XYIPGFiH/7jDz9WDQ99QdgFOCedakvIhTkavo?= =?utf-8?q?s6qpinCsdJ3+AZEaTLhs0mG6DOyk7oiIXYibcBO1SjxbliUv8Xhtff7q/XVT694hn?= =?utf-8?q?VJ7ket3KLlcV7WnIKPLd4K2+6b6qjh+vJb62+T10kfSepPcUFRKJVPoZiQHlWkPbC?= =?utf-8?q?uXDW2hlAzdoOSNqvSbbkA6uEn0CFR4WvbegWQwPQKHasNwJRWL1EjZi2vUAh7fztL?= =?utf-8?q?Z/L5cIpWJmLIsegMuYI6tVimOmV2sCefwwkSMMu1gZL9C0EC6W/Kyue44sQN7TVXV?= =?utf-8?q?v5uUbWWeP5VYOA5hSPpbEk8YJV5hXD0ASr622+Py+OsvVMmcwQUGsK8E3bV4mek/U?= =?utf-8?q?XTzCPapV4tSlWGINgdD+k+owCBnYxUaPrYwDfKcO9pC+XTXMnWsE2fi/LO0E8XXIJ?= =?utf-8?q?bUkCj1ASqra+1POQp2P/oPxHFzPAYOoUyg47ycOwe0yschVdxPUIwRbjBEPjA7053?= =?utf-8?q?8P5AtcYh1mJ0zeApq1JZWqPrv/UfhVu/H765zUb4xDImArWBuuyb6sQJeP9lLrnYI?= =?utf-8?q?vL9ji9UkMlFFrKHKm+yMz74gaZIOhS0YLvuoev4RjYniBFkPWCVAzqDVv4DjY4kRU?= =?utf-8?q?ky2eASuJ4PZD+QW1rqRnCYSc+EXt1CKhCkJzAwGmFyuNpyB1KbPqqofG8B8vSELjr?= =?utf-8?q?ADc4BBv6JHvZh8lEJOKVqc1tmdEc0dWIAnwMRCPpwMaGK/JJdfTorUQ6vEzIy4zI9?= =?utf-8?q?I7fMqdQOE1w4Vu02vtrTqtVKa9SgOb/wcAaB/9RASycc3ROHsxLuuLJe80nwTZq6q?= =?utf-8?q?MnABoGZz+oa8wkIiNQCP9EajH7iakoX/HXwjF0YEF8JD1bdybMN0BHRxSCHvkdaAE?= =?utf-8?q?4e195/rn9kuKyoSG5iIJdfg76ewMWdkJOVV8kq5l8zhKiNbN5XAmYSLgsuqK8ZFsE?= =?utf-8?q?h02iZwgdB9Z9hJyC5363TIQildU478hF2aNNy+vWGvdHiSSm22aD0L4k9znJyjFy7?= =?utf-8?q?25okdMEa08vr21v5DesQ78zxkkG6xwWadU2hHuWlYvDM67gOK8dXQvlqE+rfh9NfC?= =?utf-8?q?8U8bk6j5KRNU8/f1u?= X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: f34fcdf5-44b9-4803-e334-08dbe36b26e9 X-MS-Exchange-CrossTenant-AuthSource: TYSPR06MB6433.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Nov 2023 10:35:55.9565 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYSPR06MB6768 Subject: [FFmpeg-devel] [PATCH v5 03/14] vvcdec: add parameter parser for sps, pps, ph, sh X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Nuo Mi Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: gH9dHb5knQwb --- libavcodec/vvc/Makefile | 1 + libavcodec/vvc/vvc_ps.c | 1149 +++++++++++++++++++++++++++++++++++++++ libavcodec/vvc/vvc_ps.h | 263 +++++++++ libavcodec/vvc/vvcdec.h | 7 + 4 files changed, 1420 insertions(+) create mode 100644 libavcodec/vvc/vvc_ps.c create mode 100644 libavcodec/vvc/vvc_ps.h diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile index 8a5c66ab13..b2b187ac6f 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -3,3 +3,4 @@ clean:: OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ vvc/vvc_data.o \ + vvc/vvc_ps.o \ diff --git a/libavcodec/vvc/vvc_ps.c b/libavcodec/vvc/vvc_ps.c new file mode 100644 index 0000000000..284e7406c2 --- /dev/null +++ b/libavcodec/vvc/vvc_ps.c @@ -0,0 +1,1149 @@ +/* + * VVC parameter set parser + * + * Copyright (C) 2023 Nuo Mi + * Copyright (C) 2022 Xu Mu + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "libavcodec/cbs_h266.h" +#include "libavutil/imgutils.h" +#include "libavcodec/refstruct.h" +#include "vvc_data.h" +#include "vvc_ps.h" +#include "vvcdec.h" + +static int sps_map_pixel_format(VVCSPS *sps, void *log_ctx) +{ + const H266RawSPS *r = sps->r; + const AVPixFmtDescriptor *desc; + + switch (sps->bit_depth) { + case 8: + if (r->sps_chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY8; + if (r->sps_chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P; + if (r->sps_chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P; + if (r->sps_chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P; + break; + case 10: + if (r->sps_chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY10; + if (r->sps_chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P10; + if (r->sps_chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P10; + if (r->sps_chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P10; + break; + case 12: + if (r->sps_chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY12; + if (r->sps_chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P12; + if (r->sps_chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P12; + if (r->sps_chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P12; + break; + default: + av_log(log_ctx, AV_LOG_ERROR, + "The following bit-depths are currently specified: 8, 10, 12 bits, " + "chroma_format_idc is %d, depth is %d\n", + r->sps_chroma_format_idc, sps->bit_depth); + return AVERROR_INVALIDDATA; + } + + desc = av_pix_fmt_desc_get(sps->pix_fmt); + if (!desc) + return AVERROR(EINVAL); + + sps->hshift[0] = sps->vshift[0] = 0; + sps->hshift[2] = sps->hshift[1] = desc->log2_chroma_w; + sps->vshift[2] = sps->vshift[1] = desc->log2_chroma_h; + + sps->pixel_shift = sps->bit_depth > 8; + + return 0; +} + +static int sps_bit_depth(VVCSPS *sps, void *log_ctx) +{ + const H266RawSPS *r = sps->r; + + sps->bit_depth = r->sps_bitdepth_minus8 + 8; + sps->qp_bd_offset = 6 * (sps->bit_depth - 8); + sps->log2_transform_range = + r->sps_extended_precision_flag ? FFMAX(15, FFMIN(20, sps->bit_depth + 6)) : 15; + return sps_map_pixel_format(sps, log_ctx); +} + +static int sps_chroma_qp_table(VVCSPS *sps) +{ + const H266RawSPS *r = sps->r; + const int num_qp_tables = r->sps_same_qp_table_for_chroma_flag ? + 1 : (r->sps_joint_cbcr_enabled_flag ? 3 : 2); + + for (int i = 0; i < num_qp_tables; i++) { + int num_points_in_qp_table; + int8_t qp_in[VVC_MAX_POINTS_IN_QP_TABLE], qp_out[VVC_MAX_POINTS_IN_QP_TABLE]; + unsigned int delta_qp_in[VVC_MAX_POINTS_IN_QP_TABLE]; + int off = sps->qp_bd_offset; + + num_points_in_qp_table = r->sps_num_points_in_qp_table_minus1[i] + 1; + + qp_out[0] = qp_in[0] = r->sps_qp_table_start_minus26[i] + 26; + for (int j = 0; j < num_points_in_qp_table; j++ ) { + delta_qp_in[j] = r->sps_delta_qp_in_val_minus1[i][j] + 1; + qp_in[j+1] = qp_in[j] + delta_qp_in[j]; + qp_out[j+1] = qp_out[j] + (r->sps_delta_qp_in_val_minus1[i][j] ^ r->sps_delta_qp_diff_val[i][j]); + } + sps->chroma_qp_table[i][qp_in[0] + off] = qp_out[0]; + for (int k = qp_in[0] - 1 + off; k >= 0; k--) + sps->chroma_qp_table[i][k] = av_clip(sps->chroma_qp_table[i][k+1]-1, -off, 63); + + for (int j = 0; j < num_points_in_qp_table; j++) { + int sh = delta_qp_in[j] >> 1; + for (int k = qp_in[j] + 1 + off, m = 1; k <= qp_in[j+1] + off; k++, m++) { + sps->chroma_qp_table[i][k] = sps->chroma_qp_table[i][qp_in[j] + off] + + ((qp_out[j+1] - qp_out[j]) * m + sh) / delta_qp_in[j]; + } + } + for (int k = qp_in[num_points_in_qp_table] + 1 + off; k <= 63 + off; k++) + sps->chroma_qp_table[i][k] = av_clip(sps->chroma_qp_table[i][k-1] + 1, -sps->qp_bd_offset, 63); + } + if (r->sps_same_qp_table_for_chroma_flag) { + memcpy(&sps->chroma_qp_table[1], &sps->chroma_qp_table[0], sizeof(sps->chroma_qp_table[0])); + memcpy(&sps->chroma_qp_table[2], &sps->chroma_qp_table[0], sizeof(sps->chroma_qp_table[0])); + } + + return 0; +} + +static void sps_poc(VVCSPS *sps) +{ + sps->max_pic_order_cnt_lsb = 1 << (sps->r->sps_log2_max_pic_order_cnt_lsb_minus4 + 4); +} + +static void sps_inter(VVCSPS *sps) +{ + const H266RawSPS *r = sps->r; + + sps->max_num_merge_cand = 6 - r->sps_six_minus_max_num_merge_cand; + sps->max_num_ibc_merge_cand = 6 - r->sps_six_minus_max_num_ibc_merge_cand; + + if (sps->r->sps_gpm_enabled_flag) { + sps->max_num_gpm_merge_cand = 2; + if (sps->max_num_merge_cand >= 3) + sps->max_num_gpm_merge_cand = sps->max_num_merge_cand - r->sps_max_num_merge_cand_minus_max_num_gpm_cand; + } + + sps->log2_parallel_merge_level = r->sps_log2_parallel_merge_level_minus2 + 2; +} + +static void sps_partition_constraints(VVCSPS* sps) +{ + const H266RawSPS *r = sps->r; + + sps->ctb_log2_size_y = r->sps_log2_ctu_size_minus5 + 5; + sps->ctb_size_y = 1 << sps->ctb_log2_size_y; + sps->min_cb_log2_size_y = r->sps_log2_min_luma_coding_block_size_minus2 + 2; + sps->min_cb_size_y = 1 << sps->min_cb_log2_size_y; + sps->max_tb_size_y = 1 << (r->sps_max_luma_transform_size_64_flag ? 6 : 5); + sps->max_ts_size = 1 << (r->sps_log2_transform_skip_max_size_minus2 + 2); +} + +static void sps_ladf(VVCSPS* sps) +{ + const H266RawSPS *r = sps->r; + + if (r->sps_ladf_enabled_flag) { + sps->num_ladf_intervals = r->sps_num_ladf_intervals_minus2 + 2; + sps->ladf_interval_lower_bound[0] = 0; + for (int i = 0; i < sps->num_ladf_intervals - 1; i++) { + sps->ladf_interval_lower_bound[i + 1] = + sps->ladf_interval_lower_bound[i] + r->sps_ladf_delta_threshold_minus1[i] + 1; + } + } +} + +static int sps_derive(VVCSPS *sps, void *log_ctx) +{ + int ret; + const H266RawSPS *r = sps->r; + + sps->width = r->sps_pic_width_max_in_luma_samples; + sps->height = r->sps_pic_height_max_in_luma_samples; + + ret = sps_bit_depth(sps, log_ctx); + if (ret < 0) + return ret; + sps_poc(sps); + sps_inter(sps); + sps_partition_constraints(sps); + sps_ladf(sps); + if (r->sps_chroma_format_idc != 0) + sps_chroma_qp_table(sps); + + return 0; +} + +static void sps_free(FFRefStructOpaque opaque, void *obj) +{ + VVCSPS *sps = (VVCSPS*)obj; + ff_refstruct_unref(&sps->r); +} + +static const VVCSPS *sps_alloc(const H266RawSPS *rsps, void *log_ctx) +{ + int ret; + VVCSPS *sps = ff_refstruct_alloc_ext(sizeof(*sps), 0, NULL, sps_free); + + ff_refstruct_replace(&sps->r, rsps); + + ret = sps_derive(sps, log_ctx); + if (ret < 0) + goto fail; + + return sps; + +fail: + ff_refstruct_unref(&sps); + return NULL; +} + +static int decode_sps(VVCParamSets *ps, const H266RawSPS *rsps, void *log_ctx) +{ + const int sps_id = rsps->sps_seq_parameter_set_id; + const VVCSPS *old_sps = ps->sps_list[sps_id]; + const VVCSPS *sps; + + if (old_sps && old_sps->r == rsps) + return 0; + + sps = sps_alloc(rsps, log_ctx); + if (!sps) + return AVERROR(ENOMEM); + + ff_refstruct_replace(&ps->sps_list[sps_id], sps); + ff_refstruct_unref(&sps); + + return 0; +} + +static void pps_chroma_qp_offset(VVCPPS *pps) +{ + pps->chroma_qp_offset[CB - 1] = pps->r->pps_cb_qp_offset; + pps->chroma_qp_offset[CR - 1] = pps->r->pps_cr_qp_offset; + pps->chroma_qp_offset[JCBCR - 1]= pps->r->pps_joint_cbcr_qp_offset_value; + for (int i = 0; i < 6; i++) { + pps->chroma_qp_offset_list[i][CB - 1] = pps->r->pps_cb_qp_offset_list[i]; + pps->chroma_qp_offset_list[i][CR - 1] = pps->r->pps_cr_qp_offset_list[i]; + pps->chroma_qp_offset_list[i][JCBCR - 1]= pps->r->pps_joint_cbcr_qp_offset_list[i]; + } +} + +static void pps_width_height(VVCPPS *pps, const VVCSPS *sps) +{ + const H266RawPPS *r = pps->r; + + pps->width = r->pps_pic_width_in_luma_samples; + pps->height = r->pps_pic_height_in_luma_samples; + + pps->ctb_width = AV_CEIL_RSHIFT(pps->width, sps->ctb_log2_size_y); + pps->ctb_height = AV_CEIL_RSHIFT(pps->height, sps->ctb_log2_size_y); + pps->ctb_count = pps->ctb_width * pps->ctb_height; + + pps->min_cb_width = pps->width >> sps->min_cb_log2_size_y; + pps->min_cb_height = pps->height >> sps->min_cb_log2_size_y; + + pps->min_pu_width = pps->width >> MIN_PU_LOG2; + pps->min_pu_height = pps->height >> MIN_PU_LOG2; + pps->min_tu_width = pps->width >> MIN_TU_LOG2; + pps->min_tu_height = pps->height >> MIN_TU_LOG2; + + pps->width32 = AV_CEIL_RSHIFT(pps->width, 5); + pps->height32 = AV_CEIL_RSHIFT(pps->height, 5); + pps->width64 = AV_CEIL_RSHIFT(pps->width, 6); + pps->height64 = AV_CEIL_RSHIFT(pps->height, 6); +} + +static int pps_bd(VVCPPS *pps) +{ + const H266RawPPS *r = pps->r; + + pps->col_bd = av_calloc(r->num_tile_columns + 1, sizeof(*pps->col_bd)); + pps->row_bd = av_calloc(r->num_tile_rows + 1, sizeof(*pps->row_bd)); + pps->ctb_to_col_bd = av_calloc(pps->ctb_width + 1, sizeof(*pps->ctb_to_col_bd)); + pps->ctb_to_row_bd = av_calloc(pps->ctb_height + 1, sizeof(*pps->ctb_to_col_bd)); + if (!pps->col_bd || !pps->row_bd || !pps->ctb_to_col_bd || !pps->ctb_to_row_bd) + return AVERROR(ENOMEM); + + for (int i = 0, j = 0; i < r->num_tile_columns; i++) { + pps->col_bd[i] = j; + j += r->col_width_val[i]; + for (int k = pps->col_bd[i]; k < j; k++) + pps->ctb_to_col_bd[k] = pps->col_bd[i]; + } + + for (int i = 0, j = 0; i < r->num_tile_rows; i++) { + pps->row_bd[i] = j; + j += r->row_height_val[i]; + for (int k = pps->row_bd[i]; k < j; k++) + pps->ctb_to_row_bd[k] = pps->row_bd[i]; + } + return 0; +} + + +static int next_tile_idx(int tile_idx, const int i, const H266RawPPS *r) +{ + if (r->pps_tile_idx_delta_present_flag) { + tile_idx += r->pps_tile_idx_delta_val[i]; + } else { + tile_idx += r->pps_slice_width_in_tiles_minus1[i] + 1; + if (tile_idx % r->num_tile_columns == 0) + tile_idx += (r->pps_slice_height_in_tiles_minus1[i]) * r->num_tile_columns; + } + return tile_idx; +} + +static void tile_xy(int *tile_x, int *tile_y, const int tile_idx, const VVCPPS *pps) +{ + *tile_x = tile_idx % pps->r->num_tile_columns; + *tile_y = tile_idx / pps->r->num_tile_columns; +} + +static void ctu_xy(int *ctu_x, int *ctu_y, const int tile_x, const int tile_y, const VVCPPS *pps) +{ + *ctu_x = pps->col_bd[tile_x]; + *ctu_y = pps->row_bd[tile_y]; +} + +static int ctu_rs(const int ctu_x, const int ctu_y, const VVCPPS *pps) +{ + return pps->ctb_width * ctu_y + ctu_x; +} + +static int pps_add_ctus(VVCPPS *pps, int *off, const int ctu_x, const int ctu_y, + const int w, const int h) +{ + int start = *off; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + pps->ctb_addr_in_slice[*off] = ctu_rs(ctu_x + x, ctu_y + y, pps); + (*off)++; + } + } + return *off - start; +} + +static int pps_one_tile_slices(VVCPPS *pps, const int tile_idx, int i, int *off) +{ + const H266RawPPS *r = pps->r; + int ctu_x, ctu_y, ctu_y_end, tile_x, tile_y; + + tile_xy(&tile_x, &tile_y, tile_idx, pps); + ctu_xy(&ctu_x, &ctu_y, tile_x, tile_y, pps); + ctu_y_end = ctu_y + r->row_height_val[tile_y]; + while (ctu_y < ctu_y_end) { + pps->slice_start_offset[i] = *off; + pps->num_ctus_in_slice[i] = pps_add_ctus(pps, off, ctu_x, ctu_y, + r->col_width_val[tile_x], r->slice_height_in_ctus[i]); + ctu_y += r->slice_height_in_ctus[i++]; + } + i--; + return i; +} + +static void pps_multi_tiles_slice(VVCPPS *pps, const int tile_idx, const int i, int *off) +{ + const H266RawPPS *r = pps->r; + int ctu_x, ctu_y,tile_x, tile_y; + + tile_xy(&tile_x, &tile_y, tile_idx, pps); + pps->slice_start_offset[i] = *off; + pps->num_ctus_in_slice[i] = 0; + for (int ty = tile_y; ty <= tile_y + r->pps_slice_height_in_tiles_minus1[i]; ty++) { + for (int tx = tile_x; tx <= tile_x + r->pps_slice_width_in_tiles_minus1[i]; tx++) { + ctu_xy(&ctu_x, &ctu_y, tx, ty, pps); + pps->num_ctus_in_slice[i] += pps_add_ctus(pps, off, ctu_x, ctu_y, + r->col_width_val[tx], r->row_height_val[ty]); + } + } +} + +static void pps_rect_slice(VVCPPS* pps) +{ + const H266RawPPS* r = pps->r; + int tile_idx = 0, off = 0; + + for (int i = 0; i < r->pps_num_slices_in_pic_minus1 + 1; i++) { + if (!r->pps_slice_width_in_tiles_minus1[i] && + !r->pps_slice_height_in_tiles_minus1[i]) { + i = pps_one_tile_slices(pps, tile_idx, i, &off); + } else { + pps_multi_tiles_slice(pps, tile_idx, i, &off); + + } + tile_idx = next_tile_idx(tile_idx, i, r); + } +} + +static void pps_no_rect_slice(VVCPPS* pps) +{ + const H266RawPPS* r = pps->r; + int ctu_x, ctu_y, off = 0; + + for (int tile_y = 0; tile_y < r->num_tile_rows; tile_y++) { + for (int tile_x = 0; tile_x < r->num_tile_columns; tile_x++) { + ctu_xy(&ctu_x, &ctu_y, tile_x, tile_y, pps); + pps_add_ctus(pps, &off, ctu_x, ctu_y, r->col_width_val[tile_x], r->row_height_val[tile_y]); + } + } +} + +static int pps_slice_map(VVCPPS *pps) +{ + pps->ctb_addr_in_slice = av_calloc(pps->ctb_count, sizeof(*pps->ctb_addr_in_slice)); + if (!pps->ctb_addr_in_slice) + return AVERROR(ENOMEM); + + if (pps->r->pps_rect_slice_flag) + pps_rect_slice(pps); + else + pps_no_rect_slice(pps); + + return 0; +} + +static void pps_ref_wraparound_offset(VVCPPS *pps, const VVCSPS *sps) +{ + const H266RawPPS *r = pps->r; + + if (r->pps_ref_wraparound_enabled_flag) + pps->ref_wraparound_offset = (pps->width / sps->min_cb_size_y) - r->pps_pic_width_minus_wraparound_offset; +} + +static int pps_derive(VVCPPS *pps, const VVCSPS *sps) +{ + int ret; + + pps_chroma_qp_offset(pps); + pps_width_height(pps, sps); + + ret = pps_bd(pps); + if (ret < 0) + return ret; + + ret = pps_slice_map(pps); + if (ret < 0) + return ret; + + pps_ref_wraparound_offset(pps, sps); + + return 0; +} + +static void pps_free(FFRefStructOpaque opaque, void *obj) +{ + VVCPPS *pps = (VVCPPS *)obj; + + ff_refstruct_unref(&pps->r); + + av_freep(&pps->col_bd); + av_freep(&pps->row_bd); + av_freep(&pps->ctb_to_col_bd); + av_freep(&pps->ctb_to_row_bd); + av_freep(&pps->ctb_addr_in_slice); +} + +static const VVCPPS *pps_alloc(const H266RawPPS *rpps, const VVCSPS *sps) +{ + int ret; + VVCPPS *pps = ff_refstruct_alloc_ext(sizeof(*pps), 0, NULL, pps_free); + + if (!pps) + return NULL; + + ff_refstruct_replace(&pps->r, rpps); + + ret = pps_derive(pps, sps); + if (ret < 0) + goto fail; + + return pps; + +fail: + ff_refstruct_unref(&pps); + return NULL; +} + +static int decode_pps(VVCParamSets *ps, const H266RawPPS *rpps) +{ + int ret = 0; + const int pps_id = rpps->pps_pic_parameter_set_id; + const int sps_id = rpps->pps_seq_parameter_set_id; + const VVCPPS *old_pps = ps->pps_list[pps_id]; + const VVCPPS *pps; + + if (old_pps && old_pps->r == rpps) + return 0; + + pps = pps_alloc(rpps, ps->sps_list[sps_id]); + if (!pps) + return AVERROR(ENOMEM); + + ff_refstruct_replace(&ps->pps_list[pps_id], pps); + ff_refstruct_unref(&pps); + + return ret; +} + +static int decode_ps(VVCParamSets *ps, const CodedBitstreamH266Context *h266, void *log_ctx) +{ + const H266RawPictureHeader *ph = h266->ph; + const H266RawPPS *rpps; + const H266RawSPS *rsps; + int ret; + + if (!ph) + return AVERROR_INVALIDDATA; + + rpps = h266->pps[ph->ph_pic_parameter_set_id]; + if (!rpps) + return AVERROR_INVALIDDATA; + + rsps = h266->sps[rpps->pps_seq_parameter_set_id]; + if (!rsps) + return AVERROR_INVALIDDATA; + + ret = decode_sps(ps, rsps, log_ctx); + if (ret < 0) + return ret; + + ret = decode_pps(ps, rpps); + if (ret < 0) + return ret; + + return 0; +} + +#define WEIGHT_TABLE(x) \ + w->nb_weights[L##x] = r->num_weights_l##x; \ + for (int i = 0; i < w->nb_weights[L##x]; i++) { \ + w->weight_flag[L##x][LUMA][i] = r->luma_weight_l##x##_flag[i]; \ + w->weight_flag[L##x][CHROMA][i] = r->chroma_weight_l##x##_flag[i]; \ + w->weight[L##x][LUMA][i] = denom[LUMA] + r->delta_luma_weight_l##x[i]; \ + w->offset[L##x][LUMA][i] = r->luma_offset_l##x[i]; \ + for (int j = CB; j <= CR; j++) { \ + w->weight[L##x][j][i] = denom[CHROMA] + r->delta_chroma_weight_l##x[i][j - 1]; \ + w->offset[L##x][j][i] = 128 + r->delta_chroma_offset_l##x[i][j - 1]; \ + w->offset[L##x][j][i] -= (128 * w->weight[L##x][j][i]) >> w->log2_denom[CHROMA]; \ + w->offset[L##x][j][i] = av_clip_intp2(w->offset[L##x][j][i], 7); \ + } \ + } \ + +static void pred_weight_table(PredWeightTable *w, const H266RawPredWeightTable *r) +{ + int denom[2]; + + w->log2_denom[LUMA] = r->luma_log2_weight_denom; + w->log2_denom[CHROMA] = w->log2_denom[LUMA] + r->delta_chroma_log2_weight_denom; + denom[LUMA] = 1 << w->log2_denom[LUMA]; + denom[CHROMA] = 1 << w->log2_denom[CHROMA]; + WEIGHT_TABLE(0) + WEIGHT_TABLE(1) +} + +// 8.3.1 Decoding process for picture order count +static int ph_compute_poc(const H266RawPictureHeader *ph, const H266RawSPS *sps, const int poc_tid0, const int is_clvss) +{ + const int max_poc_lsb = 1 << (sps->sps_log2_max_pic_order_cnt_lsb_minus4 + 4); + const int prev_poc_lsb = poc_tid0 % max_poc_lsb; + const int prev_poc_msb = poc_tid0 - prev_poc_lsb; + const int poc_lsb = ph->ph_pic_order_cnt_lsb; + int poc_msb; + + if (ph->ph_poc_msb_cycle_present_flag) { + poc_msb = ph->ph_poc_msb_cycle_val * max_poc_lsb; + } else if (is_clvss) { + poc_msb = 0; + } else { + if (poc_lsb < prev_poc_lsb && prev_poc_lsb - poc_lsb >= max_poc_lsb / 2) + poc_msb = prev_poc_msb + max_poc_lsb; + else if (poc_lsb > prev_poc_lsb && poc_lsb - prev_poc_lsb > max_poc_lsb / 2) + poc_msb = prev_poc_msb - max_poc_lsb; + else + poc_msb = prev_poc_msb; + } + + return poc_msb + poc_lsb; +} + +static av_always_inline uint16_t lmcs_derive_lut_sample(uint16_t sample, + uint16_t *pivot1, uint16_t *pivot2, uint16_t *scale_coeff, const int idx, const int max) +{ + const int lut_sample = + pivot1[idx] + ((scale_coeff[idx] * (sample - pivot2[idx]) + (1<< 10)) >> 11); + return av_clip(lut_sample, 0, max - 1); +} + +//8.8.2.2 Inverse mapping process for a luma sample +static int lmcs_derive_lut(VVCLMCS *lmcs, const H266RawAPS *rlmcs, const H266RawSPS *sps) +{ + const int bit_depth = (sps->sps_bitdepth_minus8 + 8); + const int max = (1 << bit_depth); + const int org_cw = max / LMCS_MAX_BIN_SIZE; + const int shift = av_log2(org_cw); + const int off = 1 << (shift - 1); + int cw[LMCS_MAX_BIN_SIZE]; + uint16_t input_pivot[LMCS_MAX_BIN_SIZE]; + uint16_t scale_coeff[LMCS_MAX_BIN_SIZE]; + uint16_t inv_scale_coeff[LMCS_MAX_BIN_SIZE]; + int i, delta_crs; + if (bit_depth > LMCS_MAX_BIT_DEPTH) + return AVERROR_PATCHWELCOME; + + if (!rlmcs) + return AVERROR_INVALIDDATA; + + lmcs->min_bin_idx = rlmcs->lmcs_min_bin_idx; + lmcs->max_bin_idx = LMCS_MAX_BIN_SIZE - 1 - rlmcs->lmcs_min_bin_idx; + + memset(cw, 0, sizeof(cw)); + for (int i = lmcs->min_bin_idx; i <= lmcs->max_bin_idx; i++) + cw[i] = org_cw + (1 - 2 * rlmcs->lmcs_delta_sign_cw_flag[i]) * rlmcs->lmcs_delta_abs_cw[i]; + + delta_crs = (1 - 2 * rlmcs->lmcs_delta_sign_crs_flag) * rlmcs->lmcs_delta_abs_crs; + + lmcs->pivot[0] = 0; + for (i = 0; i < LMCS_MAX_BIN_SIZE; i++) { + input_pivot[i] = i * org_cw; + lmcs->pivot[i + 1] = lmcs->pivot[i] + cw[i]; + scale_coeff[i] = (cw[i] * (1 << 11) + off) >> shift; + if (cw[i] == 0) { + inv_scale_coeff[i] = 0; + lmcs->chroma_scale_coeff[i] = (1 << 11); + } else { + inv_scale_coeff[i] = org_cw * (1 << 11) / cw[i]; + lmcs->chroma_scale_coeff[i] = org_cw * (1 << 11) / (cw[i] + delta_crs); + } + } + + //derive lmcs_fwd_lut + for (uint16_t sample = 0; sample < max; sample++) { + const int idx_y = sample / org_cw; + const uint16_t fwd_sample = lmcs_derive_lut_sample(sample, lmcs->pivot, + input_pivot, scale_coeff, idx_y, max); + if (bit_depth > 8) + ((uint16_t *)lmcs->fwd_lut)[sample] = fwd_sample; + else + lmcs->fwd_lut[sample] = fwd_sample; + + } + + //derive lmcs_inv_lut + i = lmcs->min_bin_idx; + for (uint16_t sample = 0; sample < max; sample++) { + uint16_t inv_sample; + while (sample >= lmcs->pivot[i + 1] && i <= lmcs->max_bin_idx) + i++; + + inv_sample = lmcs_derive_lut_sample(sample, input_pivot, lmcs->pivot, + inv_scale_coeff, i, max); + + if (bit_depth > 8) + ((uint16_t *)lmcs->inv_lut)[sample] = inv_sample; + else + lmcs->inv_lut[sample] = inv_sample; + } + + return 0; +} + +static int ph_max_num_subblock_merge_cand(const H266RawSPS *sps, const H266RawPictureHeader *ph) +{ + if (sps->sps_affine_enabled_flag) + return 5 - sps->sps_five_minus_max_num_subblock_merge_cand; + return sps->sps_sbtmvp_enabled_flag && ph->ph_temporal_mvp_enabled_flag; +} + +static int ph_derive(VVCPH *ph, const H266RawSPS *sps, const H266RawPPS *pps, const int poc_tid0, const int is_clvss) +{ + ph->max_num_subblock_merge_cand = ph_max_num_subblock_merge_cand(sps, ph->r); + + ph->poc = ph_compute_poc(ph->r, sps, poc_tid0, is_clvss); + + if (pps->pps_wp_info_in_ph_flag) + pred_weight_table(&ph->pwt, &ph->r->ph_pred_weight_table); + + return 0; +} + +static int decode_ph(VVCFrameParamSets *fps, const H266RawPictureHeader *rph, void *rph_ref, + const int poc_tid0, const int is_clvss) +{ + int ret; + VVCPH *ph = &fps->ph; + const H266RawSPS *sps = fps->sps->r; + const H266RawPPS *pps = fps->pps->r; + + ph->r = rph; + ff_refstruct_replace(&ph->rref, rph_ref); + ret = ph_derive(ph, sps, pps, poc_tid0, is_clvss); + if (ret < 0) + return ret; + + return 0; +} + +static int decode_frame_ps(VVCFrameParamSets *fps, const VVCParamSets *ps, + const CodedBitstreamH266Context *h266, const int poc_tid0, const int is_clvss) +{ + const H266RawPictureHeader *ph = h266->ph; + const H266RawPPS *rpps; + int ret; + + if (!ph) + return AVERROR_INVALIDDATA; + + rpps = h266->pps[ph->ph_pic_parameter_set_id]; + if (!rpps) + return AVERROR_INVALIDDATA; + + ff_refstruct_replace(&fps->sps, ps->sps_list[rpps->pps_seq_parameter_set_id]); + ff_refstruct_replace(&fps->pps, ps->pps_list[rpps->pps_pic_parameter_set_id]); + + ret = decode_ph(fps, ph, h266->ph_ref, poc_tid0, is_clvss); + if (ret < 0) + return ret; + + if (ph->ph_explicit_scaling_list_enabled_flag) + ff_refstruct_replace(&fps->sl, ps->scaling_list[ph->ph_scaling_list_aps_id]); + + if (ph->ph_lmcs_enabled_flag) { + ret = lmcs_derive_lut(&fps->lmcs, ps->lmcs_list[ph->ph_lmcs_aps_id], fps->sps->r); + if (ret < 0) + return ret; + } + + for (int i = 0; i < FF_ARRAY_ELEMS(fps->alf_list); i++) + ff_refstruct_replace(&fps->alf_list[i], ps->alf_list[i]); + + return 0; +} + +static void decode_recovery_flag(VVCContext *s) +{ + if (IS_IDR(s)) + s->no_output_before_recovery_flag = 0; + else if (IS_CRA(s) || IS_GDR(s)) + s->no_output_before_recovery_flag = s->last_eos; +} + +static void decode_recovery_poc(VVCContext *s, const VVCPH *ph) +{ + if (s->no_output_before_recovery_flag) { + if (IS_GDR(s)) + s->gdr_recovery_point_poc = ph->poc + ph->r->ph_recovery_poc_cnt; + if (!GDR_IS_RECOVERED(s) && s->gdr_recovery_point_poc <= ph->poc) + GDR_SET_RECOVERED(s); + } +} + +int ff_vvc_decode_frame_ps(VVCFrameParamSets *fps, struct VVCContext *s) +{ + int ret = 0; + VVCParamSets *ps = &s->ps; + const CodedBitstreamH266Context *h266 = s->cbc->priv_data; + + ret = decode_ps(ps, h266, s->avctx); + if (ret < 0) + return ret; + + decode_recovery_flag(s); + ret = decode_frame_ps(fps, ps, h266, s->poc_tid0, IS_CLVSS(s)); + decode_recovery_poc(s, &fps->ph); + return ret; +} + +void ff_vvc_frame_ps_free(VVCFrameParamSets *fps) +{ + ff_refstruct_unref(&fps->sps); + ff_refstruct_unref(&fps->pps); + ff_refstruct_unref(&fps->ph.rref); + ff_refstruct_unref(&fps->sl); + for (int i = 0; i < FF_ARRAY_ELEMS(fps->alf_list); i++) + ff_refstruct_unref(&fps->alf_list[i]); +} + +void ff_vvc_ps_uninit(VVCParamSets *ps) +{ + int i; + + for (i = 0; i < FF_ARRAY_ELEMS(ps->scaling_list); i++) + ff_refstruct_unref(&ps->scaling_list[i]); + for (i = 0; i < FF_ARRAY_ELEMS(ps->lmcs_list); i++) + ff_refstruct_unref(&ps->lmcs_list[i]); + for (i = 0; i < FF_ARRAY_ELEMS(ps->alf_list); i++) + ff_refstruct_unref(&ps->alf_list[i]); + for (i = 0; i < FF_ARRAY_ELEMS(ps->sps_list); i++) + ff_refstruct_unref(&ps->sps_list[i]); + for (i = 0; i < FF_ARRAY_ELEMS(ps->pps_list); i++) + ff_refstruct_unref(&ps->pps_list[i]); +} + +enum { + APS_ALF, + APS_LMCS, + APS_SCALING, +}; + +static void alf_coeff(int16_t *coeff, + const uint8_t *abs, const uint8_t *sign, const int size) +{ + for (int i = 0; i < size; i++) + coeff[i] = (1 - 2 * sign[i]) * abs[i]; +} + +static void alf_coeff_cc(int16_t *coeff, + const uint8_t *mapped_abs, const uint8_t *sign) +{ + for (int i = 0; i < ALF_NUM_COEFF_CC; i++) { + int c = mapped_abs[i];; + if (c) + c = (1 - 2 * sign[i]) * (1 << (c - 1)); + coeff[i] = c; + } +} + +static void alf_luma(VVCALF *alf, const H266RawAPS *aps) +{ + if (!aps->alf_luma_filter_signal_flag) + return; + + for (int i = 0; i < ALF_NUM_FILTERS_LUMA; i++) { + const int ref = aps->alf_luma_coeff_delta_idx[i]; + const uint8_t *abs = aps->alf_luma_coeff_abs[ref]; + const uint8_t *sign = aps->alf_luma_coeff_sign[ref]; + + alf_coeff(alf->luma_coeff[i], abs, sign, ALF_NUM_COEFF_LUMA); + memcpy(alf->luma_clip_idx[i], aps->alf_luma_clip_idx[ref], + sizeof(alf->luma_clip_idx[i])); + } +} + +static void alf_chroma(VVCALF *alf, const H266RawAPS *aps) +{ + if (!aps->alf_chroma_filter_signal_flag) + return; + + alf->num_chroma_filters = aps->alf_chroma_num_alt_filters_minus1 + 1; + for (int i = 0; i < alf->num_chroma_filters; i++) { + const uint8_t *abs = aps->alf_chroma_coeff_abs[i]; + const uint8_t *sign = aps->alf_chroma_coeff_sign[i]; + + alf_coeff(alf->chroma_coeff[i], abs, sign, ALF_NUM_COEFF_CHROMA); + memcpy(alf->chroma_clip_idx[i], aps->alf_chroma_clip_idx[i], + sizeof(alf->chroma_clip_idx[i])); + } +} + +static void alf_cc(VVCALF *alf, const H266RawAPS *aps) +{ + const uint8_t (*abs[])[ALF_NUM_COEFF_CC] = + { aps->alf_cc_cb_mapped_coeff_abs, aps->alf_cc_cr_mapped_coeff_abs }; + const uint8_t (*sign[])[ALF_NUM_COEFF_CC] = + {aps->alf_cc_cb_coeff_sign, aps->alf_cc_cr_coeff_sign }; + const int signaled[] = { aps->alf_cc_cb_filter_signal_flag, aps->alf_cc_cr_filter_signal_flag}; + + alf->num_cc_filters[0] = aps->alf_cc_cb_filters_signalled_minus1 + 1; + alf->num_cc_filters[1] = aps->alf_cc_cr_filters_signalled_minus1 + 1; + + for (int idx = 0; idx < 2; idx++) { + if (signaled[idx]) { + for (int i = 0; i < alf->num_cc_filters[idx]; i++) + alf_coeff_cc(alf->cc_coeff[idx][i], abs[idx][i], sign[idx][i]); + } + } +} + +static void alf_derive(VVCALF *alf, const H266RawAPS *aps) +{ + alf_luma(alf, aps); + alf_chroma(alf, aps); + alf_cc(alf, aps); +} + +static int aps_decode_alf(const VVCALF **alf, const H266RawAPS *aps) +{ + VVCALF *a = ff_refstruct_allocz(sizeof(*a)); + if (!a) + return AVERROR(ENOMEM); + + alf_derive(a, aps); + ff_refstruct_replace(alf, a); + ff_refstruct_unref(&a); + + return 0; +} + +static int is_luma_list(const int id) +{ + return id % VVC_MAX_SAMPLE_ARRAYS == SL_START_4x4 || id == SL_START_64x64 + 1; +} + +static int derive_matrix_size(const int id) +{ + return id < SL_START_4x4 ? 2 : (id < SL_START_8x8 ? 4 : 8); +} + +// 7.4.3.20 Scaling list data semantics +static void scaling_derive(VVCScalingList *sl, const H266RawAPS *aps) +{ + for (int id = 0; id < SL_MAX_ID; id++) { + const int matrix_size = derive_matrix_size(id); + const int log2_size = log2(matrix_size); + const int list_size = matrix_size * matrix_size; + int coeff[SL_MAX_MATRIX_SIZE * SL_MAX_MATRIX_SIZE]; + const uint8_t *pred; + const int *scaling_list; + int dc = 0; + + if (aps->aps_chroma_present_flag || is_luma_list(id)) { + if (!aps->scaling_list_copy_mode_flag[id]) { + int next_coef = 0; + + if (id >= SL_START_16x16) + dc = next_coef = aps->scaling_list_dc_coef[id - SL_START_16x16]; + + for (int i = 0; i < list_size; i++) { + const int x = ff_vvc_diag_scan_x[3][3][i]; + const int y = ff_vvc_diag_scan_y[3][3][i]; + + if (!(id >= SL_START_64x64 && x >= 4 && y >= 4)) + next_coef += aps->scaling_list_delta_coef[id][i]; + coeff[i] = next_coef; + } + } + } + + //dc + if (id >= SL_START_16x16) { + if (!aps->scaling_list_copy_mode_flag[id] && !aps->scaling_list_pred_mode_flag[id]) { + sl->scaling_matrix_dc_rec[id - SL_START_16x16] = 8; + } else if (!aps->scaling_list_pred_id_delta[id]) { + sl->scaling_matrix_dc_rec[id - SL_START_16x16] = 16; + } else { + const int ref_id = id - aps->scaling_list_pred_id_delta[id]; + if (ref_id >= SL_START_16x16) + dc += sl->scaling_matrix_dc_rec[ref_id - SL_START_16x16]; + else + dc += sl->scaling_matrix_rec[ref_id][0]; + sl->scaling_matrix_dc_rec[id - SL_START_16x16] = dc & 255; + } + } + + //ac + scaling_list = aps->scaling_list_copy_mode_flag[id] ? ff_vvc_scaling_list0 : coeff; + if (!aps->scaling_list_copy_mode_flag[id] && !aps->scaling_list_pred_mode_flag[id]) + pred = ff_vvc_scaling_pred_8; + else if (!aps->scaling_list_pred_id_delta[id]) + pred = ff_vvc_scaling_pred_16; + else + pred = sl->scaling_matrix_rec[id - aps->scaling_list_pred_id_delta[id]]; + for (int i = 0; i < list_size; i++) { + const int x = ff_vvc_diag_scan_x[log2_size][log2_size][i]; + const int y = ff_vvc_diag_scan_y[log2_size][log2_size][i]; + const int off = y * matrix_size + x; + sl->scaling_matrix_rec[id][off] = (pred[off] + scaling_list[i]) & 255; + } + } +} + +static int aps_decode_scaling(const VVCScalingList **scaling, const H266RawAPS *aps) +{ + VVCScalingList *sl = ff_refstruct_allocz(sizeof(*sl)); + if (!sl) + return AVERROR(ENOMEM); + + scaling_derive(sl, aps); + ff_refstruct_replace(scaling, sl); + ff_refstruct_unref(&sl); + + return 0; +} + +int ff_vvc_decode_aps(VVCParamSets *ps, const CodedBitstreamUnit *unit) +{ + const H266RawAPS *aps = unit->content_ref; + int ret = 0; + + if (!aps) + return AVERROR_INVALIDDATA; + + switch (aps->aps_params_type) { + case APS_ALF: + ret = aps_decode_alf(&ps->alf_list[aps->aps_adaptation_parameter_set_id], aps); + break; + case APS_LMCS: + ff_refstruct_replace(&ps->lmcs_list[aps->aps_adaptation_parameter_set_id], aps); + break; + case APS_SCALING: + ret = aps_decode_scaling(&ps->scaling_list[aps->aps_adaptation_parameter_set_id], aps); + break; + } + + return ret; +} + +static void sh_slice_address(VVCSH *sh, const H266RawSPS *sps, const VVCPPS *pps) +{ + const int slice_address = sh->r->sh_slice_address; + + if (pps->r->pps_rect_slice_flag) { + int pic_level_slice_idx = slice_address; + for (int j = 0; j < sh->r->curr_subpic_idx; j++) + pic_level_slice_idx += pps->r->num_slices_in_subpic[j]; + sh->ctb_addr_in_curr_slice = pps->ctb_addr_in_slice + pps->slice_start_offset[pic_level_slice_idx]; + sh->num_ctus_in_curr_slice = pps->num_ctus_in_slice[pic_level_slice_idx]; + } else { + int tile_x = slice_address % pps->r->num_tile_columns; + int tile_y = slice_address / pps->r->num_tile_columns; + const int slice_start_ctb = pps->row_bd[tile_y] * pps->ctb_width + pps->col_bd[tile_x] * pps->r->row_height_val[tile_y]; + + sh->ctb_addr_in_curr_slice = pps->ctb_addr_in_slice + slice_start_ctb; + + sh->num_ctus_in_curr_slice = 0; + for (int tile_idx = slice_address; tile_idx <= slice_address + sh->r->sh_num_tiles_in_slice_minus1; tile_idx++) { + tile_x = tile_idx % pps->r->num_tile_columns; + tile_y = tile_idx / pps->r->num_tile_columns; + sh->num_ctus_in_curr_slice += pps->r->row_height_val[tile_y] * pps->r->col_width_val[tile_x]; + } + } +} + +static void sh_qp_y(VVCSH *sh, const H266RawPPS *pps, const H266RawPictureHeader *ph) +{ + const int init_qp = pps->pps_init_qp_minus26 + 26; + + if (!pps->pps_qp_delta_info_in_ph_flag) + sh->slice_qp_y = init_qp + sh->r->sh_qp_delta; + else + sh->slice_qp_y = init_qp + ph->ph_qp_delta; +} + +static void sh_inter(VVCSH *sh, const H266RawSPS *sps, const H266RawPPS *pps) +{ + const H266RawSliceHeader *rsh = sh->r; + + if (!pps->pps_wp_info_in_ph_flag && + ((pps->pps_weighted_pred_flag && IS_P(rsh)) || + (pps->pps_weighted_bipred_flag && IS_B(rsh)))) + pred_weight_table(&sh->pwt, &rsh->sh_pred_weight_table); +} + +static void sh_deblock_offsets(VVCSH *sh) +{ + const H266RawSliceHeader *r = sh->r; + + if (!r->sh_deblocking_filter_disabled_flag) { + sh->deblock.beta_offset[LUMA] = r->sh_luma_beta_offset_div2 << 1; + sh->deblock.tc_offset[LUMA] = r->sh_luma_tc_offset_div2 << 1; + sh->deblock.beta_offset[CB] = r->sh_cb_beta_offset_div2 << 1; + sh->deblock.tc_offset[CB] = r->sh_cb_tc_offset_div2 << 1; + sh->deblock.beta_offset[CR] = r->sh_cr_beta_offset_div2 << 1; + sh->deblock.tc_offset[CR] = r->sh_cr_tc_offset_div2 << 1; + } +} + +static void sh_partition_constraints(VVCSH *sh, const H266RawSPS *sps, const H266RawPictureHeader *ph) +{ + const int min_cb_log2_size_y = sps->sps_log2_min_luma_coding_block_size_minus2 + 2; + int min_qt_log2_size_y[2]; + + if (IS_I(sh->r)) { + min_qt_log2_size_y[LUMA] = (min_cb_log2_size_y + ph->ph_log2_diff_min_qt_min_cb_intra_slice_luma); + min_qt_log2_size_y[CHROMA] = (min_cb_log2_size_y + ph->ph_log2_diff_min_qt_min_cb_intra_slice_chroma); + + sh->max_bt_size[LUMA] = 1 << (min_qt_log2_size_y[LUMA] + ph->ph_log2_diff_max_bt_min_qt_intra_slice_luma); + sh->max_bt_size[CHROMA] = 1 << (min_qt_log2_size_y[CHROMA]+ ph->ph_log2_diff_max_bt_min_qt_intra_slice_chroma); + + sh->max_tt_size[LUMA] = 1 << (min_qt_log2_size_y[LUMA] + ph->ph_log2_diff_max_tt_min_qt_intra_slice_luma); + sh->max_tt_size[CHROMA] = 1 << (min_qt_log2_size_y[CHROMA]+ ph->ph_log2_diff_max_tt_min_qt_intra_slice_chroma); + + sh->max_mtt_depth[LUMA] = ph->ph_max_mtt_hierarchy_depth_intra_slice_luma; + sh->max_mtt_depth[CHROMA] = ph->ph_max_mtt_hierarchy_depth_intra_slice_chroma; + + sh->cu_qp_delta_subdiv = ph->ph_cu_qp_delta_subdiv_intra_slice; + sh->cu_chroma_qp_offset_subdiv = ph->ph_cu_chroma_qp_offset_subdiv_intra_slice; + } else { + for (int i = LUMA; i <= CHROMA; i++) { + min_qt_log2_size_y[i] = (min_cb_log2_size_y + ph->ph_log2_diff_min_qt_min_cb_inter_slice); + sh->max_bt_size[i] = 1 << (min_qt_log2_size_y[i] + ph->ph_log2_diff_max_bt_min_qt_inter_slice); + sh->max_tt_size[i] = 1 << (min_qt_log2_size_y[i] + ph->ph_log2_diff_max_tt_min_qt_inter_slice); + sh->max_mtt_depth[i] = ph->ph_max_mtt_hierarchy_depth_inter_slice; + } + + sh->cu_qp_delta_subdiv = ph->ph_cu_qp_delta_subdiv_inter_slice; + sh->cu_chroma_qp_offset_subdiv = ph->ph_cu_chroma_qp_offset_subdiv_inter_slice; + } + + sh->min_qt_size[LUMA] = 1 << min_qt_log2_size_y[LUMA]; + sh->min_qt_size[CHROMA] = 1 << min_qt_log2_size_y[CHROMA]; +} + +static void sh_entry_points(VVCSH *sh, const H266RawSPS *sps, const VVCPPS *pps) +{ + if (sps->sps_entry_point_offsets_present_flag) { + for (int i = 1, j = 0; i < sh->num_ctus_in_curr_slice; i++) { + const int pre_ctb_addr_x = sh->ctb_addr_in_curr_slice[i - 1] % pps->ctb_width; + const int pre_ctb_addr_y = sh->ctb_addr_in_curr_slice[i - 1] / pps->ctb_width; + const int ctb_addr_x = sh->ctb_addr_in_curr_slice[i] % pps->ctb_width; + const int ctb_addr_y = sh->ctb_addr_in_curr_slice[i] / pps->ctb_width; + if (pps->ctb_to_row_bd[ctb_addr_y] != pps->ctb_to_row_bd[pre_ctb_addr_y] || + pps->ctb_to_col_bd[ctb_addr_x] != pps->ctb_to_col_bd[pre_ctb_addr_x] || + (ctb_addr_y != pre_ctb_addr_y && sps->sps_entropy_coding_sync_enabled_flag)) { + sh->entry_point_start_ctu[j++] = i; + } + } + } +} + +static int sh_derive(VVCSH *sh, const VVCFrameParamSets *fps) +{ + const H266RawSPS *sps = fps->sps->r; + const H266RawPPS *pps = fps->pps->r; + const H266RawPictureHeader *ph = fps->ph.r; + + sh_slice_address(sh, sps, fps->pps); + sh_inter(sh, sps, pps); + sh_qp_y(sh, pps, ph); + sh_deblock_offsets(sh); + sh_partition_constraints(sh, sps, ph); + sh_entry_points(sh, sps, fps->pps); + + return 0; +} + +int ff_vvc_decode_sh(VVCSH *sh, const VVCFrameParamSets *fps, const CodedBitstreamUnit *unit) +{ + int ret; + + if (!fps->sps || !fps->pps) + return AVERROR_INVALIDDATA; + + ff_refstruct_replace(&sh->r, unit->content_ref); + + ret = sh_derive(sh, fps); + if (ret < 0) + return ret; + + return 0; +} diff --git a/libavcodec/vvc/vvc_ps.h b/libavcodec/vvc/vvc_ps.h new file mode 100644 index 0000000000..43c72d5483 --- /dev/null +++ b/libavcodec/vvc/vvc_ps.h @@ -0,0 +1,263 @@ +/* + * VVC parameter set parser + * + * Copyright (C) 2023 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VVC_PS_H +#define AVCODEC_VVC_PS_H + +#include "libavcodec/cbs_h266.h" +#include "libavcodec/vvc.h" + +#define IS_IDR(s) ((s)->vcl_unit_type == VVC_IDR_W_RADL || (s)->vcl_unit_type == VVC_IDR_N_LP) +#define IS_CRA(s) ((s)->vcl_unit_type == VVC_CRA_NUT) +#define IS_IRAP(s) (IS_IDR(s) || IS_CRA(s)) +#define IS_GDR(s) ((s)->vcl_unit_type == VVC_GDR_NUT) +#define IS_CVSS(s) (IS_IRAP(s)|| IS_GDR(s)) +#define IS_CLVSS(s) (IS_CVSS(s) && s->no_output_before_recovery_flag) +#define IS_RASL(s) ((s)->vcl_unit_type == VVC_RASL_NUT) +#define IS_RADL(s) ((s)->vcl_unit_type == VVC_RADL_NUT) + +#define IS_I(rsh) ((rsh)->sh_slice_type == VVC_SLICE_TYPE_I) +#define IS_P(rsh) ((rsh)->sh_slice_type == VVC_SLICE_TYPE_P) +#define IS_B(rsh) ((rsh)->sh_slice_type == VVC_SLICE_TYPE_B) + +#define INV_POC INT_MIN +#define GDR_IS_RECOVERED(s) (s->gdr_recovery_point_poc == INV_POC) +#define GDR_SET_RECOVERED(s) (s->gdr_recovery_point_poc = INV_POC) + +#define LMCS_MAX_BIT_DEPTH 12 +#define LMCS_MAX_LUT_SIZE (1 << LMCS_MAX_BIT_DEPTH) +#define LMCS_MAX_BIN_SIZE 16 +#define LADF_MAX_INTERVAL 5 + +enum { + CHROMA_FORMAT_MONO, + CHROMA_FORMAT_420, + CHROMA_FORMAT_422, + CHROMA_FORMAT_444, +}; + +typedef struct VVCSPS { + const H266RawSPS *r; ///< RefStruct reference + + //derived values + uint16_t width; + uint16_t height; + uint8_t hshift[VVC_MAX_SAMPLE_ARRAYS]; + uint8_t vshift[VVC_MAX_SAMPLE_ARRAYS]; + uint32_t max_pic_order_cnt_lsb; ///< MaxPicOrderCntLsb + + uint8_t pixel_shift; + enum AVPixelFormat pix_fmt; + + uint8_t bit_depth; ///< BitDepth + uint8_t qp_bd_offset; ///< QpBdOffset + uint8_t ctb_log2_size_y; ///< CtbLog2SizeY + uint8_t ctb_size_y; ///< CtbSizeY + uint8_t min_cb_log2_size_y; ///< MinCbLog2SizeY + uint8_t min_cb_size_y; ///< MinCbSizeY + uint8_t max_tb_size_y; ///< MaxTbSizeY + uint8_t max_ts_size; ///< MaxTsSize + uint8_t max_num_merge_cand; ///< MaxNumMergeCand + uint8_t max_num_ibc_merge_cand; ///< MaxNumIbcMergeCand + uint8_t max_num_gpm_merge_cand; ///< MaxNumGpmMergeCand + uint8_t num_ladf_intervals; ///< sps_num_ladf_intervals_minus2 + 2; + uint32_t ladf_interval_lower_bound[LADF_MAX_INTERVAL]; ///< SpsLadfIntervalLowerBound[] + uint8_t log2_parallel_merge_level; ///< sps_log2_parallel_merge_level_minus2 + 2; + uint8_t log2_transform_range; ///< Log2TransformRange + int8_t chroma_qp_table[3][VVC_MAX_POINTS_IN_QP_TABLE]; ///< ChromaQpTable +} VVCSPS; + +typedef struct DBParams { + int8_t beta_offset[VVC_MAX_SAMPLE_ARRAYS]; + int8_t tc_offset[VVC_MAX_SAMPLE_ARRAYS]; +} DBParams; + +typedef struct VVCPPS { + const H266RawPPS *r; ///< RefStruct reference + + //derived value; + int8_t chroma_qp_offset[3]; ///< pps_cb_qp_offset, pps_cr_qp_offset, pps_joint_cbcr_qp_offset_value + int8_t chroma_qp_offset_list[6][3]; ///< pps_cb_qp_offset_list, pps_cr_qp_offset_list, pps_joint_cbcr_qp_offset_list + + uint16_t width; + uint16_t height; + + uint16_t slice_start_offset[VVC_MAX_SLICES]; + uint16_t num_ctus_in_slice [VVC_MAX_SLICES]; + + uint16_t min_cb_width; + uint16_t min_cb_height; + + uint16_t ctb_width; + uint16_t ctb_height; + uint32_t ctb_count; + + uint16_t min_pu_width; + uint16_t min_pu_height; + uint16_t min_tu_width; + uint16_t min_tu_height; + + uint32_t *ctb_addr_in_slice; ///< CtbAddrInCurrSlice for entire picture + uint16_t *col_bd; + uint16_t *row_bd; + uint16_t *ctb_to_col_bd; + uint16_t *ctb_to_row_bd; + + uint16_t width32; ///< width in 32 pixels + uint16_t height32; ///< height in 32 pixels + uint16_t width64; ///< width in 64 pixels + uint16_t height64; ///< height in 64 pixels + + uint16_t ref_wraparound_offset; ///< PpsRefWraparoundOffset + +} VVCPPS; + +#define MAX_WEIGHTS 15 +typedef struct PredWeightTable { + uint8_t log2_denom[2]; ///< luma_log2_weight_denom, ChromaLog2WeightDenom + + uint8_t nb_weights[2]; ///< num_l0_weights, num_l1_weights + uint8_t weight_flag[2][2][MAX_WEIGHTS]; ///< luma_weight_l0_flag, chroma_weight_l0_flag, + ///< luma_weight_l1_flag, chroma_weight_l1_flag, + int16_t weight[2][VVC_MAX_SAMPLE_ARRAYS][MAX_WEIGHTS]; ///< LumaWeightL0, LumaWeightL1, ChromaWeightL0, ChromaWeightL1 + int16_t offset[2][VVC_MAX_SAMPLE_ARRAYS][MAX_WEIGHTS]; ///< luma_offset_l0, luma_offset_l1, ChromaOffsetL0, ChromaOffsetL1 +} PredWeightTable; + +typedef struct VVCPH { + const H266RawPictureHeader *r; + void *rref; ///< RefStruct reference, backing ph above + + //derived values + uint32_t max_num_subblock_merge_cand; ///< MaxNumSubblockMergeCand + int32_t poc; ///< PicOrderCntVal + PredWeightTable pwt; +} VVCPH; + +#define ALF_NUM_FILTERS_LUMA 25 +#define ALF_NUM_FILTERS_CHROMA 8 +#define ALF_NUM_FILTERS_CC 5 + +#define ALF_NUM_COEFF_LUMA 12 +#define ALF_NUM_COEFF_CHROMA 6 +#define ALF_NUM_COEFF_CC 7 + +typedef struct VVCALF { + int16_t luma_coeff [ALF_NUM_FILTERS_LUMA][ALF_NUM_COEFF_LUMA]; + uint8_t luma_clip_idx [ALF_NUM_FILTERS_LUMA][ALF_NUM_COEFF_LUMA]; + + uint8_t num_chroma_filters; + int16_t chroma_coeff [ALF_NUM_FILTERS_CHROMA][ALF_NUM_COEFF_CHROMA]; + uint8_t chroma_clip_idx[ALF_NUM_FILTERS_CHROMA][ALF_NUM_COEFF_CHROMA]; + + uint8_t num_cc_filters[2]; ///< alf_cc_cb_filters_signalled_minus1 + 1, alf_cc_cr_filters_signalled_minus1 + 1 + int16_t cc_coeff[2][ALF_NUM_FILTERS_CC][ALF_NUM_COEFF_CC]; +} VVCALF; + +enum { + SL_START_2x2 = 0, + SL_START_4x4 = 2, + SL_START_8x8 = 8, + SL_START_16x16 = 14, + SL_START_32x32 = 20, + SL_START_64x64 = 26, + SL_MAX_ID = 28, +}; + +#define SL_MAX_MATRIX_SIZE 8 + +typedef struct VVCScalingList { + uint8_t scaling_matrix_rec[SL_MAX_ID][SL_MAX_MATRIX_SIZE * SL_MAX_MATRIX_SIZE]; ///< ScalingMatrixRec + uint8_t scaling_matrix_dc_rec[SL_MAX_ID - SL_START_16x16]; ///< ScalingMatrixDcRec[refId − 14] +} VVCScalingList; + +typedef struct VVCLMCS { + uint8_t min_bin_idx; + uint8_t max_bin_idx; + + //*2 for high depth + uint8_t fwd_lut[LMCS_MAX_LUT_SIZE * 2]; + uint8_t inv_lut[LMCS_MAX_LUT_SIZE * 2]; + + uint16_t pivot[LMCS_MAX_BIN_SIZE + 1]; + uint16_t chroma_scale_coeff[LMCS_MAX_BIN_SIZE]; +} VVCLMCS; + +#define VVC_MAX_ALF_COUNT 8 +#define VVC_MAX_LMCS_COUNT 4 +#define VVC_MAX_SL_COUNT 8 + +typedef struct VVCParamSets { + const VVCSPS *sps_list[VVC_MAX_SPS_COUNT]; ///< RefStruct reference + const VVCPPS *pps_list[VVC_MAX_PPS_COUNT]; ///< RefStruct reference + const VVCALF *alf_list[VVC_MAX_ALF_COUNT]; ///< RefStruct reference + const H266RawAPS *lmcs_list[VVC_MAX_LMCS_COUNT]; ///< RefStruct reference + const VVCScalingList *scaling_list[VVC_MAX_SL_COUNT]; ///< RefStruct reference +} VVCParamSets; + +typedef struct VVCFrameParamSets { + const VVCSPS *sps; ///< RefStruct reference + const VVCPPS *pps; ///< RefStruct reference + VVCPH ph; + const VVCALF *alf_list[VVC_MAX_ALF_COUNT]; ///< RefStruct reference + VVCLMCS lmcs; + const VVCScalingList *sl; ///< RefStruct reference +} VVCFrameParamSets; + +typedef struct VVCSH { + const H266RawSliceHeader *r; ///< RefStruct reference + + // derived values + // ctu address + uint32_t num_ctus_in_curr_slice; ///< NumCtusInCurrSlice + const uint32_t* ctb_addr_in_curr_slice; ///< CtbAddrInCurrSlice + + // inter + PredWeightTable pwt; + int8_t ref_idx_sym[2]; ///< RefIdxSymL0, RefIdxSymL1 + + // qp_y + int8_t slice_qp_y; ///< SliceQpY + + // deblock_offsets + DBParams deblock; + + // partition constrains + uint8_t min_qt_size[2]; ///< MinQtSizeY, MinQtSizeC + uint8_t max_bt_size[2]; ///< MaxBtSizeY, MaxBtSizeC + uint8_t max_tt_size[2]; ///< MaxTtSizeY, MaxTtSizeC + uint8_t max_mtt_depth[2]; ///< MaxMttDepthY, MaxMttDepthC + uint8_t cu_qp_delta_subdiv; ///< CuQpDeltaSubdiv + uint8_t cu_chroma_qp_offset_subdiv; ///< CuChromaQpOffsetSubdiv + + // entries + uint32_t entry_point_start_ctu[VVC_MAX_ENTRY_POINTS]; ///< entry point start in ctu_addr +} VVCSH; + +struct VVCContext; + +int ff_vvc_decode_frame_ps(VVCFrameParamSets *fps, struct VVCContext *s); +int ff_vvc_decode_aps(VVCParamSets *ps, const CodedBitstreamUnit *unit); +int ff_vvc_decode_sh(VVCSH *sh, const VVCFrameParamSets *ps, const CodedBitstreamUnit *unit); +void ff_vvc_frame_ps_free(VVCFrameParamSets *fps); +void ff_vvc_ps_uninit(VVCParamSets *ps); + +#endif /* AVCODEC_VVC_PS_H */ diff --git a/libavcodec/vvc/vvcdec.h b/libavcodec/vvc/vvcdec.h index 2823faee75..fbbedbec00 100644 --- a/libavcodec/vvc/vvcdec.h +++ b/libavcodec/vvc/vvcdec.h @@ -26,6 +26,8 @@ #include "libavcodec/vvc.h" +#include "vvc_ps.h" + #define LUMA 0 #define CHROMA 1 #define CB 1 @@ -175,6 +177,11 @@ typedef struct VVCFrameContext { typedef struct VVCContext { struct AVCodecContext *avctx; + CodedBitstreamContext *cbc; + CodedBitstreamFragment current_frame; + + VVCParamSets ps; + int temporal_id; ///< temporal_id_plus1 - 1 int poc_tid0; From patchwork Sun Nov 12 10:35:16 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 44625 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:92a5:b0:181:818d:5e7f with SMTP id q37csp728322pzg; Sun, 12 Nov 2023 02:37:12 -0800 (PST) X-Google-Smtp-Source: AGHT+IEFfBrxLjK2wzt0wQ8XuhRg5cEQXhOYvjC1wURRAhnq+1wfeFgsMFOiW9E95ai23WslYSSY X-Received: by 2002:a17:906:10d0:b0:9be:f78a:d438 with SMTP id v16-20020a17090610d000b009bef78ad438mr2535548ejv.5.1699785431788; Sun, 12 Nov 2023 02:37:11 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id u27-20020a170906069b00b009be8c9d7138si1564888ejb.360.2023.11.12.02.37.11; Sun, 12 Nov 2023 02:37:11 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@outlook.com header.s=selector1 header.b=QfimG5K+; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 3D71868CC95; Sun, 12 Nov 2023 12:36:22 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from APC01-PSA-obe.outbound.protection.outlook.com (mail-psaapc01olkn2037.outbound.protection.outlook.com [40.92.52.37]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 1F86468CC4C for ; Sun, 12 Nov 2023 12:36:13 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=Q7mE0Uf3wFnBZRpGoIme28O+fEBOINhVzI69wLtW1SiJNFE47YSwJGfctdO4FT8HumG8PaFv9AWgZWf6cXwRNN6BYv4CMmnxCXNQFrZ91BTTGgScxG5NfV0F3wsdu0W5/DMpTdOq2dyc8hOCKoXNfLxY9bVtXXDv7KH8nqwfeH3tSN3ibIG8B1Jd6olx+Zdih3pDkhsiOymulJjvz7H3dVFf/gSvHsnt3Hji4qhFL74/GzjPwoTJDoXq8tNPzfneEOypISRY7BGhGHRPLlwli0IRI5fBkQawok8kskJVGBxxHdjNzsU2EXD5SS/IRJ/66i1L42mD7RmFrGJakuFK/w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=z2QVz+Az87lnprzgxEGkkzbufzrAJK07fEKsezbMkUw=; b=kEoQnqXqaaZ/QeEROJD4WmAGi+DnOJa11qG9nhi04L/LITdFkMdSAlthrk2korI9lvN5NVTgq9cpIXU1tVapUSF5RKoeUBJ8oEYOHTJCwmj91wsMXS+XKP8tOzWaxn/ftpSXA61wbhIZSSXTgbcyMkf0DeMImLX9/P6UlQYMY6a5QeI6bInQK2ietDxmeblMZ659EyFoQJps7GXNXTUSI2gC2jP5nN0vztQa4Fa+utNlX/XZOPlO4EVnLsd8+rJgQMip85I+ok+rHjHTYJBaOc7E93GrNXfjpnu+hj/mY2cx7PXeBVX0XTT7eg8H6gmuTvsyd086zaVDDSO3VB2WyA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=outlook.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=z2QVz+Az87lnprzgxEGkkzbufzrAJK07fEKsezbMkUw=; b=QfimG5K+uS4Rce4BmYv1/eGQIRMVO/hol7WM75ch2/6keiCI3H5tQaGQwufYDTZ8uDXg2YejDDqhroCmxIINcAYex0quljtDKwdQ9p4nTiKfo3wHlEQgxAJ7RdMqS8URXUwalUvCSDwPmGZA2tNsJ0CLlzmPe0smbXt5iRz/9cRguHAvWN8v1UtJoDKRblizhLquGV4M0YPiELEOnCYykrnOOT66VgtIUYKXLRrcPyNNofGV5I6n32MYsm9C5TLgTe7VvvV+wx4HJex8aFUZtW5SlpIFUFfIpqWUKepZC1BU6O0lxdn6YR91dlC8vmHJdqhP3tiAJ8tPiSF4muf5xw== Received: from TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) by TYSPR06MB6768.apcprd06.prod.outlook.com (2603:1096:400:475::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6954.29; Sun, 12 Nov 2023 10:35:56 +0000 Received: from TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f]) by TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f%4]) with mapi id 15.20.6954.028; Sun, 12 Nov 2023 10:35:56 +0000 From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Nov 2023 18:35:16 +0800 Message-ID: X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231112103526.11245-1-nuomi2021@gmail.com> References: <20231112103526.11245-1-nuomi2021@gmail.com> X-TMN: [m4VwshQZKbFf18EbTIaLEppaorxL92a5] X-ClientProxiedBy: TYWP286CA0030.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:262::20) To TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) X-Microsoft-Original-Message-ID: <20231112103526.11245-4-nuomi2021@gmail.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 2 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: TYSPR06MB6433:EE_|TYSPR06MB6768:EE_ X-MS-Office365-Filtering-Correlation-Id: bc6ed160-287e-489f-f504-08dbe36b2759 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: U6L5D7Ziq7H2MJwAA0u4rkm2aI+oFbVtI5M9THpNSe3SvGwl9Z63PRRqVFHp1AxYJwLBVjkX8BF3XnM1jhalPyWrJCvvZ0AA9OUpDgF6mAifh8RJRD8MfxohDu3YQKihp7R3gVQ338U331Bd30SxKqg9rtvHS9YiGojz0X60FaE2tqsQzMl+SUgvk3K2orGVcX9V4VMlVUWqtH8Bfcrku5cXcZl2pRSbF2q+eoN1wFp0pxSvtCX5zSa2H916jqVdShdSzSrMV8J95otlByRCfBWhB0+4LNtmh/8lNZqK/TvuDL2DJfpeXqVWpodjsaj1RwliXDfsUhidP0WnqUZjx5lt8+8ze0mM2og0hdVSs6gW7UznWxMTJ0N7Uqi7a4ywm3GES0gXL2U0pvz42Siql5NtifOgxoTphk/0ulyxPtU41NSocj2dFDiYfR9Dcf4/XbeZbuf+QdmbKjpywgzNtYaJ8U4TKOWzAQNgRG1vOeRQ6UNenYR6eAw6QA1U3lb0NDAHNhNbfCDtcGCHFx4KrW8XI7SOkBSA3OAOwfY1+LWz6+rROnqut421PErprKBwKXYh3dAeSVG2U0Fn+XDLviSULnGW74XsAI8kvTZW4nSb6g/mBNckBilZZ3EW48Bc X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?q?sroCW0nC7/Zp3uQoJNGnj8nwlik4?= =?utf-8?q?sO4l77RR4WXp/dgcUKgtww9q/VPy69o0qqbfygaetix1QKEQP4TQ1t9qEeCZVomEo?= =?utf-8?q?0RQyFqMMim/SBoXE8JJkbpBV9By9uN5AMiLQ/6gl6Ff/v2RicmTz4uifiqvE73J5c?= =?utf-8?q?C3RmRvWzFUp7LBePZGEaVk0/VNMk+S4IeR8y4fBLb6qHaOEK2MqPPtUkBNz4iz6D+?= =?utf-8?q?mIVNrQ0YqIc5kUVpYQPaKjzUr9anhrY+9tHDubmeSm+ZctYekwr/wniKayD9j2kvV?= =?utf-8?q?GLvs8CZPv4zir8hcfl/4WZyhZxFTdHi71oqZh7JVYbXqUGUFWM1ROBBjIjszuyVEu?= =?utf-8?q?W83Qs1omEfnKJxGlyaBdBoY7AlzYUlcpPjjY41sbH52Skvm3CNHV/7zvmDDszbM2L?= =?utf-8?q?R74kW7S5y/RMK3vNmS2WC5TXYALnwMdW13Cn2f2bxZN+68KsJN0jMLUnziy598lKO?= =?utf-8?q?pOVdHjdKon5rg5H4EDToRhm/nUxyj5pehN+0bojcIigANPwYYm+eQGOpDlaN3AaCZ?= =?utf-8?q?ZjLYrD7o2S+FJtA2H7TSREStDbHiCyrQmXvBrBJfBurdQyZ6C2cJWI/ESqqUryPIk?= =?utf-8?q?d2LDyyB0SpXJprYm2TabFmZX1qa4tYpf9JA4S8SL59kXFYpHdjmbFwR/dRxXdI664?= =?utf-8?q?c1OOwAScdFt7qjB7Cv0iVZY/AuV33nZqWdxJ4++CQcRrjuNJGxkS2c8hlT5Gcjcho?= =?utf-8?q?HMFKMPA6TvG/Yf8RJv8MloguFQtMDLBsYMf39X/4fxDjEORpJ5Njj2bR4RQIJxLdY?= =?utf-8?q?wakI+hGtG1K4LzPEWX7RSgT5tR6jLeG8YPwX7NID2HlGDcCZCPy54bA0Qo3P9w0rA?= =?utf-8?q?RMt5rGos+SIstlUIQx9O1+FVch73OI9+YSORs+WnDWQxo1y5ZA++6w1+d8R9Ie6Cx?= =?utf-8?q?jXqcc3z8Kf7ilZqkVsxZUtGXPnIs59/JKKJB5TfDQpfc+k2z7jgg7CzER5gXnfkMI?= =?utf-8?q?0XSHpsDagL5KQTNMdqTByi04v+HuujQhsyX4SFkGn2iXnfvTYlypRiS7yksaltGjo?= =?utf-8?q?l7Z2Rl0o9tvOjMh0rdRlPCxnsUzzkqvF9+1rK4XPzqVSyQehOJirH1i9z4WtvnKq+?= =?utf-8?q?Sflb3UvP3+MQqdMApgzS5/R2bX4LboJopLLFxp/g+BiCBDKetZmSKnR5cQcpUO8XW?= =?utf-8?q?gm5NzX8m1j+NYXz6rtO2MHpytPht4TgJbsEzrxA+H5npJHF7QjRb9TglxpmT31VWL?= =?utf-8?q?4C1k4ToL8qOqX73Z9?= X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: bc6ed160-287e-489f-f504-08dbe36b2759 X-MS-Exchange-CrossTenant-AuthSource: TYSPR06MB6433.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Nov 2023 10:35:56.7665 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYSPR06MB6768 Subject: [FFmpeg-devel] [PATCH v5 04/14] vvcdec: add cabac decoder X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Nuo Mi Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: DQLKKVTSOXh1 add Context-based Adaptive Binary Arithmetic Coding (CABAC) decoder --- libavcodec/vvc/Makefile | 2 + libavcodec/vvc/vvc_cabac.c | 2484 ++++++++++++++++++++++++++++++++++++ libavcodec/vvc/vvc_cabac.h | 126 ++ libavcodec/vvc/vvc_ctu.c | 32 + libavcodec/vvc/vvc_ctu.h | 463 +++++++ libavcodec/vvc/vvcdec.h | 7 + 6 files changed, 3114 insertions(+) create mode 100644 libavcodec/vvc/vvc_cabac.c create mode 100644 libavcodec/vvc/vvc_cabac.h create mode 100644 libavcodec/vvc/vvc_ctu.c create mode 100644 libavcodec/vvc/vvc_ctu.h diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile index b2b187ac6f..6c03ba19ac 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -2,5 +2,7 @@ clean:: $(RM) $(CLEANSUFFIXES:%=libavcodec/vvc/%) OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ + vvc/vvc_cabac.o \ + vvc/vvc_ctu.o \ vvc/vvc_data.o \ vvc/vvc_ps.o \ diff --git a/libavcodec/vvc/vvc_cabac.c b/libavcodec/vvc/vvc_cabac.c new file mode 100644 index 0000000000..7275f44c4d --- /dev/null +++ b/libavcodec/vvc/vvc_cabac.c @@ -0,0 +1,2484 @@ +/* + * VVC CABAC decoder + * + * Copyright (C) 2021 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "libavcodec/cabac_functions.h" + +#include "vvc_cabac.h" +#include "vvc_ctu.h" +#include "vvc_data.h" + +#define CABAC_MAX_BIN 31 + +#define CNU 35 + +enum SyntaxElement { + ALF_CTB_FLAG = 0, + ALF_USE_APS_FLAG = ALF_CTB_FLAG + 9, + ALF_CTB_CC_CB_IDC, + ALF_CTB_CC_CR_IDC = ALF_CTB_CC_CB_IDC + 3, + ALF_CTB_FILTER_ALT_IDX = ALF_CTB_CC_CR_IDC + 3, + SAO_MERGE_FLAG = ALF_CTB_FILTER_ALT_IDX + 2, + SAO_TYPE_IDX, + SPLIT_CU_FLAG, + SPLIT_QT_FLAG = SPLIT_CU_FLAG + 9, + MTT_SPLIT_CU_VERTICAL_FLAG = SPLIT_QT_FLAG + 6, + MTT_SPLIT_CU_BINARY_FLAG = MTT_SPLIT_CU_VERTICAL_FLAG + 5, + NON_INTER_FLAG = MTT_SPLIT_CU_BINARY_FLAG + 4, + CU_SKIP_FLAG = NON_INTER_FLAG + 2, + PRED_MODE_IBC_FLAG = CU_SKIP_FLAG + 3, + PRED_MODE_FLAG = PRED_MODE_IBC_FLAG + 3, + PRED_MODE_PLT_FLAG = PRED_MODE_FLAG + 2, + CU_ACT_ENABLED_FLAG, + INTRA_BDPCM_LUMA_FLAG, + INTRA_BDPCM_LUMA_DIR_FLAG, + INTRA_MIP_FLAG, + INTRA_LUMA_REF_IDX = INTRA_MIP_FLAG + 4, + INTRA_SUBPARTITIONS_MODE_FLAG = INTRA_LUMA_REF_IDX + 2, + INTRA_SUBPARTITIONS_SPLIT_FLAG, + INTRA_LUMA_MPM_FLAG, + INTRA_LUMA_NOT_PLANAR_FLAG, + INTRA_BDPCM_CHROMA_FLAG = INTRA_LUMA_NOT_PLANAR_FLAG + 2, + INTRA_BDPCM_CHROMA_DIR_FLAG, + CCLM_MODE_FLAG, + CCLM_MODE_IDX, + INTRA_CHROMA_PRED_MODE, + GENERAL_MERGE_FLAG, + INTER_PRED_IDC, + INTER_AFFINE_FLAG = INTER_PRED_IDC + 6, + CU_AFFINE_TYPE_FLAG = INTER_AFFINE_FLAG + 3, + SYM_MVD_FLAG, + REF_IDX_LX, + MVP_LX_FLAG = REF_IDX_LX + 2, + AMVR_FLAG, + AMVR_PRECISION_IDX = AMVR_FLAG + 2, + BCW_IDX = AMVR_PRECISION_IDX + 3, + CU_CODED_FLAG, + CU_SBT_FLAG, + CU_SBT_QUAD_FLAG = CU_SBT_FLAG + 2, + CU_SBT_HORIZONTAL_FLAG, + CU_SBT_POS_FLAG = CU_SBT_HORIZONTAL_FLAG + 3, + LFNST_IDX, + MTS_IDX = LFNST_IDX + 3, + COPY_ABOVE_PALETTE_INDICES_FLAG = MTS_IDX + 4, + PALETTE_TRANSPOSE_FLAG, + RUN_COPY_FLAG, + REGULAR_MERGE_FLAG = RUN_COPY_FLAG + 8, + MMVD_MERGE_FLAG = REGULAR_MERGE_FLAG + 2, + MMVD_CAND_FLAG, + MMVD_DISTANCE_IDX, + CIIP_FLAG, + MERGE_SUBBLOCK_FLAG, + MERGE_SUBBLOCK_IDX = MERGE_SUBBLOCK_FLAG + 3, + MERGE_IDX, + ABS_MVD_GREATER0_FLAG, + ABS_MVD_GREATER1_FLAG, + TU_Y_CODED_FLAG, + TU_CB_CODED_FLAG = TU_Y_CODED_FLAG + 4, + TU_CR_CODED_FLAG = TU_CB_CODED_FLAG + 2, + CU_QP_DELTA_ABS = TU_CR_CODED_FLAG + 3, + CU_CHROMA_QP_OFFSET_FLAG = CU_QP_DELTA_ABS + 2, + CU_CHROMA_QP_OFFSET_IDX, + TRANSFORM_SKIP_FLAG, + TU_JOINT_CBCR_RESIDUAL_FLAG = TRANSFORM_SKIP_FLAG + 2, + LAST_SIG_COEFF_X_PREFIX = TU_JOINT_CBCR_RESIDUAL_FLAG + 3, + LAST_SIG_COEFF_Y_PREFIX = LAST_SIG_COEFF_X_PREFIX +23, + SB_CODED_FLAG = LAST_SIG_COEFF_Y_PREFIX +23, + SIG_COEFF_FLAG = SB_CODED_FLAG + 7, + PAR_LEVEL_FLAG = SIG_COEFF_FLAG +63, + ABS_LEVEL_GTX_FLAG = PAR_LEVEL_FLAG +33, + COEFF_SIGN_FLAG = ABS_LEVEL_GTX_FLAG +72, + SYNTAX_ELEMENT_LAST = COEFF_SIGN_FLAG + 6, +}; + +static const uint8_t init_values[4][SYNTAX_ELEMENT_LAST] = { + { + //alf_ctb_flag + 62, 39, 39, 54, 39, 39, 31, 39, 39, + //alf_use_aps_flag + 46, + //alf_ctb_cc_cb_idc + 18, 30, 31, + //alf_ctb_cc_cr_idc + 18, 30, 31, + //alf_ctb_filter_alt_idx + 11, 11, + //sao_merge_left_flag and sao_merge_up_flag + 60, + //sao_type_idx_luma and sao_type_idx_chroma + 13, + //split_cu_flag + 19, 28, 38, 27, 29, 38, 20, 30, 31, + //split_qt_flag + 27, 6, 15, 25, 19, 37, + //mtt_split_cu_vertical_flag + 43, 42, 29, 27, 44, + //mtt_split_cu_binary_flag + 36, 45, 36, 45, + //non_inter_flag + CNU, CNU, + //cu_skip_flag + 0, 26, 28, + //pred_mode_ibc_flag + 17, 42, 36, + //pred_mode_flag + CNU, CNU, + //pred_mode_plt_flag + 25, + //cu_act_enabled_flag + 52, + //intra_bdpcm_luma_flag + 19, + //intra_bdpcm_luma_dir_flag + 35, + //intra_mip_flag + 33, 49, 50, 25, + //intra_luma_ref_idx + 25, 60, + //intra_subpartitions_mode_flag + 33, + //intra_subpartitions_split_flag + 43, + //intra_luma_mpm_flag + 45, + //intra_luma_not_planar_flag + 13, 28, + //intra_bdpcm_chroma_flag + 1, + //intra_bdpcm_chroma_dir_flag + 27, + //cclm_mode_flag + 59, + //cclm_mode_idx + 27, + //intra_chroma_pred_mode + 34, + //general_merge_flag + 26, + //inter_pred_idc + CNU, CNU, CNU, CNU, CNU, CNU, + //inter_affine_flag + CNU, CNU, CNU, + //cu_affine_type_flag + CNU, + //sym_mvd_flag + CNU, + //ref_idx_l0 and ref_idx_l1 + CNU, CNU, + //mvp_l0_flag and mvp_l1_flag + 42, + //amvr_flag + CNU, CNU, + //amvr_precision_idx + 35, 34, 35, + //bcw_idx + CNU, + //cu_coded_flag + 6, + //cu_sbt_flag + CNU, CNU, + //cu_sbt_quad_flag + CNU, + //cu_sbt_horizontal_flag + CNU, CNU, CNU, + //cu_sbt_pos_flag + CNU, + //lfnst_idx + 28, 52, 42, + //mts_idx + 29, 0, 28, 0, + //copy_above_palette_indices_flag + 42, + //palette_transpose_flag + 42, + //run_copy_flag + 50, 37, 45, 30, 46, 45, 38, 46, + //regular_merge_flag + CNU, CNU, + //mmvd_merge_flag + CNU, + //mmvd_cand_flag + CNU, + //mmvd_distance_idx + CNU, + //ciip_flag + CNU, + //merge_subblock_flag + CNU, CNU, CNU, + //merge_subblock_idx + CNU, + //merge_idx, merge_gpm_idx0, and merge_gpm_idx1 + 34, + //abs_mvd_greater0_flag + 14, + //abs_mvd_greater1_flag + 45, + //tu_y_coded_flag + 15, 12, 5, 7, + //tu_cb_coded_flag + 12, 21, + //tu_cr_coded_flag + 33, 28, 36, + //cu_qp_delta_abs + CNU, CNU, + //cu_chroma_qp_offset_flag + CNU, + //cu_chroma_qp_offset_idx + CNU, + //transform_skip_flag + 25, 9, + //tu_joint_cbcr_residual_flag + 12, 21, 35, + //last_sig_coeff_x_prefix + 13, 5, 4, 21, 14, 4, 6, 14, 21, 11, 14, 7, 14, 5, 11, 21, + 30, 22, 13, 42, 12, 4, 3, + //last_sig_coeff_y_prefix + 13, 5, 4, 6, 13, 11, 14, 6, 5, 3, 14, 22, 6, 4, 3, 6, + 22, 29, 20, 34, 12, 4, 3, + //sb_coded_flag + 18, 31, 25, 15, 18, 20, 38, + //sig_coeff_flag + 25, 19, 28, 14, 25, 20, 29, 30, 19, 37, 30, 38, 11, 38, 46, 54, + 27, 39, 39, 39, 44, 39, 39, 39, 18, 39, 39, 39, 27, 39, 39, 39, + 0, 39, 39, 39, 25, 27, 28, 37, 34, 53, 53, 46, 19, 46, 38, 39, + 52, 39, 39, 39, 11, 39, 39, 39, 19, 39, 39, 39, 25, 28, 38, + //par_level_flag + 33, 25, 18, 26, 34, 27, 25, 26, 19, 42, 35, 33, 19, 27, 35, 35, + 34, 42, 20, 43, 20, 33, 25, 26, 42, 19, 27, 26, 50, 35, 20, 43, + 11, + //abs_level_gtx_flag + 25, 25, 11, 27, 20, 21, 33, 12, 28, 21, 22, 34, 28, 29, 29, 30, + 36, 29, 45, 30, 23, 40, 33, 27, 28, 21, 37, 36, 37, 45, 38, 46, + 25, 1, 40, 25, 33, 11, 17, 25, 25, 18, 4, 17, 33, 26, 19, 13, + 33, 19, 20, 28, 22, 40, 9, 25, 18, 26, 35, 25, 26, 35, 28, 37, + 11, 5, 5, 14, 10, 3, 3, 3, + //coeff_sign_flag + 12, 17, 46, 28, 25, 46, + }, + { + //alf_ctb_flag + 13, 23, 46, 4, 61, 54, 19, 46, 54, + //alf_use_aps_flag + 46, + //alf_ctb_cc_cb_idc + 18, 21, 38, + //alf_ctb_cc_cr_idc + 18, 21, 38, + //alf_ctb_filter_alt_idx + 20, 12, + //sao_merge_left_flag and sao_merge_up_flag + 60, + //sao_type_idx_luma and sao_type_idx_chroma + 5, + //split_cu_flag + 11, 35, 53, 12, 6, 30, 13, 15, 31, + //split_qt_flag + 20, 14, 23, 18, 19, 6, + //mtt_split_cu_vertical_flag + 43, 35, 37, 34, 52, + //mtt_split_cu_binary_flag + 43, 37, 21, 22, + //non_inter_flag + 25, 12, + //cu_skip_flag + 57, 59, 45, + //pred_mode_ibc_flag + 0, 57, 44, + //pred_mode_flag + 40, 35, + //pred_mode_plt_flag + 0, + //cu_act_enabled_flag + 46, + //intra_bdpcm_luma_flag + 40, + //intra_bdpcm_luma_dir_flag + 36, + //intra_mip_flag + 41, 57, 58, 26, + //intra_luma_ref_idx + 25, 58, + //intra_subpartitions_mode_flag + 33, + //intra_subpartitions_split_flag + 36, + //intra_luma_mpm_flag + 36, + //intra_luma_not_planar_flag + 12, 20, + //intra_bdpcm_chroma_flag + 0, + //intra_bdpcm_chroma_dir_flag + 13, + //cclm_mode_flag + 34, + //cclm_mode_idx + 27, + //intra_chroma_pred_mode + 25, + //general_merge_flag + 21, + //inter_pred_idc + 7, 6, 5, 12, 4, 40, + //inter_affine_flag + 12, 13, 14, + //cu_affine_type_flag + 35, + //sym_mvd_flag + 28, + //ref_idx_l0 and ref_idx_l1 + 20, 35, + //mvp_l0_flag and mvp_l1_flag + 34, + //amvr_flag + 59, 58, + //amvr_precision_idx + 60, 48, 60, + //bcw_idx + 4, + //cu_coded_flag + 5, + //cu_sbt_flag + 56, 57, + //cu_sbt_quad_flag + 42, + //cu_sbt_horizontal_flag + 20, 43, 12, + //cu_sbt_pos_flag + 28, + //lfnst_idx + 37, 45, 27, + //mts_idx + 45, 40, 27, 0, + //copy_above_palette_indices_flag + 59, + //palette_transpose_flag + 42, + //run_copy_flag + 51, 30, 30, 38, 23, 38, 53, 46, + //regular_merge_flag + 38, 7, + //mmvd_merge_flag + 26, + //mmvd_cand_flag + 43, + //mmvd_distance_idx + 60, + //ciip_flag + 57, + //merge_subblock_flag + 48, 57, 44, + //merge_subblock_idx + 5, + //merge_idx, merge_gpm_idx0, and merge_gpm_idx1 + 20, + //abs_mvd_greater0_flag + 44, + //abs_mvd_greater1_flag + 43, + //tu_y_coded_flag + 23, 5, 20, 7, + //tu_cb_coded_flag + 25, 28, + //tu_cr_coded_flag + 25, 29, 45, + //cu_qp_delta_abs + CNU, CNU, + //cu_chroma_qp_offset_flag + CNU, + //cu_chroma_qp_offset_idx + CNU, + //transform_skip_flag + 25, 9, + //tu_joint_cbcr_residual_flag + 27, 36, 45, + //last_sig_coeff_x_prefix + 6, 13, 12, 6, 6, 12, 14, 14, 13, 12, 29, 7, 6, 13, 36, 28, + 14, 13, 5, 26, 12, 4, 18, + //last_sig_coeff_y_prefix + 5, 5, 12, 6, 6, 4, 6, 14, 5, 12, 14, 7, 13, 5, 13, 21, + 14, 20, 12, 34, 11, 4, 18, + //sb_coded_flag + 25, 30, 25, 45, 18, 12, 29, + //sig_coeff_flag + 17, 41, 42, 29, 25, 49, 43, 37, 33, 58, 51, 30, 19, 38, 38, 46, + 34, 54, 54, 39, 6, 39, 39, 39, 19, 39, 54, 39, 19, 39, 39, 39, + 56, 39, 39, 39, 17, 34, 35, 21, 41, 59, 60, 38, 35, 45, 53, 54, + 44, 39, 39, 39, 34, 38, 62, 39, 26, 39, 39, 39, 40, 35, 44, + //par_level_flag + 18, 17, 33, 18, 26, 42, 25, 33, 26, 42, 27, 25, 34, 42, 42, 35, + 26, 27, 42, 20, 20, 25, 25, 26, 11, 19, 27, 33, 42, 35, 35, 43, + 3, + //abs_level_gtx_flag + 0, 17, 26, 19, 35, 21, 25, 34, 20, 28, 29, 33, 27, 28, 29, 22, + 34, 28, 44, 37, 38, 0, 25, 19, 20, 13, 14, 57, 44, 30, 30, 23, + 17, 0, 1, 17, 25, 18, 0, 9, 25, 33, 34, 9, 25, 18, 26, 20, + 25, 18, 19, 27, 29, 17, 9, 25, 10, 18, 4, 17, 33, 19, 20, 29, + 18, 11, 4, 28, 2, 10, 3, 3, + //coeff_sign_flag + 5, 10, 53, 43, 25, 46, + }, + { + //alf_ctb_flag + 33, 52, 46, 25, 61, 54, 25, 61, 54, + //alf_use_aps_flag + 46, + //alf_ctb_cc_cb_idc + 25, 35, 38, + //alf_ctb_cc_cr_idc + 25, 28, 38, + //alf_ctb_filter_alt_idx + 11, 26, + //sao_merge_left_flag and sao_merge_up_flag + 2, + //sao_type_idx_luma and sao_type_idx_chroma + 2, + //split_cu_flag + 18, 27, 15, 18, 28, 45, 26, 7, 23, + //split_qt_flag + 26, 36, 38, 18, 34, 21, + //mtt_split_cu_vertical_flag + 43, 42, 37, 42, 44, + //mtt_split_cu_binary_flag + 28, 29, 28, 29, + //non_inter_flag + 25, 20, + //cu_skip_flag + 57, 60, 46, + //pred_mode_ibc_flag + 0, 43, 45, + //pred_mode_flag + 40, 35, + //pred_mode_plt_flag + 17, + //cu_act_enabled_flag + 46, + //intra_bdpcm_luma_flag + 19, + //intra_bdpcm_luma_dir_flag + 21, + //intra_mip_flag + 56, 57, 50, 26, + //intra_luma_ref_idx + 25, 59, + //intra_subpartitions_mode_flag + 33, + //intra_subpartitions_split_flag + 43, + //intra_luma_mpm_flag + 44, + //intra_luma_not_planar_flag + 13, 6, + //intra_bdpcm_chroma_flag + 0, + //intra_bdpcm_chroma_dir_flag + 28, + //cclm_mode_flag + 26, + //cclm_mode_idx + 27, + //intra_chroma_pred_mode + 25, + //general_merge_flag + 6, + //inter_pred_idc + 14, 13, 5, 4, 3, 40, + //inter_affine_flag + 19, 13, 6, + //cu_affine_type_flag + 35, + //sym_mvd_flag + 28, + //ref_idx_l0 and ref_idx_l1 + 5, 35, + //mvp_l0_flag and mvp_l1_flag + 34, + //amvr_flag + 59, 50, + //amvr_precision_idx + 38, 26, 60, + //bcw_idx + 5, + //cu_coded_flag + 12, + //cu_sbt_flag + 41, 57, + //cu_sbt_quad_flag + 42, + //cu_sbt_horizontal_flag + 35, 51, 27, + //cu_sbt_pos_flag + 28, + //lfnst_idx + 52, 37, 27, + //mts_idx + 45, 25, 27, 0, + //copy_above_palette_indices_flag + 50, + //palette_transpose_flag + 35, + //run_copy_flag + 58, 45, 45, 30, 38, 45, 38, 46, + //regular_merge_flag + 46, 15, + //mmvd_merge_flag + 25, + //mmvd_cand_flag + 43, + //mmvd_distance_idx + 59, + //ciip_flag + 57, + //merge_subblock_flag + 25, 58, 45, + //merge_subblock_idx + 4, + //merge_idx, merge_gpm_idx0, and merge_gpm_idx1 + 18, + //abs_mvd_greater0_flag + 51, + //abs_mvd_greater1_flag + 36, + //tu_y_coded_flag + 15, 6, 5, 14, + //tu_cb_coded_flag + 25, 37, + //tu_cr_coded_flag + 9, 36, 45, + //cu_qp_delta_abs + CNU, CNU, + //cu_chroma_qp_offset_flag + CNU, + //cu_chroma_qp_offset_idx + CNU, + //transform_skip_flag + 25, 17, + //tu_joint_cbcr_residual_flag + 42, 43, 52, + //last_sig_coeff_x_prefix + 6, 6, 12, 14, 6, 4, 14, 7, 6, 4, 29, 7, 6, 6, 12, 28, + 7, 13, 13, 35, 19, 5, 4, + //last_sig_coeff_y_prefix + 5, 5, 20, 13, 13, 19, 21, 6, 12, 12, 14, 14, 5, 4, 12, 13, + 7, 13, 12, 41, 11, 5, 27, + //sb_coded_flag + 25, 45, 25, 14, 18, 35, 45, + //sig_coeff_flag + 17, 41, 49, 36, 1, 49, 50, 37, 48, 51, 58, 45, 26, 45, 53, 46, + 49, 54, 61, 39, 35, 39, 39, 39, 19, 54, 39, 39, 50, 39, 39, 39, + 0, 39, 39, 39, 9, 49, 50, 36, 48, 59, 59, 38, 34, 45, 38, 31, + 58, 39, 39, 39, 34, 38, 54, 39, 41, 39, 39, 39, 25, 50, 37, + //par_level_flag + 33, 40, 25, 41, 26, 42, 25, 33, 26, 34, 27, 25, 41, 42, 42, 35, + 33, 27, 35, 42, 43, 33, 25, 26, 34, 19, 27, 33, 42, 43, 35, 43, + 11, + //abs_level_gtx_flag + 0, 0, 33, 34, 35, 21, 25, 34, 35, 28, 29, 40, 42, 43, 29, 30, + 49, 36, 37, 45, 38, 0, 40, 34, 43, 36, 37, 57, 52, 45, 38, 46, + 25, 0, 0, 17, 25, 26, 0, 9, 25, 33, 19, 0, 25, 33, 26, 20, + 25, 33, 27, 35, 22, 25, 1, 25, 33, 26, 12, 25, 33, 27, 28, 37, + 19, 11, 4, 6, 3, 4, 4, 5, + //coeff_sign_flag + 35, 25, 46, 28, 33, 38, + }, + //shiftIdx + { + //alf_ctb_flag + 0, 0, 0, 4, 0, 0, 1, 0, 0, + //alf_use_aps_flag + 0, + //alf_ctb_cc_cb_idc + 4, 1, 4, + //alf_ctb_cc_cr_idc + 4, 1, 4, + //alf_ctb_filter_alt_idx + 0, 0, + //sao_merge_left_flag and sao_merge_up_flag + 0, + //sao_type_idx_luma and sao_type_idx_chroma + 4, + //split_cu_flag + 12, 13, 8, 8, 13, 12, 5, 9, 9, + //split_qt_flag + 0, 8, 8, 12, 12, 8, + //mtt_split_cu_vertical_flag + 9, 8, 9, 8, 5, + //mtt_split_cu_binary_flag + 12, 13, 12, 13, + //non_inter_flag + 1, 0, + //cu_skip_flag + 5, 4, 8, + //pred_mode_ibc_flag + 1, 5, 8, + //pred_mode_flag + 5, 1, + //pred_mode_plt_flag + 1, + //cu_act_enabled_flag + 1, + //intra_bdpcm_luma_flag + 1, + //intra_bdpcm_luma_dir_flag + 4, + //intra_mip_flag + 9, 10, 9, 6, + //intra_luma_ref_idx + 5, 8, + //intra_subpartitions_mode_flag + 9, + //intra_subpartitions_split_flag + 2, + //intra_luma_mpm_flag + 6, + //intra_luma_not_planar_flag + 1, 5, + //intra_bdpcm_chroma_flag + 1, + //intra_bdpcm_chroma_dir_flag + 0, + //cclm_mode_flag + 4, + //cclm_mode_idx + 9, + //intra_chroma_pred_mode + 5, + //general_merge_flag + 4, + //inter_pred_idc + 0, 0, 1, 4, 4, 0, + //inter_affine_flag + 4, 0, 0, + //cu_affine_type_flag + 4, + //sym_mvd_flag + 5, + //ref_idx_l0 and ref_idx_l1 + 0, 4, + //mvp_l0_flag and mvp_l1_flag + 12, + //amvr_flag + 0, 0, + //amvr_precision_idx + 4, 5, 0, + //bcw_idx + 1, + //cu_coded_flag + 4, + //cu_sbt_flag + 1, 5, + //cu_sbt_quad_flag + 10, + //cu_sbt_horizontal_flag + 8, 4, 1, + //cu_sbt_pos_flag + 13, + //lfnst_idx + 9, 9, 10, + //mts_idx + 8, 0, 9, 0, + //copy_above_palette_indices_flag + 9, + //palette_transpose_flag + 5, + //run_copy_flag + 9, 6, 9, 10, 5, 0, 9, 5, + //regular_merge_flag + 5, 5, + //mmvd_merge_flag + 4, + //mmvd_cand_flag + 10, + //mmvd_distance_idx + 0, + //ciip_flag + 1, + //merge_subblock_flag + 4, 4, 4, + //merge_subblock_idx + 0, + //merge_idx, merge_gpm_idx0, and merge_gpm_idx1 + 4, + //abs_mvd_greater0_flag + 9, + //abs_mvd_greater1_flag + 5, + //tu_y_coded_flag + 5, 1, 8, 9, + //tu_cb_coded_flag + 5, 0, + //tu_cr_coded_flag + 2, 1, 0, + //cu_qp_delta_abs + 8, 8, + //cu_chroma_qp_offset_flag + 8, + //cu_chroma_qp_offset_idx + 8, + //transform_skip_flag + 1, 1, + //tu_joint_cbcr_residual_flag + 1, 1, 0, + //last_sig_coeff_x_prefix + 8, 5, 4, 5, 4, 4, 5, 4, 1, 0, 4, 1, 0, 0, 0, 0, + 1, 0, 0, 0, 5, 4, 4, + //last_sig_coeff_y_prefix + 8, 5, 8, 5, 5, 4, 5, 5, 4, 0, 5, 4, 1, 0, 0, 1, + 4, 0, 0, 0, 6, 5, 5, + //sb_coded_flag + 8, 5, 5, 8, 5, 8, 8, + //sig_coeff_flag + 12, 9, 9, 10, 9, 9, 9, 10, 8, 8, 8, 10, 9, 13, 8, 8, + 8, 8, 8, 5, 8, 0, 0, 0, 8, 8, 8, 8, 8, 0, 4, 4, + 0, 0, 0, 0, 12, 12, 9, 13, 4, 5, 8, 9, 8, 12, 12, 8, + 4, 0, 0, 0, 8, 8, 8, 8, 4, 0, 0, 0, 13, 13, 8, + //par_level_flag + 8, 9, 12, 13, 13, 13, 10, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 10, 13, 13, 13, 13, 8, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, + 6, + //abs_level_gtx_flag + 9, 5, 10, 13, 13, 10, 9, 10, 13, 13, 13, 9, 10, 10, 10, 13, + 8, 9, 10, 10, 13, 8, 8, 9, 12, 12, 10, 5, 9, 9, 9, 13, + 1, 5, 9, 9, 9, 6, 5, 9, 10, 10, 9, 9, 9, 9, 9, 9, + 6, 8, 9, 9, 10, 1, 5, 8, 8, 9, 6, 6, 9, 8, 8, 9, + 4, 2, 1, 6, 1, 1, 1, 1, + //coeff_sign_flag + 1, 4, 4, 5, 8, 8, + } +}; + +#define MAX_SUB_BLOCKS 16 +#define MAX_SUB_BLOCK_SIZE 4 +#define MAX_TB_SIZE 64 + +typedef struct ResidualCoding { + //common for ts and non ts + TransformBlock *tb; + + int log2_sb_w; + int log2_sb_h; + int last_sub_block; + int hist_value; + int update_hist; + int num_sb_coeff; + int rem_bins_pass1; + + int width_in_sbs; + int height_in_sbs; + int nb_sbs; + + const uint8_t *sb_scan_x_off; + const uint8_t *sb_scan_y_off; + const uint8_t *scan_x_off; + const uint8_t *scan_y_off; + + uint8_t sb_coded_flag[MAX_SUB_BLOCKS * MAX_SUB_BLOCKS]; + int sig_coeff_flag[MAX_TB_SIZE * MAX_TB_SIZE]; + int abs_level_pass1[MAX_TB_SIZE * MAX_TB_SIZE]; ///< AbsLevelPass1[][] + int abs_level[MAX_TB_SIZE * MAX_TB_SIZE]; + + //for ts only + uint8_t infer_sb_cbf; + int coeff_sign_level[MAX_TB_SIZE * MAX_TB_SIZE]; ///< CoeffSignLevel[][] + + //for non ts only + int qstate; + int last_scan_pos; + int last_significant_coeff_x; + int last_significant_coeff_y; +} ResidualCoding; + +static int cabac_reinit(VVCLocalContext *lc) +{ + return skip_bytes(&lc->ep->cc, 0) == NULL ? AVERROR_INVALIDDATA : 0; +} + +static void cabac_init_state(VVCLocalContext *lc) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int qp = av_clip_uintp2(lc->sc->sh.slice_qp_y, 6); + int init_type = 2 - rsh->sh_slice_type; + + av_assert0(VVC_CONTEXTS == SYNTAX_ELEMENT_LAST); + + ff_vvc_ep_init_stat_coeff(lc->ep, sps->bit_depth, sps->r->sps_persistent_rice_adaptation_enabled_flag); + + if (rsh->sh_cabac_init_flag && !IS_I(rsh)) + init_type ^= 3; + + for (int i = 0; i < VVC_CONTEXTS; i++) { + VVCCabacState *state = &lc->ep->cabac_state[i]; + const int init_value = init_values[init_type][i]; + const int shift_idx = init_values[3][i]; + const int m = (init_value >> 3) - 4; + const int n = ((init_value & 7) * 18) + 1; + const int pre = av_clip(((m * (qp - 16)) >> 1) + n, 1, 127); + + state->state[0] = pre << 3; + state->state[1] = pre << 7; + state->shift[0] = (shift_idx >> 2 ) + 2; + state->shift[1] = (shift_idx & 3 ) + 3 + state->shift[0]; + } +} + +int ff_vvc_cabac_init(VVCLocalContext *lc, + const int ctu_idx, const int rx, const int ry) +{ + int ret = 0; + const VVCPPS *pps = lc->fc->ps.pps; + const int first_ctb_in_slice = !ctu_idx; + const int first_ctb_in_tile = rx == pps->ctb_to_col_bd[rx] && ry == pps->ctb_to_row_bd[ry]; + + if (first_ctb_in_slice|| first_ctb_in_tile) { + if (lc->sc->nb_eps == 1 && !first_ctb_in_slice) + ret = cabac_reinit(lc); + if (ret == 0) + cabac_init_state(lc); + } + return ret; +} + +//fixme +static void vvc_refill2(CABACContext* c) { + int i; + unsigned x; +#if !HAVE_FAST_CLZ + x = c->low ^ (c->low - 1); + i = 7 - ff_h264_norm_shift[x >> (CABAC_BITS - 1)]; +#else + i = ff_ctz(c->low) - CABAC_BITS; +#endif + + x = -CABAC_MASK; + +#if CABAC_BITS == 16 + x += (c->bytestream[0] << 9) + (c->bytestream[1] << 1); +#else + x += c->bytestream[0] << 1; +#endif + + c->low += x << i; +#if !UNCHECKED_BITSTREAM_READER + if (c->bytestream < c->bytestream_end) +#endif + c->bytestream += CABAC_BITS / 8; +} + +static int inline vvc_get_cabac(CABACContext *c, VVCCabacState* base, const int ctx) +{ + VVCCabacState *s = base + ctx; + const int qRangeIdx = c->range >> 5; + const int pState = s->state[1] + (s->state[0] << 4); + const int valMps = pState >> 14; + const int RangeLPS = (qRangeIdx * ((valMps ? 32767 - pState : pState) >> 9 ) >> 1) + 4; + int bit, lps_mask; + + c->range -= RangeLPS; + lps_mask = ((c->range<<(CABAC_BITS+1)) - c->low)>>31; + + c->low -= (c->range<<(CABAC_BITS+1)) & lps_mask; + c->range += (RangeLPS - c->range) & lps_mask; + + bit = valMps ^ (lps_mask & 1); + + lps_mask = ff_h264_norm_shift[c->range]; + c->range <<= lps_mask; + c->low <<= lps_mask; + + if (!(c->low & CABAC_MASK)) + vvc_refill2(c); + s->state[0] = s->state[0] - (s->state[0] >> s->shift[0]) + (1023 * bit >> s->shift[0]); + s->state[1] = s->state[1] - (s->state[1] >> s->shift[1]) + (16383 * bit >> s->shift[1]); + return bit; +} + +#define GET_CABAC(ctx) vvc_get_cabac(&lc->ep->cc, lc->ep->cabac_state, ctx) + +static av_always_inline int vvc_get_cabac_bypass(CABACContext *c) +{ + return get_cabac_bypass(c); +} + +//9.3.3.4 Truncated binary (TB) binarization process +static int truncated_binary_decode(VVCLocalContext *lc, const int c_max) +{ + const int n = c_max + 1; + const int k = av_log2(n); + const int u = (1 << (k+1)) - n; + int v = 0; + for (int i = 0; i < k; i++) + v = (v << 1) | vvc_get_cabac_bypass(&lc->ep->cc); + if (v >= u) { + v = (v << 1) | vvc_get_cabac_bypass(&lc->ep->cc); + v -= u; + } + return v; +} + +// 9.3.3.6 Limited k-th order Exp-Golomb binarization process +static int limited_kth_order_egk_decode(CABACContext *c, const int k, const int max_pre_ext_len, const int trunc_suffix_len) +{ + int pre_ext_len = 0; + int escape_length; + int val = 0; + while ((pre_ext_len < max_pre_ext_len) && vvc_get_cabac_bypass(c)) + pre_ext_len++; + if (pre_ext_len == max_pre_ext_len) + escape_length = trunc_suffix_len; + else + escape_length = pre_ext_len + k; + while (escape_length-- > 0) { + val = (val << 1) + vvc_get_cabac_bypass(c); + } + val += ((1 << pre_ext_len) - 1) << k; + return val; +} + +static av_always_inline +void get_left_top(const VVCLocalContext *lc, uint8_t *left, uint8_t *top, + const int x0, const int y0, const uint8_t *left_ctx, const uint8_t *top_ctx) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int x0b = av_mod_uintp2(x0, sps->ctb_log2_size_y); + const int y0b = av_mod_uintp2(y0, sps->ctb_log2_size_y); + const int x_cb = x0 >> sps->min_cb_log2_size_y; + const int y_cb = y0 >> sps->min_cb_log2_size_y; + + if (lc->ctb_left_flag || x0b) + *left = SAMPLE_CTB(left_ctx, x_cb - 1, y_cb); + if (lc->ctb_up_flag || y0b) + *top = SAMPLE_CTB(top_ctx, x_cb, y_cb - 1); +} + +static av_always_inline +uint8_t get_inc(VVCLocalContext *lc, const uint8_t *ctx) +{ + uint8_t left = 0, top = 0; + get_left_top(lc, &left, &top, lc->cu->x0, lc->cu->y0, ctx, ctx); + return left + top; +} + +int ff_vvc_sao_merge_flag_decode(VVCLocalContext *lc) +{ + return GET_CABAC(SAO_MERGE_FLAG); +} + +int ff_vvc_sao_type_idx_decode(VVCLocalContext *lc) +{ + if (!GET_CABAC(SAO_TYPE_IDX)) + return SAO_NOT_APPLIED; + + if (!vvc_get_cabac_bypass(&lc->ep->cc)) + return SAO_BAND; + return SAO_EDGE; +} + +int ff_vvc_sao_band_position_decode(VVCLocalContext *lc) +{ + int i; + int value = vvc_get_cabac_bypass(&lc->ep->cc); + + for (i = 0; i < 4; i++) + value = (value << 1) | vvc_get_cabac_bypass(&lc->ep->cc); + return value; +} + +int ff_vvc_sao_offset_abs_decode(VVCLocalContext *lc) +{ + int i = 0; + int length = (1 << (FFMIN(lc->fc->ps.sps->bit_depth, 10) - 5)) - 1; + + while (i < length && vvc_get_cabac_bypass(&lc->ep->cc)) + i++; + return i; +} + +int ff_vvc_sao_offset_sign_decode(VVCLocalContext *lc) +{ + return vvc_get_cabac_bypass(&lc->ep->cc); +} + +int ff_vvc_sao_eo_class_decode(VVCLocalContext *lc) +{ + int ret = vvc_get_cabac_bypass(&lc->ep->cc) << 1; + ret |= vvc_get_cabac_bypass(&lc->ep->cc); + return ret; +} + +int ff_vvc_alf_ctb_flag(VVCLocalContext *lc, const int rx, const int ry, const int c_idx) +{ + int inc = c_idx * 3; + const VVCFrameContext *fc = lc->fc; + if (lc->ctb_left_flag) { + const ALFParams *left = &CTB(fc->tab.alf, rx - 1, ry); + inc += left->ctb_flag[c_idx]; + } + if (lc->ctb_up_flag) { + const ALFParams *above = &CTB(fc->tab.alf, rx, ry - 1); + inc += above->ctb_flag[c_idx]; + } + return GET_CABAC(ALF_CTB_FLAG + inc); +} + +int ff_vvc_alf_use_aps_flag(VVCLocalContext *lc) +{ + return GET_CABAC(ALF_USE_APS_FLAG); +} + +int ff_vvc_alf_luma_prev_filter_idx(VVCLocalContext *lc) +{ + return truncated_binary_decode(lc, lc->sc->sh.r->sh_num_alf_aps_ids_luma - 1); +} + +int ff_vvc_alf_luma_fixed_filter_idx(VVCLocalContext *lc) +{ + return truncated_binary_decode(lc, 15); +} + +int ff_vvc_alf_ctb_filter_alt_idx(VVCLocalContext *lc, const int c_idx, const int num_chroma_filters) +{ + int i = 0; + const int length = num_chroma_filters - 1; + + while (i < length && GET_CABAC(ALF_CTB_FILTER_ALT_IDX + c_idx - 1)) + i++; + return i; +} + +int ff_vvc_alf_ctb_cc_idc(VVCLocalContext *lc, const int rx, const int ry, const int idx, const int cc_filters_signalled) +{ + int inc = !idx ? ALF_CTB_CC_CB_IDC : ALF_CTB_CC_CR_IDC; + int i = 0; + const VVCFrameContext *fc = lc->fc; + if (lc->ctb_left_flag) { + const ALFParams *left = &CTB(fc->tab.alf, rx - 1, ry); + inc += left->ctb_cc_idc[idx] != 0; + } + if (lc->ctb_up_flag) { + const ALFParams *above = &CTB(fc->tab.alf, rx, ry - 1); + inc += above->ctb_cc_idc[idx] != 0; + } + + if (!GET_CABAC(inc)) + return 0; + i++; + while (i < cc_filters_signalled && vvc_get_cabac_bypass(&lc->ep->cc)) + i++; + return i; +} + +int ff_vvc_split_cu_flag(VVCLocalContext *lc, const int x0, const int y0, + const int cb_width, const int cb_height, const int is_chroma, const VVCAllowedSplit *a) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const int is_inside = (x0 + cb_width <= pps->width) && (y0 + cb_height <= pps->height); + + if ((a->btv || a->bth || a->ttv || a->tth || a->qt) && is_inside) + { + uint8_t inc = 0, left_height = cb_height, top_width = cb_width; + + get_left_top(lc, &left_height, &top_width, x0, y0, fc->tab.cb_height[is_chroma], fc->tab.cb_width[is_chroma]); + inc += left_height < cb_height; + inc += top_width < cb_width; + inc += (a->btv + a->bth + a->ttv + a->tth + 2 * a->qt - 1) / 2 * 3; + + return GET_CABAC(SPLIT_CU_FLAG + inc); + + } + return !is_inside; +} + +static int split_qt_flag_decode(VVCLocalContext *lc, const int x0, const int y0, const int ch_type, const int cqt_depth) +{ + const VVCFrameContext *fc = lc->fc; + int inc = 0; + uint8_t depth_left = 0, depth_top = 0; + + get_left_top(lc, &depth_left, &depth_top, x0, y0, fc->tab.cqt_depth[ch_type], fc->tab.cqt_depth[ch_type]); + inc += depth_left > cqt_depth; + inc += depth_top > cqt_depth; + inc += (cqt_depth >= 2) * 3; + + return GET_CABAC(SPLIT_QT_FLAG + inc); +} + +static int mtt_split_cu_vertical_flag_decode(VVCLocalContext *lc, const int x0, const int y0, + const int cb_width, const int cb_height, const int ch_type, const VVCAllowedSplit* a) +{ + if ((a->bth || a->tth) && (a->btv || a->ttv)) { + int inc; + const int v = a->btv + a->ttv; + const int h = a->bth + a->tth; + if (v > h) + inc = 4; + else if (v < h) + inc = 3; + else { + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int x0b = av_mod_uintp2(x0, sps->ctb_log2_size_y); + const int y0b = av_mod_uintp2(y0, sps->ctb_log2_size_y); + const int x_cb = x0 >> sps->min_cb_log2_size_y; + const int y_cb = y0 >> sps->min_cb_log2_size_y; + const int available_a = lc->ctb_up_flag || y0b; + const int available_l = lc->ctb_left_flag || x0b; + const int da = cb_width / (available_a ? SAMPLE_CTB(fc->tab.cb_width[ch_type], x_cb, y_cb - 1) : 1); + const int dl = cb_height / (available_l ? SAMPLE_CTB(fc->tab.cb_height[ch_type], x_cb - 1, y_cb) : 1); + + if (da == dl || !available_a || !available_l) + inc = 0; + else if (da < dl) + inc = 1; + else + inc = 2; + } + return GET_CABAC(MTT_SPLIT_CU_VERTICAL_FLAG + inc); + } + return !(a->bth || a->tth); +} + +static int mtt_split_cu_binary_flag_decode(VVCLocalContext *lc, const int mtt_split_cu_vertical_flag, const int mtt_depth) +{ + int inc = (2 * mtt_split_cu_vertical_flag) + ((mtt_depth <= 1) ? 1 : 0); + return GET_CABAC(MTT_SPLIT_CU_BINARY_FLAG + inc); +} + +VVCSplitMode ff_vvc_split_mode(VVCLocalContext *lc, const int x0, const int y0, const int cb_width, const int cb_height, + const int cqt_depth, const int mtt_depth, const int ch_type, const VVCAllowedSplit *a) +{ + int allow_no_qt = a->btv || a->bth || a->ttv || a->tth; + int split_qt_flag; + int mtt_split_cu_vertical_flag; + int mtt_split_cu_binary_flag; + const VVCSplitMode mtt_split_modes[] = { + SPLIT_TT_HOR, SPLIT_BT_HOR, SPLIT_TT_VER, SPLIT_BT_VER, + }; + if (allow_no_qt && a->qt) { + split_qt_flag = split_qt_flag_decode(lc, x0, y0, ch_type, cqt_depth); + } else { + split_qt_flag = !allow_no_qt || a->qt; + } + if (split_qt_flag) + return SPLIT_QT; + mtt_split_cu_vertical_flag = mtt_split_cu_vertical_flag_decode(lc, x0, y0, cb_width, cb_height, ch_type, a); + if ((a->btv && a->ttv && mtt_split_cu_vertical_flag) || + (a->bth && a->tth && !mtt_split_cu_vertical_flag)) { + mtt_split_cu_binary_flag = mtt_split_cu_binary_flag_decode(lc, mtt_split_cu_vertical_flag, mtt_depth); + } else { + if (!a->btv && !a->bth) + mtt_split_cu_binary_flag = 0; + else if (!a->ttv && !a->tth) + mtt_split_cu_binary_flag = 1; + else if (a->bth && a->ttv) + mtt_split_cu_binary_flag = 1 - mtt_split_cu_vertical_flag; + else + mtt_split_cu_binary_flag = mtt_split_cu_vertical_flag; + } + return mtt_split_modes[(mtt_split_cu_vertical_flag << 1) + mtt_split_cu_binary_flag]; +} + +int ff_vvc_non_inter_flag(VVCLocalContext *lc, const int x0, const int y0, const int ch_type) +{ + const VVCFrameContext *fc = lc->fc; + uint8_t inc, left = 0, top = 0; + + get_left_top(lc, &left, &top, x0, y0, fc->tab.cpm[ch_type], fc->tab.cpm[ch_type]); + inc = left || top; + return GET_CABAC(NON_INTER_FLAG + inc); +} + +int ff_vvc_pred_mode_flag(VVCLocalContext *lc, const int is_chroma) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + uint8_t inc, left = 0, top = 0; + + get_left_top(lc, &left, &top, cu->x0, cu->y0, fc->tab.cpm[is_chroma], fc->tab.cpm[is_chroma]); + inc = left || top; + return GET_CABAC(PRED_MODE_FLAG + inc); +} + +int ff_vvc_pred_mode_plt_flag(VVCLocalContext *lc) +{ + return GET_CABAC(PRED_MODE_PLT_FLAG); +} + +int ff_vvc_intra_bdpcm_luma_flag(VVCLocalContext *lc) +{ + return GET_CABAC(INTRA_BDPCM_LUMA_FLAG); +} + +int ff_vvc_intra_bdpcm_luma_dir_flag(VVCLocalContext *lc) +{ + return GET_CABAC(INTRA_BDPCM_LUMA_DIR_FLAG); +} + +int ff_vvc_intra_bdpcm_chroma_flag(VVCLocalContext *lc) +{ + return GET_CABAC(INTRA_BDPCM_CHROMA_FLAG); +} + +int ff_vvc_intra_bdpcm_chroma_dir_flag(VVCLocalContext *lc) +{ + return GET_CABAC(INTRA_BDPCM_CHROMA_DIR_FLAG); +} + +int ff_vvc_cu_skip_flag(VVCLocalContext *lc, const uint8_t *cu_skip_flag) +{ + const int inc = get_inc(lc, cu_skip_flag); + return GET_CABAC(CU_SKIP_FLAG + inc); +} + +int ff_vvc_pred_mode_ibc_flag(VVCLocalContext *lc, const int is_chroma) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + uint8_t left_mode = MODE_INTER, top_mode = MODE_INTER; + int inc; + + get_left_top(lc, &left_mode, &top_mode, cu->x0, cu->y0, fc->tab.cpm[is_chroma], fc->tab.cpm[is_chroma]); + inc = (left_mode == MODE_IBC) + (top_mode == MODE_IBC); + return GET_CABAC(PRED_MODE_IBC_FLAG + inc); +} + +int ff_vvc_intra_mip_flag(VVCLocalContext *lc, const uint8_t *intra_mip_flag) +{ + const int w = lc->cu->cb_width; + const int h = lc->cu->cb_height; + const int inc = (w > h * 2 || h > w * 2) ? 3 : get_inc(lc, intra_mip_flag); + return GET_CABAC(INTRA_MIP_FLAG + inc); +} + +int ff_vvc_intra_mip_transposed_flag(VVCLocalContext *lc) +{ + return vvc_get_cabac_bypass(&lc->ep->cc); +} + +int ff_vvc_intra_mip_mode(VVCLocalContext *lc) +{ + const int w = lc->cu->cb_width; + const int h = lc->cu->cb_height; + const int c_max = (w == 4 && h == 4) ? 15 : + ((w == 4 || h == 4) || (w == 8 && h == 8)) ? 7: 5; + return truncated_binary_decode(lc, c_max); +} + +int ff_vvc_intra_luma_ref_idx(VVCLocalContext *lc) +{ + int i; + for (i = 0; i < 2; i++) { + if (!GET_CABAC(INTRA_LUMA_REF_IDX + i)) + return i; + } + return i; +} + +int ff_vvc_intra_subpartitions_mode_flag(VVCLocalContext *lc) +{ + return GET_CABAC(INTRA_SUBPARTITIONS_MODE_FLAG); +} + +enum IspType ff_vvc_isp_split_type(VVCLocalContext *lc, const int intra_subpartitions_mode_flag) +{ + if (!intra_subpartitions_mode_flag) + return ISP_NO_SPLIT; + return 1 + GET_CABAC(INTRA_SUBPARTITIONS_SPLIT_FLAG); +} + +int ff_vvc_intra_luma_mpm_flag(VVCLocalContext *lc) +{ + return GET_CABAC(INTRA_LUMA_MPM_FLAG); +} + +int ff_vvc_intra_luma_not_planar_flag(VVCLocalContext *lc, const int intra_subpartitions_mode_flag) +{ + return GET_CABAC(INTRA_LUMA_NOT_PLANAR_FLAG + !intra_subpartitions_mode_flag); +} + +int ff_vvc_intra_luma_mpm_idx(VVCLocalContext *lc) +{ + int i; + for (i = 0; i < 4 && vvc_get_cabac_bypass(&lc->ep->cc); i++) + /* nothing */; + return i; +} + +int ff_vvc_intra_luma_mpm_remainder(VVCLocalContext *lc) +{ + return truncated_binary_decode(lc, 60); +} + +int ff_vvc_cclm_mode_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CCLM_MODE_FLAG); +} + +int ff_vvc_cclm_mode_idx(VVCLocalContext *lc) +{ + if (!GET_CABAC(CCLM_MODE_IDX)) + return 0; + return vvc_get_cabac_bypass(&lc->ep->cc) + 1; +} + +int ff_vvc_intra_chroma_pred_mode(VVCLocalContext *lc) +{ + if (!GET_CABAC(INTRA_CHROMA_PRED_MODE)) + return 4; + return (vvc_get_cabac_bypass(&lc->ep->cc) << 1) | vvc_get_cabac_bypass(&lc->ep->cc); +} + +int ff_vvc_general_merge_flag(VVCLocalContext *lc) +{ + return GET_CABAC(GENERAL_MERGE_FLAG); +} + +static int get_inter_flag_inc(VVCLocalContext *lc, const int x0, const int y0) +{ + uint8_t left_merge = 0, top_merge = 0; + uint8_t left_affine = 0, top_affine = 0; + const VVCFrameContext *fc = lc->fc; + + get_left_top(lc, &left_merge, &top_merge, x0, y0, fc->tab.msf, fc->tab.msf); + get_left_top(lc, &left_affine, &top_affine, x0, y0, fc->tab.iaf, fc->tab.iaf); + return (left_merge || left_affine) + (top_merge + top_affine); +} + +int ff_vvc_merge_subblock_flag(VVCLocalContext *lc) +{ + const int inc = get_inter_flag_inc(lc, lc->cu->x0, lc->cu->y0); + return GET_CABAC(MERGE_SUBBLOCK_FLAG + inc); +} + +int ff_vvc_merge_subblock_idx(VVCLocalContext *lc, const int max_num_subblock_merge_cand) +{ + int i; + if (!GET_CABAC(MERGE_SUBBLOCK_IDX)) + return 0; + for (i = 1; i < max_num_subblock_merge_cand - 1 && vvc_get_cabac_bypass(&lc->ep->cc); i++) + /* nothing */; + return i; +} + +int ff_vvc_regular_merge_flag(VVCLocalContext *lc, const int cu_skip_flag) +{ + int inc = !cu_skip_flag; + return GET_CABAC(REGULAR_MERGE_FLAG + inc); +} + +int ff_vvc_mmvd_merge_flag(VVCLocalContext *lc) +{ + return GET_CABAC(MMVD_MERGE_FLAG); +} + +int ff_vvc_mmvd_cand_flag(VVCLocalContext *lc) +{ + return GET_CABAC(MMVD_CAND_FLAG); +} + +static int mmvd_distance_idx_decode(VVCLocalContext *lc) +{ + int i; + if (!GET_CABAC(MMVD_DISTANCE_IDX)) + return 0; + for (i = 1; i < 7 && vvc_get_cabac_bypass(&lc->ep->cc); i++) + /* nothing */; + return i; +} + +static int mmvd_direction_idx_decode(VVCLocalContext *lc) +{ + return (vvc_get_cabac_bypass(&lc->ep->cc) << 1) | vvc_get_cabac_bypass(&lc->ep->cc); +} + +void ff_vvc_mmvd_offset_coding(VVCLocalContext *lc, Mv *mmvd_offset, const int ph_mmvd_fullpel_only_flag) +{ + const int shift = ph_mmvd_fullpel_only_flag ? 4 : 2; + const int mmvd_distance = 1 << (mmvd_distance_idx_decode(lc) + shift); + const int mmvd_direction_idx = mmvd_direction_idx_decode(lc); + const int mmvd_signs[][2] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} }; + mmvd_offset->x = mmvd_distance * mmvd_signs[mmvd_direction_idx][0]; + mmvd_offset->y = mmvd_distance * mmvd_signs[mmvd_direction_idx][1]; +} + +static PredMode get_luma_pred_mode(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + PredMode pred_mode; + if (cu->tree_type != DUAL_TREE_CHROMA) { + pred_mode = cu->pred_mode; + } else { + const int x_cb = cu->x0 >> fc->ps.sps->min_cb_log2_size_y; + const int y_cb = cu->y0 >> fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + pred_mode = SAMPLE_CTB(fc->tab.cpm[0], x_cb, y_cb); + } + return pred_mode; +} + +int ff_vvc_merge_idx(VVCLocalContext *lc) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const int is_ibc = get_luma_pred_mode(lc) == MODE_IBC; + const int c_max = (is_ibc ? sps->max_num_ibc_merge_cand : sps->max_num_merge_cand) - 1; + int i; + + if (!GET_CABAC(MERGE_IDX)) + return 0; + + for (i = 1; i < c_max && vvc_get_cabac_bypass(&lc->ep->cc); i++) + /* nothing */; + return i; +} + +int ff_vvc_merge_gpm_partition_idx(VVCLocalContext *lc) +{ + int i = 0; + + for (int j = 0; j < 6; j++) + i = (i << 1) | vvc_get_cabac_bypass(&lc->ep->cc); + + return i; +} + +int ff_vvc_merge_gpm_idx(VVCLocalContext *lc, const int idx) +{ + const int c_max = lc->fc->ps.sps->max_num_gpm_merge_cand - idx - 1; + int i; + + if (!GET_CABAC(MERGE_IDX)) + return 0; + + for (i = 1; i < c_max && vvc_get_cabac_bypass(&lc->ep->cc); i++) + /* nothing */; + + return i; +} + +int ff_vvc_ciip_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CIIP_FLAG); +} + +PredFlag ff_vvc_pred_flag(VVCLocalContext *lc, const int is_b) +{ + const int w = lc->cu->cb_width; + const int h = lc->cu->cb_height; + if (!is_b) + return PF_L0; + if (w + h > 12) { + const int log2 = av_log2(w) + av_log2(h); + const int inc = 7 - ((1 + log2)>>1); + if (GET_CABAC(INTER_PRED_IDC + inc)) + return PF_BI; + } + return PF_L0 + GET_CABAC(INTER_PRED_IDC + 5); +} + +int ff_vvc_inter_affine_flag(VVCLocalContext *lc) +{ + const int inc = get_inter_flag_inc(lc, lc->cu->x0, lc->cu->y0); + return GET_CABAC(INTER_AFFINE_FLAG + inc); +} + +int ff_vvc_cu_affine_type_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CU_AFFINE_TYPE_FLAG); +} + +int ff_vvc_sym_mvd_flag(VVCLocalContext *lc) +{ + return GET_CABAC(SYM_MVD_FLAG); +} + +int ff_vvc_ref_idx_lx(VVCLocalContext *lc, const uint8_t nb_refs) +{ + const int c_max = nb_refs - 1; + const int max_ctx = FFMIN(c_max, 2); + int i = 0; + + while (i < max_ctx && GET_CABAC(REF_IDX_LX + i)) + i++; + if (i == 2) { + while (i < c_max && vvc_get_cabac_bypass(&lc->ep->cc)) + i++; + } + return i; +} + +int ff_vvc_abs_mvd_greater0_flag(VVCLocalContext *lc) +{ + return GET_CABAC(ABS_MVD_GREATER0_FLAG); +} + +int ff_vvc_abs_mvd_greater1_flag(VVCLocalContext *lc) +{ + return GET_CABAC(ABS_MVD_GREATER1_FLAG); +} + +int ff_vvc_abs_mvd_minus2(VVCLocalContext *lc) +{ + return limited_kth_order_egk_decode(&lc->ep->cc, 1, 15, 17); +} + +int ff_vvc_mvd_sign_flag(VVCLocalContext *lc) +{ + return vvc_get_cabac_bypass(&lc->ep->cc); +} + +int ff_vvc_mvp_lx_flag(VVCLocalContext *lc) +{ + return GET_CABAC(MVP_LX_FLAG); +} + +static int amvr_flag(VVCLocalContext *lc, const int inter_affine_flag) +{ + return GET_CABAC(AMVR_FLAG + inter_affine_flag); +} + +static int amvr_precision_idx(VVCLocalContext *lc, const int inc, const int c_max) +{ + int i = 0; + if (!GET_CABAC(AMVR_PRECISION_IDX + inc)) + return 0; + i++; + if (i < c_max && GET_CABAC(AMVR_PRECISION_IDX + 1)) + i++; + return i; +} + +int ff_vvc_amvr_shift(VVCLocalContext *lc, const int inter_affine_flag, + const PredMode pred_mode, const int has_amvr_flag) +{ + int amvr_shift = 2; + if (has_amvr_flag) { + if (amvr_flag(lc, inter_affine_flag)) { + int idx; + if (inter_affine_flag) { + idx = amvr_precision_idx(lc, 2, 1); + amvr_shift = idx * 4; + } else if (pred_mode == MODE_IBC) { + idx = amvr_precision_idx(lc, 1, 1); + amvr_shift = 4 + idx * 2; + } else { + static const int shifts[] = {3, 4, 6}; + idx = amvr_precision_idx(lc, 0, 2); + amvr_shift = shifts[idx]; + } + } + } + return amvr_shift; +} + +int ff_vvc_bcw_idx(VVCLocalContext *lc, const int no_backward_pred_flag) +{ + const int c_max = no_backward_pred_flag ? 4 : 2; + int i = 1; + if (!GET_CABAC(BCW_IDX)) + return 0; + while (i < c_max && vvc_get_cabac_bypass(&lc->ep->cc)) + i++; + return i; +} + +int ff_vvc_tu_cb_coded_flag(VVCLocalContext *lc) +{ + return GET_CABAC(TU_CB_CODED_FLAG + lc->cu->bdpcm_flag[1]); +} + +int ff_vvc_tu_cr_coded_flag(VVCLocalContext *lc, int tu_cb_coded_flag) +{ + return GET_CABAC(TU_CR_CODED_FLAG + (lc->cu->bdpcm_flag[1] ? 2 : tu_cb_coded_flag)); +} + +int ff_vvc_tu_y_coded_flag(VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + int inc; + if (cu->bdpcm_flag[0]) + inc = 1; + else if (cu->isp_split_type == ISP_NO_SPLIT) + inc = 0; + else + inc = 2 + lc->parse.prev_tu_cbf_y; + lc->parse.prev_tu_cbf_y = GET_CABAC(TU_Y_CODED_FLAG + inc); + return lc->parse.prev_tu_cbf_y; +} + +int ff_vvc_cu_qp_delta_abs(VVCLocalContext *lc) +{ + int v, i, k; + if (!GET_CABAC(CU_QP_DELTA_ABS)) + return 0; + + // prefixVal + for (v = 1; v < 5 && GET_CABAC(CU_QP_DELTA_ABS + 1); v++) + /* nothing */; + if (v < 5) + return v; + + // 9.3.3.5 k-th order Exp-Golomb binarization process + // suffixVal + + // CuQpDeltaVal shall in the range of −( 32 + QpBdOffset / 2 ) to +( 31 + QpBdOffset / 2 ) + // so k = 6 should enough + for (k = 0; k < 6 && vvc_get_cabac_bypass(&lc->ep->cc); k++) + /* nothing */; + i = (1 << k) - 1; + v = 0; + while (k--) + v = (v << 1) + vvc_get_cabac_bypass(&lc->ep->cc); + v += i; + + return v + 5; +} + +int ff_vvc_cu_qp_delta_sign_flag(VVCLocalContext *lc) +{ + return vvc_get_cabac_bypass(&lc->ep->cc); +} + +int ff_vvc_cu_chroma_qp_offset_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CU_CHROMA_QP_OFFSET_FLAG); +} +int ff_vvc_cu_chroma_qp_offset_idx(VVCLocalContext *lc) +{ + const int c_max = lc->fc->ps.pps->r->pps_chroma_qp_offset_list_len_minus1; + int i; + for (i = 0; i < c_max && GET_CABAC(CU_CHROMA_QP_OFFSET_IDX); i++) + /* nothing */; + return i; +} + +static av_always_inline int last_significant_coeff_xy_prefix(VVCLocalContext *lc, + const int log2_tb_size, const int log2_zo_tb_size, const int c_idx, const int ctx) +{ + int i = 0; + int max = (log2_zo_tb_size << 1) - 1; + int ctx_offset, ctx_shift; + if (!log2_tb_size) + return 0; + if (!c_idx) { + const int offset_y[] = {0, 0, 3, 6, 10, 15}; + ctx_offset = offset_y[log2_tb_size - 1]; + ctx_shift = (log2_tb_size + 1) >> 2; + } else { + const int shifts[] = {0, 0, 0, 1, 2, 2, 2}; + ctx_offset = 20; + ctx_shift = shifts[log2_tb_size]; + } + while (i < max && GET_CABAC(ctx + (i >> ctx_shift) + ctx_offset)) + i++; + return i; +} + +static av_always_inline int last_significant_coeff_x_prefix_decode(VVCLocalContext *lc, + const int log2_tb_width, const int log2_zo_tb_width, const int c_idx) +{ + return last_significant_coeff_xy_prefix(lc, log2_tb_width, log2_zo_tb_width, c_idx, LAST_SIG_COEFF_X_PREFIX); +} + +static av_always_inline int last_significant_coeff_y_prefix_decode(VVCLocalContext *lc, + const int log2_tb_height, const int log2_zo_tb_height, const int c_idx) +{ + return last_significant_coeff_xy_prefix(lc, log2_tb_height, log2_zo_tb_height, c_idx, LAST_SIG_COEFF_Y_PREFIX); +} + +static av_always_inline int last_sig_coeff_suffix_decode(VVCLocalContext *lc, + const int last_significant_coeff_y_prefix) +{ + int i; + const int length = (last_significant_coeff_y_prefix >> 1) - 1; + int value = vvc_get_cabac_bypass(&lc->ep->cc); + + for (i = 1; i < length; i++) + value = (value << 1) | vvc_get_cabac_bypass(&lc->ep->cc); + return value; +} + +int ff_vvc_tu_joint_cbcr_residual_flag(VVCLocalContext *lc, const int tu_cb_coded_flag, const int tu_cr_coded_flag) +{ + return GET_CABAC(TU_JOINT_CBCR_RESIDUAL_FLAG + 2 * tu_cb_coded_flag + tu_cr_coded_flag - 1); +} + +int ff_vvc_transform_skip_flag(VVCLocalContext *lc, const int inc) +{ + return GET_CABAC(TRANSFORM_SKIP_FLAG + inc); +} + +//9.3.4.2.7 Derivation process for the variables locNumSig, locSumAbsPass1 +static int get_local_sum(const int *level, const int w, const int h, + const int xc, const int yc, const int hist_value) +{ + int loc_sum = 3 * hist_value; + level += w * yc + xc; + if (xc < w - 1) { + loc_sum += level[1]; + if (xc < w - 2) + loc_sum += level[2] - hist_value; + if (yc < h - 1) + loc_sum += level[w + 1] - hist_value; + } + if (yc < h - 1) { + loc_sum += level[w]; + if (yc < h - 2) + loc_sum += level[w << 1] - hist_value; + } + return loc_sum; +} + +//9.3.4.2.7 Derivation process for the variables locNumSig, locSumAbsPass1 +static int get_local_sum_ts(const int *level, const int w, const int h, const int xc, const int yc) +{ + int loc_sum = 0; + level += w * yc + xc; + if (xc > 0) + loc_sum += level[-1]; + if (yc > 0) + loc_sum += level[-w]; + return loc_sum; +} + +static int get_gtx_flag_inc(const ResidualCoding* rc, const int xc, const int yc, const int last) +{ + const TransformBlock *tb = rc->tb; + int inc; + if (last) { + const int incs[] = {0, 21, 21}; + inc = incs[tb->c_idx]; + } else { + const int d = xc + yc; + const int local_sum_sig = get_local_sum(rc->sig_coeff_flag, + tb->tb_width,tb->tb_height, xc, yc, rc->hist_value); + const int loc_sum_abs_pass1 = get_local_sum(rc->abs_level_pass1, + tb->tb_width, tb->tb_height, xc, yc, rc->hist_value); + const int offset = FFMIN(loc_sum_abs_pass1 - local_sum_sig, 4); + + if (!tb->c_idx) + inc = 1 + offset + (!d ? 15 : (d < 3 ? 10 : (d < 10 ? 5 : 0))); + else + inc = 22 + offset + (!d ? 5 : 0); + } + return inc; +} + +static int abs_level_gtx_flag_decode(VVCLocalContext *lc, const int inc) +{ + return GET_CABAC(ABS_LEVEL_GTX_FLAG + inc); +} + +static int par_level_flag_decode(VVCLocalContext *lc, const int inc) +{ + return GET_CABAC(PAR_LEVEL_FLAG + inc); +} + +static int par_level_flag_ts_decode(VVCLocalContext *lc) +{ + const int inc = 32; + return GET_CABAC(PAR_LEVEL_FLAG + inc); +} + +static int sb_coded_flag_decode(VVCLocalContext *lc, const uint8_t *sb_coded_flag, + const ResidualCoding *rc, const int xs, const int ys) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const TransformBlock *tb = rc->tb; + const int w = rc->width_in_sbs; + const int h = rc->height_in_sbs; + int inc; + + if (tb->ts && !rsh->sh_ts_residual_coding_disabled_flag) { + const int left = xs > 0 ? sb_coded_flag[-1] : 0; + const int above = ys > 0 ? sb_coded_flag[-w] : 0; + inc = left + above + 4; + } else { + const int right = (xs < w - 1) ? sb_coded_flag[1] : 0; + const int bottom = (ys < h - 1) ? sb_coded_flag[w] : 0; + inc = (right | bottom) + (tb->c_idx ? 2 : 0); + } + return GET_CABAC(SB_CODED_FLAG + inc); +} + +static int sig_coeff_flag_decode(VVCLocalContext *lc, const ResidualCoding* rc, const int xc, const int yc) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const TransformBlock *tb = rc->tb; + int inc; + + if (tb->ts && !rsh->sh_ts_residual_coding_disabled_flag) { + const int local_num_sig = get_local_sum_ts(rc->sig_coeff_flag, tb->tb_width, tb->tb_height, xc, yc); + inc = 60 + local_num_sig; + } else { + const int d = xc + yc; + const int loc_sum_abs_pass1 = get_local_sum(rc->abs_level_pass1, + tb->tb_width, tb->tb_height, xc, yc, 0); + + if (!tb->c_idx) { + inc = 12 * FFMAX(0, rc->qstate - 1) + FFMIN((loc_sum_abs_pass1 + 1) >> 1, 3) + ((d < 2) ? 8 : (d < 5 ? 4 : 0)); + } else { + inc = 36 + 8 * FFMAX(0, rc->qstate - 1) + FFMIN((loc_sum_abs_pass1 + 1) >> 1, 3) + (d < 2 ? 4 : 0); + } + } + return GET_CABAC(SIG_COEFF_FLAG + inc); +} + +static int abs_get_rice_param(VVCLocalContext *lc, const ResidualCoding* rc, + const int xc, const int yc, const int base_level) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const TransformBlock* tb = rc->tb; + const int rice_params[] = { + 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, + }; + int loc_sum_abs; + int shift_val; + + loc_sum_abs = get_local_sum(rc->abs_level, tb->tb_width, tb->tb_height, xc, + yc, rc->hist_value); + + if (!sps->r->sps_rrc_rice_extension_flag) { + shift_val = 0; + } else { + shift_val = (av_log2(FFMAX(FFMIN(loc_sum_abs, 2048), 8)) - 3) & ~1; + } + + loc_sum_abs = av_clip_uintp2((loc_sum_abs >> shift_val) - base_level * 5, 5); + + return rice_params[loc_sum_abs] + shift_val; +} + +static int abs_decode(VVCLocalContext *lc, const int c_rice_param) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const int MAX_BIN = 6; + int prefix = 0; + int suffix = 0; + int i; + + while (prefix < MAX_BIN && vvc_get_cabac_bypass(&lc->ep->cc)) + prefix++; + if (prefix < MAX_BIN) { + for (i = 0; i < c_rice_param; i++) { + suffix = (suffix << 1) | vvc_get_cabac_bypass(&lc->ep->cc); + } + } else { + suffix = limited_kth_order_egk_decode(&lc->ep->cc, + c_rice_param + 1, + 26 - sps->log2_transform_range, + sps->log2_transform_range); + } + return suffix + (prefix << c_rice_param); +} + +static int abs_remainder_decode(VVCLocalContext *lc, const ResidualCoding* rc, const int xc, const int yc) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int base_level[][2][2] = { + { {4, 4}, {4, 4} }, + { {3, 2}, {2, 1} } + }; + const int c_rice_param = abs_get_rice_param(lc, rc, xc, yc, + base_level[sps->r->sps_rrc_rice_extension_flag][sps->bit_depth > 12][IS_I(rsh)]); + const int rem = abs_decode(lc, c_rice_param); + return rem; +} + +static int abs_remainder_ts_decode(VVCLocalContext *lc, const ResidualCoding* rc, const int xc, const int yc) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int c_rice_param = rsh->sh_ts_residual_coding_rice_idx_minus1 + 1; + const int rem = abs_decode(lc, c_rice_param); + + return rem; +} + +static int coeff_sign_flag_decode(VVCLocalContext *lc) +{ + return vvc_get_cabac_bypass(&lc->ep->cc); +} + +//9.3.4.2.10 Derivation process of ctxInc for the syntax element coeff_sign_flag for transform skip mode +static int coeff_sign_flag_ts_decode(VVCLocalContext *lc, const CodingUnit *cu, const ResidualCoding *rc, const int xc, const int yc) +{ + const TransformBlock *tb = rc->tb; + const int w = tb->tb_width; + const int *level = rc->coeff_sign_level + yc * w + xc; + const int left_sign = (xc == 0) ? 0 : level[-1]; + const int above_sign = (yc == 0) ? 0 : level[-w]; + const int bdpcm_flag = cu->bdpcm_flag[tb->c_idx]; + int inc; + + if (left_sign == -above_sign) + inc = bdpcm_flag == 0 ? 0 : 3; + else if (left_sign >= 0 && above_sign >= 0) + inc = bdpcm_flag == 0 ? 1 : 4; + else + inc = bdpcm_flag == 0 ? 2 : 5; + return GET_CABAC(COEFF_SIGN_FLAG + inc); +} + +static int abs_level_gt1_flag_ts_decode(VVCLocalContext *lc, const CodingUnit *cu, const ResidualCoding *rc, const int xc, const int yc) +{ + const TransformBlock *tb = rc->tb; + const int *sig_coeff_flag = rc->sig_coeff_flag + yc * tb->tb_width + xc; + int inc; + + if (cu->bdpcm_flag[tb->c_idx]) { + inc = 67; + } else { + const int l = xc > 0 ? sig_coeff_flag[-1] : 0; + const int a = yc > 0 ? sig_coeff_flag[-tb->tb_width] : 0; + inc = 64 + a + l; + } + return GET_CABAC(ABS_LEVEL_GTX_FLAG + inc); +} + +static int abs_level_gtx_flag_ts_decode(VVCLocalContext *lc, const int j) +{ + const int inc = 67 + j; + return GET_CABAC(ABS_LEVEL_GTX_FLAG + inc); +} + +static const uint8_t qstate_translate_table[][2] = { + { 0, 2 }, { 2, 0 }, { 1, 3 }, { 3, 1 } +}; + +static int dec_abs_level_decode(VVCLocalContext *lc, const ResidualCoding *rc, + const int xc, const int yc, int *abs_level) +{ + const int c_rice_param = abs_get_rice_param(lc, rc, xc, yc, 0); + const int dec_abs_level = abs_decode(lc, c_rice_param); + const int zero_pos = (rc->qstate < 2 ? 1 : 2) << c_rice_param; + + *abs_level = 0; + if (dec_abs_level != zero_pos) { + *abs_level = dec_abs_level; + if (dec_abs_level < zero_pos) + *abs_level += 1; + } + return dec_abs_level; +} + +static void ep_update_hist(EntryPoint *ep, ResidualCoding *rc, + const int remainder, const int addin) +{ + int *stat = ep->stat_coeff + rc->tb->c_idx; + if (rc->update_hist && remainder > 0) { + *stat = (*stat + av_log2(remainder) + addin) >> 1; + rc->update_hist = 0; + } +} + +static void init_residual_coding(VVCLocalContext *lc, ResidualCoding *rc, + const int log2_zo_tb_width, const int log2_zo_tb_height, + TransformBlock *tb) +{ + const VVCSPS *sps = lc->fc->ps.sps; + int log2_sb_w = (FFMIN(log2_zo_tb_width, log2_zo_tb_height ) < 2 ? 1 : 2 ); + int log2_sb_h = log2_sb_w; + + if ( log2_zo_tb_width + log2_zo_tb_height > 3 ) { + if ( log2_zo_tb_width < 2 ) { + log2_sb_w = log2_zo_tb_width; + log2_sb_h = 4 - log2_sb_w; + } else if ( log2_zo_tb_height < 2 ) { + log2_sb_h = log2_zo_tb_height; + log2_sb_w = 4 - log2_sb_h; + } + } + rc->log2_sb_w = log2_sb_w; + rc->log2_sb_h = log2_sb_h; + rc->num_sb_coeff = 1 << (log2_sb_w + log2_sb_h); + rc->last_sub_block = ( 1 << ( log2_zo_tb_width + log2_zo_tb_height - (log2_sb_w + log2_sb_h))) - 1; + rc->hist_value = sps->r->sps_persistent_rice_adaptation_enabled_flag ? (1 << lc->ep->stat_coeff[tb->c_idx]) : 0; + rc->update_hist = sps->r->sps_persistent_rice_adaptation_enabled_flag ? 1 : 0; + rc->rem_bins_pass1 = (( 1 << ( log2_zo_tb_width + log2_zo_tb_height)) * 7 ) >> 2; + + + rc->sb_scan_x_off = ff_vvc_diag_scan_x[log2_zo_tb_width - log2_sb_w][log2_zo_tb_height - log2_sb_h]; + rc->sb_scan_y_off = ff_vvc_diag_scan_y[log2_zo_tb_width - log2_sb_w][log2_zo_tb_height - log2_sb_h]; + + rc->scan_x_off = ff_vvc_diag_scan_x[log2_sb_w][log2_sb_h]; + rc->scan_y_off = ff_vvc_diag_scan_y[log2_sb_w][log2_sb_h]; + + rc->infer_sb_cbf = 1; + + rc->width_in_sbs = (1 << (log2_zo_tb_width - log2_sb_w)); + rc->height_in_sbs = (1 << (log2_zo_tb_height - log2_sb_h)); + rc->nb_sbs = rc->width_in_sbs * rc->height_in_sbs; + + rc->last_scan_pos = rc->num_sb_coeff; + rc->qstate = 0; + + rc->tb = tb; +} + +static int residual_ts_coding_subblock(VVCLocalContext *lc, ResidualCoding* rc, const int i) +{ + const CodingUnit *cu = lc->cu; + TransformBlock *tb = rc->tb; + const int bdpcm_flag = cu->bdpcm_flag[tb->c_idx]; + const int xs = rc->sb_scan_x_off[i]; + const int ys = rc->sb_scan_y_off[i]; + uint8_t *sb_coded_flag = rc->sb_coded_flag + ys * rc->width_in_sbs + xs; + int infer_sb_sig_coeff_flag = 1; + int last_scan_pos_pass1 = -1, last_scan_pos_pass2 = -1, n; + int abs_level_gtx_flag[MAX_SUB_BLOCK_SIZE * MAX_SUB_BLOCK_SIZE]; + int abs_level_pass2[MAX_SUB_BLOCK_SIZE * MAX_SUB_BLOCK_SIZE]; ///< AbsLevelPass2 + + if (i != rc->last_sub_block || !rc->infer_sb_cbf) + *sb_coded_flag = sb_coded_flag_decode(lc, sb_coded_flag, rc, xs, ys); + else + *sb_coded_flag = 1; + if (*sb_coded_flag && i < rc->last_sub_block) + rc->infer_sb_cbf = 0; + + //first scan pass + for (n = 0; n < rc->num_sb_coeff && rc->rem_bins_pass1 >= 4; n++) { + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + const int off = yc * tb->tb_width + xc; + int *sig_coeff_flag = rc->sig_coeff_flag + off; + int *abs_level_pass1 = rc->abs_level_pass1 + off; + int *coeff_sign_level = rc->coeff_sign_level + off; + int par_level_flag = 0; + + abs_level_gtx_flag[n] = 0; + last_scan_pos_pass1 = n; + if (*sb_coded_flag && (n != rc->num_sb_coeff - 1 || !infer_sb_sig_coeff_flag)) { + *sig_coeff_flag = sig_coeff_flag_decode(lc, rc, xc, yc); + rc->rem_bins_pass1--; + if (*sig_coeff_flag) + infer_sb_sig_coeff_flag = 0; + } else { + *sig_coeff_flag = (n == rc->num_sb_coeff - 1) && infer_sb_sig_coeff_flag && *sb_coded_flag; + } + *coeff_sign_level = 0; + if (*sig_coeff_flag) { + *coeff_sign_level = 1 - 2 * coeff_sign_flag_ts_decode(lc, cu, rc, xc, yc); + abs_level_gtx_flag[n] = abs_level_gt1_flag_ts_decode(lc, cu, rc, xc, yc); + rc->rem_bins_pass1 -= 2; + if (abs_level_gtx_flag[n]) { + par_level_flag = par_level_flag_ts_decode(lc); + rc->rem_bins_pass1--; + } + } + *abs_level_pass1 = *sig_coeff_flag + par_level_flag + abs_level_gtx_flag[n]; + } + + //greater than x scan pass + for (n = 0; n < rc->num_sb_coeff && rc->rem_bins_pass1 >= 4; n++) { + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + const int off = yc * tb->tb_width + xc; + + abs_level_pass2[n] = rc->abs_level_pass1[off]; + for (int j = 1; j < 5 && abs_level_gtx_flag[n]; j++) { + abs_level_gtx_flag[n] = abs_level_gtx_flag_ts_decode(lc, j); + abs_level_pass2[n] += abs_level_gtx_flag[n] << 1; + rc->rem_bins_pass1--; + } + last_scan_pos_pass2 = n; + } + + /* remainder scan pass */ + for (n = 0; n < rc->num_sb_coeff; n++) { + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + const int off = yc * tb->tb_width + xc; + const int *abs_level_pass1 = rc->abs_level_pass1 + off; + int *abs_level = rc->abs_level + off; + int *coeff_sign_level = rc->coeff_sign_level + off; + int abs_remainder = 0; + + if ((n <= last_scan_pos_pass2 && abs_level_pass2[n] >= 10) || + (n > last_scan_pos_pass2 && n <= last_scan_pos_pass1 && + *abs_level_pass1 >= 2) || + (n > last_scan_pos_pass1 && *sb_coded_flag)) + abs_remainder = abs_remainder_ts_decode(lc, rc, xc, yc); + if (n <= last_scan_pos_pass2) { + *abs_level = abs_level_pass2[n] + 2 * abs_remainder; + } else if (n <= last_scan_pos_pass1) { + *abs_level = *abs_level_pass1 + 2 * abs_remainder; + } else { + *abs_level = abs_remainder; + if (abs_remainder) { + //n > lastScanPosPass1 + *coeff_sign_level = 1 - 2 * coeff_sign_flag_decode(lc); + } + } + if (!bdpcm_flag && n <= last_scan_pos_pass1) { + const int left = xc > 0 ? abs_level[-1] : 0; + const int above = yc > 0 ? abs_level[-tb->tb_width] : 0; + const int pred = FFMAX(left, above); + + if (*abs_level == 1 && pred > 0) + *abs_level = pred; + else if (*abs_level > 0 && *abs_level <= pred) + (*abs_level)--; + } + if (*abs_level) { + tb->coeffs[off] = *coeff_sign_level * *abs_level; + tb->max_scan_x = FFMAX(xc, tb->max_scan_x); + tb->max_scan_y = FFMAX(yc, tb->max_scan_y); + tb->min_scan_x = FFMIN(xc, tb->min_scan_x); + tb->min_scan_y = FFMIN(yc, tb->min_scan_y); + } else { + tb->coeffs[off] = 0; + } + } + + return 0; +} + +static int hls_residual_ts_coding(VVCLocalContext *lc, TransformBlock *tb) +{ + ResidualCoding rc; + tb->min_scan_x = tb->min_scan_y = INT_MAX; + init_residual_coding(lc, &rc, tb->log2_tb_width, tb->log2_tb_height, tb); + for (int i = 0; i <= rc.last_sub_block; i++) { + int ret = residual_ts_coding_subblock(lc, &rc, i); + if (ret < 0) + return ret; + } + + return 0; +} + +static inline int residual_coding_subblock(VVCLocalContext *lc, ResidualCoding *rc, const int i) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + TransformBlock *tb = rc->tb; + int first_sig_scan_pos_sb, last_sig_scan_pos_sb; + int first_pos_mode0, first_pos_mode1; + int infer_sb_dc_sig_coeff_flag = 0; + int n, sig_hidden_flag, sum = 0; + int abs_level_gt2_flag[MAX_SUB_BLOCK_SIZE * MAX_SUB_BLOCK_SIZE]; + const int start_qstate_sb = rc->qstate; + const int xs = rc->sb_scan_x_off[i]; + const int ys = rc->sb_scan_y_off[i]; + uint8_t *sb_coded_flag = rc->sb_coded_flag + ys * rc->width_in_sbs + xs; + + + av_assert0(rc->num_sb_coeff <= MAX_SUB_BLOCK_SIZE * MAX_SUB_BLOCK_SIZE); + if (i < rc->last_sub_block && i > 0) { + *sb_coded_flag = sb_coded_flag_decode(lc, sb_coded_flag, rc, xs, ys); + infer_sb_dc_sig_coeff_flag = 1; + } else { + *sb_coded_flag = 1; + } + if (*sb_coded_flag && (xs > 3 || ys > 3) && tb->c_idx == 0) + lc->parse.mts_zero_out_sig_coeff_flag = 0; + + if (!*sb_coded_flag) + return 0; + + first_sig_scan_pos_sb = rc->num_sb_coeff; + last_sig_scan_pos_sb = -1; + first_pos_mode0 = (i == rc->last_sub_block ? rc->last_scan_pos : rc->num_sb_coeff -1); + first_pos_mode1 = first_pos_mode0; + for (n = first_pos_mode0; n >= 0 && rc->rem_bins_pass1 >= 4; n--) { + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + const int last = (xc == rc->last_significant_coeff_x && yc == rc->last_significant_coeff_y); + int *abs_level_pass1 = rc->abs_level_pass1 + yc * tb->tb_width + xc; + int *sig_coeff_flag = rc->sig_coeff_flag + yc * tb->tb_width + xc; + + if ((n > 0 || !infer_sb_dc_sig_coeff_flag ) && !last) { + *sig_coeff_flag = sig_coeff_flag_decode(lc, rc, xc, yc); + rc->rem_bins_pass1--; + if (*sig_coeff_flag) + infer_sb_dc_sig_coeff_flag = 0; + } else { + *sig_coeff_flag = last || (!rc->scan_x_off[n] && !rc ->scan_y_off[n] && + infer_sb_dc_sig_coeff_flag); + } + *abs_level_pass1 = 0; + if (*sig_coeff_flag) { + int abs_level_gt1_flag, par_level_flag = 0; + const int inc = get_gtx_flag_inc(rc, xc, yc, last); + abs_level_gt1_flag = abs_level_gtx_flag_decode(lc, inc); + rc->rem_bins_pass1--; + if (abs_level_gt1_flag) { + par_level_flag = par_level_flag_decode(lc, inc); + abs_level_gt2_flag[n] = abs_level_gtx_flag_decode(lc, inc + 32); + rc->rem_bins_pass1 -= 2; + } else { + abs_level_gt2_flag[n] = 0; + } + if (last_sig_scan_pos_sb == -1) + last_sig_scan_pos_sb = n; + first_sig_scan_pos_sb = n; + + *abs_level_pass1 = + 1 + par_level_flag + abs_level_gt1_flag + (abs_level_gt2_flag[n] << 1); + } else { + abs_level_gt2_flag[n] = 0; + } + + if (rsh->sh_dep_quant_used_flag) + rc->qstate = qstate_translate_table[rc->qstate][*abs_level_pass1 & 1]; + + first_pos_mode1 = n - 1; + } + for (n = first_pos_mode0; n > first_pos_mode1; n--) { + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + const int *abs_level_pass1 = rc->abs_level_pass1 + yc * tb->tb_width + xc; + int *abs_level = rc->abs_level + yc * tb->tb_width + xc; + + *abs_level = *abs_level_pass1; + if (abs_level_gt2_flag[n]) { + const int abs_remainder = abs_remainder_decode(lc, rc, xc, yc); + ep_update_hist(lc->ep, rc, abs_remainder, 2); + *abs_level += 2 * abs_remainder; + } + } + for (n = first_pos_mode1; n >= 0; n--) { + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + int *abs_level = rc->abs_level + yc * tb->tb_width + xc; + + if (*sb_coded_flag) { + const int dec_abs_level = dec_abs_level_decode(lc, rc, xc, yc, abs_level); + ep_update_hist(lc->ep, rc, dec_abs_level, 0); + } + if (*abs_level > 0) { + if (last_sig_scan_pos_sb == -1) + last_sig_scan_pos_sb = n; + first_sig_scan_pos_sb = n; + } + if (rsh->sh_dep_quant_used_flag) + rc->qstate = qstate_translate_table[rc->qstate][*abs_level & 1]; + } + sig_hidden_flag = rsh->sh_sign_data_hiding_used_flag && + (last_sig_scan_pos_sb - first_sig_scan_pos_sb > 3 ? 1 : 0); + + if (rsh->sh_dep_quant_used_flag) + rc->qstate = start_qstate_sb; + n = (i == rc->last_sub_block ? rc->last_scan_pos : rc->num_sb_coeff -1); + for (/* nothing */; n >= 0; n--) { + int trans_coeff_level; + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + const int off = yc * tb->tb_width + xc; + const int *abs_level = rc->abs_level + off; + + if (*abs_level > 0) { + int sign = 1; + if (!sig_hidden_flag || (n != first_sig_scan_pos_sb)) + sign = 1 - 2 * coeff_sign_flag_decode(lc); + if (rsh->sh_dep_quant_used_flag) { + trans_coeff_level = (2 * *abs_level - (rc->qstate > 1)) * sign; + } else { + trans_coeff_level = *abs_level * sign; + if (sig_hidden_flag) { + sum += *abs_level; + if (n == first_sig_scan_pos_sb && (sum % 2)) + trans_coeff_level = -trans_coeff_level; + } + } + tb->coeffs[off] = trans_coeff_level; + tb->max_scan_x = FFMAX(xc, tb->max_scan_x); + tb->max_scan_y = FFMAX(yc, tb->max_scan_y); + } + if (rsh->sh_dep_quant_used_flag) + rc->qstate = qstate_translate_table[rc->qstate][*abs_level & 1]; + } + + return 0; +} + +static void derive_last_scan_pos(ResidualCoding *rc, + const int log2_zo_tb_width, int log2_zo_tb_height) +{ + int xc, yc, xs, ys; + do { + if (rc->last_scan_pos == 0) { + rc->last_scan_pos = rc->num_sb_coeff; + rc->last_sub_block--; + } + rc->last_scan_pos--; + xs = rc->sb_scan_x_off[rc->last_sub_block]; + ys = rc->sb_scan_y_off[rc->last_sub_block]; + xc = (xs << rc->log2_sb_w) + rc->scan_x_off[rc->last_scan_pos]; + yc = (ys << rc->log2_sb_h) + rc->scan_y_off[rc->last_scan_pos]; + } while ((xc != rc->last_significant_coeff_x) || (yc != rc->last_significant_coeff_y)); +} + +static void last_significant_coeff_x_y_decode(ResidualCoding *rc, VVCLocalContext *lc, + const int log2_zo_tb_width, const int log2_zo_tb_height) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const TransformBlock *tb = rc->tb; + int last_significant_coeff_x, last_significant_coeff_y; + + last_significant_coeff_x = last_significant_coeff_x_prefix_decode(lc, + tb->log2_tb_width, log2_zo_tb_width, tb->c_idx); + + last_significant_coeff_y = last_significant_coeff_y_prefix_decode(lc, + tb->log2_tb_height, log2_zo_tb_height, tb->c_idx); + + if (last_significant_coeff_x > 3) { + int suffix = last_sig_coeff_suffix_decode(lc, last_significant_coeff_x); + last_significant_coeff_x = (1 << ((last_significant_coeff_x >> 1) - 1)) * + (2 + (last_significant_coeff_x & 1)) + suffix; + } + if (last_significant_coeff_y > 3) { + int suffix = last_sig_coeff_suffix_decode(lc, last_significant_coeff_y); + last_significant_coeff_y = (1 << ((last_significant_coeff_y >> 1) - 1)) * + (2 + (last_significant_coeff_y & 1)) + suffix; + } + if (rsh->sh_reverse_last_sig_coeff_flag) { + last_significant_coeff_x = (1 << log2_zo_tb_width) - 1 - last_significant_coeff_x; + last_significant_coeff_y = (1 << log2_zo_tb_height) - 1 - last_significant_coeff_y; + } + rc->last_significant_coeff_x = last_significant_coeff_x; + rc->last_significant_coeff_y = last_significant_coeff_y; +} + +static int hls_residual_coding(VVCLocalContext *lc, TransformBlock *tb) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const CodingUnit *cu = lc->cu; + const int log2_tb_width = tb->log2_tb_width; + const int log2_tb_height = tb->log2_tb_height; + const int c_idx = tb->c_idx; + int log2_zo_tb_width, log2_zo_tb_height; + ResidualCoding rc; + + if (sps->r->sps_mts_enabled_flag && cu->sbt_flag && c_idx == 0 && log2_tb_width == 5 && log2_tb_height < 6) + log2_zo_tb_width = 4; + else + log2_zo_tb_width = FFMIN(log2_tb_width, 5 ); + + if (sps->r->sps_mts_enabled_flag && cu->sbt_flag && c_idx == 0 && log2_tb_width < 6 && log2_tb_height == 5 ) + log2_zo_tb_height = 4; + else + log2_zo_tb_height = FFMIN(log2_tb_height, 5); + + init_residual_coding(lc, &rc, log2_zo_tb_width, log2_zo_tb_height, tb); + last_significant_coeff_x_y_decode(&rc, lc, log2_zo_tb_width, log2_zo_tb_height); + derive_last_scan_pos(&rc, log2_zo_tb_width, log2_zo_tb_height); + + if (rc.last_sub_block == 0 && log2_tb_width >= 2 && log2_tb_height >= 2 && !tb->ts && rc.last_scan_pos > 0) + lc->parse.lfnst_dc_only = 0; + if ((rc.last_sub_block > 0 && log2_tb_width >= 2 && log2_tb_height >= 2 ) || + (rc.last_scan_pos > 7 && (log2_tb_width == 2 || log2_tb_width == 3 ) && + log2_tb_width == log2_tb_height)) + lc->parse.lfnst_zero_out_sig_coeff_flag = 0; + if ((rc.last_sub_block > 0 || rc.last_scan_pos > 0 ) && c_idx == 0) + lc->parse.mts_dc_only = 0; + + memset(tb->coeffs, 0, tb->tb_width * tb->tb_height * sizeof(*tb->coeffs)); + memset(rc.abs_level, 0, tb->tb_width * tb->tb_height * sizeof(rc.abs_level[0])); + memset(rc.sb_coded_flag, 0, rc.nb_sbs); + memset(rc.abs_level_pass1, 0, tb->tb_width * tb->tb_height * sizeof(rc.abs_level_pass1[0])); + memset(rc.sig_coeff_flag, 0, tb->tb_width * tb->tb_height * sizeof(rc.sig_coeff_flag[0])); + + for (int i = rc.last_sub_block; i >= 0; i--) { + int ret = residual_coding_subblock(lc, &rc, i); + if (ret < 0) + return ret; + } + + return 0; +} + +int ff_vvc_residual_coding(VVCLocalContext *lc, TransformBlock *tb) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int ts = !rsh->sh_ts_residual_coding_disabled_flag && tb->ts; + + return ts ? hls_residual_ts_coding(lc, tb) : hls_residual_coding(lc, tb); +} + +int ff_vvc_cu_coded_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CU_CODED_FLAG); +} + +int ff_vvc_sbt_flag(VVCLocalContext *lc) +{ + const int w = lc->cu->cb_width; + const int h = lc->cu->cb_height; + const int inc = w * h <= 256; + return GET_CABAC(CU_SBT_FLAG + inc); +} + +int ff_vvc_sbt_quad_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CU_SBT_QUAD_FLAG); +} + +int ff_vvc_sbt_horizontal_flag(VVCLocalContext *lc) +{ + const int w = lc->cu->cb_width; + const int h = lc->cu->cb_height; + const int inc = (w == h) ? 0 : ((w < h) ? 1 : 2); + return GET_CABAC(CU_SBT_HORIZONTAL_FLAG + inc); +} + +int ff_vvc_sbt_pos_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CU_SBT_POS_FLAG); +} + +int ff_vvc_lfnst_idx(VVCLocalContext *lc, const int inc) +{ + if (!GET_CABAC(LFNST_IDX + inc)) + return 0; + if (!GET_CABAC(LFNST_IDX + 2)) + return 1; + return 2; +} + +int ff_vvc_mts_idx(VVCLocalContext *lc) +{ + int i; + for (i = 0; i < 4; i++) { + if (!GET_CABAC(MTS_IDX + i)) + return i; + } + return i; +} + +int ff_vvc_end_of_slice_flag_decode(VVCLocalContext *lc) +{ + return get_cabac_terminate(&lc->ep->cc); +} + +int ff_vvc_end_of_tile_one_bit(VVCLocalContext *lc) +{ + return get_cabac_terminate(&lc->ep->cc); +} + +int ff_vvc_end_of_subset_one_bit(VVCLocalContext *lc) +{ + return get_cabac_terminate(&lc->ep->cc); +} diff --git a/libavcodec/vvc/vvc_cabac.h b/libavcodec/vvc/vvc_cabac.h new file mode 100644 index 0000000000..5bb1ae83f7 --- /dev/null +++ b/libavcodec/vvc/vvc_cabac.h @@ -0,0 +1,126 @@ +/* + * VVC CABAC decoder + * + * Copyright (C) 2022 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VVC_CABAC_H +#define AVCODEC_VVC_CABAC_H + +#include "vvc_ctu.h" + +int ff_vvc_cabac_init(VVCLocalContext *lc, int ctu_idx, int rx, int ry); + +//sao +int ff_vvc_sao_merge_flag_decode(VVCLocalContext *lc); +int ff_vvc_sao_type_idx_decode(VVCLocalContext *lc); +int ff_vvc_sao_band_position_decode(VVCLocalContext *lc); +int ff_vvc_sao_offset_abs_decode(VVCLocalContext *lc); +int ff_vvc_sao_offset_sign_decode(VVCLocalContext *lc); +int ff_vvc_sao_eo_class_decode(VVCLocalContext *lc); + +//alf +int ff_vvc_alf_ctb_flag(VVCLocalContext *lc, int rx, int ry, int c_idx); +int ff_vvc_alf_use_aps_flag(VVCLocalContext *lc); +int ff_vvc_alf_luma_prev_filter_idx(VVCLocalContext *lc); +int ff_vvc_alf_luma_fixed_filter_idx(VVCLocalContext *lc); +int ff_vvc_alf_ctb_filter_alt_idx(VVCLocalContext *lc, int c_idx, int num_chroma_filters); +int ff_vvc_alf_ctb_cc_idc(VVCLocalContext *lc, int rx, int ry, int idx, int cc_filters_signalled); + +//coding_tree +int ff_vvc_split_cu_flag(VVCLocalContext* lc, int x0, int y0, int cb_width, int cb_height, + int ch_type, const VVCAllowedSplit *a); +VVCSplitMode ff_vvc_split_mode(VVCLocalContext *lc, int x0, int y0, int cb_width, int cb_height, + int cqt_depth, int mtt_depth, int ch_type, const VVCAllowedSplit *a); +int ff_vvc_non_inter_flag(VVCLocalContext *lc, int x0, int y0, int ch_type); + +//coding unit +int ff_vvc_pred_mode_flag(VVCLocalContext *lc, int is_chroma); +int ff_vvc_pred_mode_plt_flag(VVCLocalContext *lc); +int ff_vvc_intra_bdpcm_luma_flag(VVCLocalContext *lc); +int ff_vvc_intra_bdpcm_luma_dir_flag(VVCLocalContext *lc); +int ff_vvc_intra_bdpcm_chroma_flag(VVCLocalContext *lc); +int ff_vvc_intra_bdpcm_chroma_dir_flag(VVCLocalContext *lc); +int ff_vvc_cu_skip_flag(VVCLocalContext *lc, const uint8_t *cu_skip_flag); +int ff_vvc_pred_mode_ibc_flag(VVCLocalContext *lc, int ch_type); +int ff_vvc_cu_coded_flag(VVCLocalContext *lc); +int ff_vvc_cu_qp_delta_abs(VVCLocalContext *lc); +int ff_vvc_cu_qp_delta_sign_flag(VVCLocalContext *lc); +int ff_vvc_sbt_flag(VVCLocalContext *lc); +int ff_vvc_sbt_quad_flag(VVCLocalContext *lc); +int ff_vvc_sbt_horizontal_flag(VVCLocalContext *lc); +int ff_vvc_sbt_pos_flag(VVCLocalContext *lc); + +//intra +int ff_vvc_intra_mip_flag(VVCLocalContext *lc, const uint8_t *intra_mip_flag); +int ff_vvc_intra_mip_transposed_flag(VVCLocalContext *lc); +int ff_vvc_intra_mip_mode(VVCLocalContext *lc); +int ff_vvc_intra_luma_ref_idx(VVCLocalContext *lc); +int ff_vvc_intra_subpartitions_mode_flag(VVCLocalContext *lc); +enum IspType ff_vvc_isp_split_type(VVCLocalContext *lc, int intra_subpartitions_mode_flag); +int ff_vvc_intra_luma_mpm_flag(VVCLocalContext *lc); +int ff_vvc_intra_luma_not_planar_flag(VVCLocalContext *lc, int intra_subpartitions_mode_flag); +int ff_vvc_intra_luma_mpm_idx(VVCLocalContext *lc); +int ff_vvc_intra_luma_mpm_remainder(VVCLocalContext *lc); +int ff_vvc_cclm_mode_flag(VVCLocalContext *lc); +int ff_vvc_cclm_mode_idx(VVCLocalContext *lc); +int ff_vvc_intra_chroma_pred_mode(VVCLocalContext *lc); + +//inter +int ff_vvc_general_merge_flag(VVCLocalContext *lc); +int ff_vvc_merge_subblock_flag(VVCLocalContext *lc); +int ff_vvc_merge_subblock_idx(VVCLocalContext *lc, int max_num_subblock_merge_cand); +int ff_vvc_regular_merge_flag(VVCLocalContext *lc, int cu_skip_flag); +int ff_vvc_merge_idx(VVCLocalContext *lc); +int ff_vvc_mmvd_merge_flag(VVCLocalContext *lc); +int ff_vvc_mmvd_cand_flag(VVCLocalContext *lc); +void ff_vvc_mmvd_offset_coding(VVCLocalContext *lc, Mv *mvd_offset, int ph_mmvd_fullpel_only_flag); +int ff_vvc_ciip_flag(VVCLocalContext *lc); +int ff_vvc_merge_gpm_partition_idx(VVCLocalContext *lc); +int ff_vvc_merge_gpm_idx(VVCLocalContext *lc, int idx); +PredFlag ff_vvc_pred_flag(VVCLocalContext *lc, int is_b); +int ff_vvc_inter_affine_flag(VVCLocalContext *lc); +int ff_vvc_cu_affine_type_flag(VVCLocalContext *lc); +int ff_vvc_sym_mvd_flag(VVCLocalContext *lc); +int ff_vvc_ref_idx_lx(VVCLocalContext *lc, uint8_t nb_refs); +int ff_vvc_abs_mvd_greater0_flag(VVCLocalContext *lc); +int ff_vvc_abs_mvd_greater1_flag(VVCLocalContext *lc); +int ff_vvc_abs_mvd_minus2(VVCLocalContext *lc); +int ff_vvc_mvd_sign_flag(VVCLocalContext *lc); +int ff_vvc_mvp_lx_flag(VVCLocalContext *lc); +int ff_vvc_amvr_shift(VVCLocalContext *lc, int inter_affine_flag, PredMode pred_mode, int has_amvr_flag); +int ff_vvc_bcw_idx(VVCLocalContext *lc, int no_backward_pred_flag); + +//transform +int ff_vvc_tu_cb_coded_flag(VVCLocalContext *lc); +int ff_vvc_tu_cr_coded_flag(VVCLocalContext *lc, int tu_cb_coded_flag); +int ff_vvc_tu_y_coded_flag(VVCLocalContext *lc); +int ff_vvc_cu_chroma_qp_offset_flag(VVCLocalContext *lc); +int ff_vvc_cu_chroma_qp_offset_idx(VVCLocalContext *lc); +int ff_vvc_tu_joint_cbcr_residual_flag(VVCLocalContext *lc, int tu_cb_coded_flag, int tu_cr_coded_flag); +int ff_vvc_transform_skip_flag(VVCLocalContext *lc, int ctx); +int ff_vvc_residual_coding(VVCLocalContext *lc, TransformBlock *tb); +int ff_vvc_lfnst_idx(VVCLocalContext *lc, int inc); +int ff_vvc_mts_idx(VVCLocalContext *lc); + +int ff_vvc_end_of_slice_flag_decode(VVCLocalContext *lc); +int ff_vvc_end_of_tile_one_bit(VVCLocalContext *lc); +int ff_vvc_end_of_subset_one_bit(VVCLocalContext *lc); + +#endif //AVCODEC_VVC_CABAC_H diff --git a/libavcodec/vvc/vvc_ctu.c b/libavcodec/vvc/vvc_ctu.c new file mode 100644 index 0000000000..78b13ffb00 --- /dev/null +++ b/libavcodec/vvc/vvc_ctu.c @@ -0,0 +1,32 @@ +/* + * VVC CTU(Coding Tree Unit) parser + * + * Copyright (C) 2022 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "vvc_ctu.h" + +void ff_vvc_ep_init_stat_coeff(EntryPoint *ep, + const int bit_depth, const int persistent_rice_adaptation_enabled_flag) +{ + for (size_t i = 0; i < FF_ARRAY_ELEMS(ep->stat_coeff); ++i) { + ep->stat_coeff[i] = + persistent_rice_adaptation_enabled_flag ? 2 * (av_log2(bit_depth - 10)) : 0; + } +} diff --git a/libavcodec/vvc/vvc_ctu.h b/libavcodec/vvc/vvc_ctu.h new file mode 100644 index 0000000000..eb917dcb55 --- /dev/null +++ b/libavcodec/vvc/vvc_ctu.h @@ -0,0 +1,463 @@ +/* + * VVC CTU(Coding Tree Unit) parser + * + * Copyright (C) 2022 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VVC_CTU_H +#define AVCODEC_VVC_CTU_H + +#include "libavcodec/cabac.h" +#include "libavutil/mem_internal.h" + +#include "vvcdec.h" + +#define MAX_CTU_SIZE 128 + +#define MAX_CU_SIZE MAX_CTU_SIZE +#define MIN_CU_SIZE 4 +#define MIN_CU_LOG2 2 +#define MAX_CU_DEPTH 7 + +#define MAX_PARTS_IN_CTU ((MAX_CTU_SIZE >> MIN_CU_LOG2) * (MAX_CTU_SIZE >> MIN_CU_LOG2)) + +#define MIN_PU_SIZE 4 + +#define MAX_TB_SIZE 64 +#define MIN_TU_SIZE 4 +#define MAX_TUS_IN_CU 64 + +#define MAX_QP 63 + +#define MAX_PB_SIZE 128 +#define EDGE_EMU_BUFFER_STRIDE (MAX_PB_SIZE + 32) + +#define CHROMA_EXTRA_BEFORE 1 +#define CHROMA_EXTRA_AFTER 2 +#define CHROMA_EXTRA 3 +#define LUMA_EXTRA_BEFORE 3 +#define LUMA_EXTRA_AFTER 4 +#define LUMA_EXTRA 7 +#define BILINEAR_EXTRA_BEFORE 0 +#define BILINEAR_EXTRA_AFTER 1 +#define BILINEAR_EXTRA 1 + +#define MAX_CONTROL_POINTS 3 + +#define AFFINE_MIN_BLOCK_SIZE 4 + +#define MRG_MAX_NUM_CANDS 6 +#define MAX_NUM_HMVP_CANDS 5 + +#define SAO_PADDING_SIZE 1 + +#define ALF_PADDING_SIZE 8 +#define ALF_BLOCK_SIZE 4 + +#define ALF_BORDER_LUMA 3 +#define ALF_BORDER_CHROMA 2 + +#define ALF_VB_POS_ABOVE_LUMA 4 +#define ALF_VB_POS_ABOVE_CHROMA 2 + +#define ALF_GRADIENT_STEP 2 +#define ALF_GRADIENT_BORDER 2 +#define ALF_GRADIENT_SIZE ((MAX_CU_SIZE + ALF_GRADIENT_BORDER * 2) / ALF_GRADIENT_STEP) +#define ALF_NUM_DIR 4 + + +/** + * Value of the luma sample at position (x, y) in the 2D array tab. + */ +#define SAMPLE(tab, x, y) ((tab)[(y) * s->sps->width + (x)]) +#define SAMPLE_CTB(tab, x, y) ((tab)[(y) * min_cb_width + (x)]) +#define CTB(tab, x, y) ((tab)[(y) * fc->ps.pps->ctb_width + (x)]) + +enum SAOType { + SAO_NOT_APPLIED = 0, + SAO_BAND, + SAO_EDGE, +}; + +enum SAOEOClass { + SAO_EO_HORIZ = 0, + SAO_EO_VERT, + SAO_EO_135D, + SAO_EO_45D, +}; + +typedef struct NeighbourAvailable { + int cand_left; + int cand_up; + int cand_up_left; + int cand_up_right; + int cand_up_right_sap; +} NeighbourAvailable; + +enum IspType{ + ISP_NO_SPLIT, + ISP_HOR_SPLIT, + ISP_VER_SPLIT, +}; + +typedef enum VVCSplitMode { + SPLIT_NONE, + SPLIT_TT_HOR, + SPLIT_BT_HOR, + SPLIT_TT_VER, + SPLIT_BT_VER, + SPLIT_QT, +} VVCSplitMode; + +typedef enum MtsIdx { + MTS_DCT2_DCT2, + MTS_DST7_DST7, + MTS_DST7_DCT8, + MTS_DCT8_DST7, + MTS_DCT8_DCT8, +} MtsIdx; + +typedef struct TransformBlock { + uint8_t has_coeffs; + uint8_t c_idx; + uint8_t ts; ///< transform_skip_flag + int x0; + int y0; + + int tb_width; + int tb_height; + int log2_tb_width; + int log2_tb_height; + + int max_scan_x; + int max_scan_y; + int min_scan_x; + int min_scan_y; + + int qp; + int rect_non_ts_flag; + int bd_shift; + int bd_offset; + + int *coeffs; +} TransformBlock; + +typedef enum VVCTreeType { + SINGLE_TREE, + DUAL_TREE_LUMA, + DUAL_TREE_CHROMA, +} VVCTreeType; + +typedef struct TransformUnit { + int x0; + int y0; + int width; + int height; + + uint8_t joint_cbcr_residual_flag; ///< tu_joint_cbcr_residual_flag + + uint8_t coded_flag[VVC_MAX_SAMPLE_ARRAYS]; ///< tu_y_coded_flag, tu_cb_coded_flag, tu_cr_coded_flag + uint8_t nb_tbs; + TransformBlock tbs[VVC_MAX_SAMPLE_ARRAYS]; + + struct TransformUnit *next; ///< RefStruct reference +} TransformUnit; + +typedef enum PredMode { + MODE_INTER, + MODE_INTRA, + MODE_SKIP, + MODE_PLT, + MODE_IBC, +} PredMode; + +typedef struct Mv { + int x; ///< horizontal component of motion vector + int y; ///< vertical component of motion vector +} Mv; + +typedef struct MvField { + DECLARE_ALIGNED(4, Mv, mv)[2]; ///< mvL0, vvL1 + int8_t ref_idx[2]; ///< refIdxL0, refIdxL1 + uint8_t hpel_if_idx; ///< hpelIfIdx + uint8_t bcw_idx; ///< bcwIdx + uint8_t pred_flag; + uint8_t ciip_flag; ///< ciip_flag +} MvField; + +typedef struct DMVRInfo { + DECLARE_ALIGNED(4, Mv, mv)[2]; ///< mvL0, vvL1 + uint8_t dmvr_enabled; +} DMVRInfo; + +typedef enum MotionModelIdc { + MOTION_TRANSLATION, + MOTION_4_PARAMS_AFFINE, + MOTION_6_PARAMS_AFFINE, +} MotionModelIdc; + +typedef enum PredFlag { + PF_INTRA = 0x0, + PF_L0 = 0x1, + PF_L1 = 0x2, + PF_BI = 0x3, +} PredFlag; + +typedef enum IntraPredMode { + INTRA_INVALID = -1, + INTRA_PLANAR = 0, + INTRA_DC, + INTRA_HORZ = 18, + INTRA_DIAG = 34, + INTRA_VERT = 50, + INTRA_VDIAG = 66, + INTRA_LT_CCLM = 81, + INTRA_L_CCLM, + INTRA_T_CCLM +} IntraPredMode; + +typedef struct MotionInfo { + MotionModelIdc motion_model_idc; ///< MotionModelIdc + int8_t ref_idx[2]; ///< refIdxL0, refIdxL1 + uint8_t hpel_if_idx; ///< hpelIfIdx + uint8_t bcw_idx; ///< bcwIdx + PredFlag pred_flag; + + Mv mv[2][MAX_CONTROL_POINTS]; + + int num_sb_x, num_sb_y; +} MotionInfo; + +typedef struct PredictionUnit { + uint8_t general_merge_flag; + uint8_t mmvd_merge_flag; + //InterPredIdc inter_pred_idc; + uint8_t inter_affine_flag; + + //subblock predict + uint8_t merge_subblock_flag; + + uint8_t merge_gpm_flag; + uint8_t gpm_partition_idx; + MvField gpm_mv[2]; + + int sym_mvd_flag; + + MotionInfo mi; + + // for regular prediction only + uint8_t dmvr_flag; + uint8_t bdof_flag; + + int16_t diff_mv_x[2][AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; ///< diffMvLX + int16_t diff_mv_y[2][AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; ///< diffMvLX + int cb_prof_flag[2]; +} PredictionUnit; + +typedef struct CodingUnit { + VVCTreeType tree_type; + int x0; + int y0; + int cb_width; + int cb_height; + int ch_type; + int cqt_depth; + + uint8_t coded_flag; + + uint8_t sbt_flag; + uint8_t sbt_horizontal_flag; + uint8_t sbt_pos_flag; + + int lfnst_idx; + MtsIdx mts_idx; + + uint8_t act_enabled_flag; + + uint8_t intra_luma_ref_idx; ///< IntraLumaRefLineIdx[][] + uint8_t intra_mip_flag; ///< intra_mip_flag + uint8_t skip_flag; ///< cu_skip_flag; + + //inter + uint8_t ciip_flag; + + // Inferred parameters + enum IspType isp_split_type; ///< IntraSubPartitionsSplitType + + enum PredMode pred_mode; ///< PredMode + + int num_intra_subpartitions; + + IntraPredMode intra_pred_mode_y; ///< IntraPredModeY + IntraPredMode intra_pred_mode_c; ///< IntraPredModeC + int mip_chroma_direct_flag; ///< MipChromaDirectFlag + + int bdpcm_flag[VVC_MAX_SAMPLE_ARRAYS]; ///< BdpcmFlag + + int apply_lfnst_flag[VVC_MAX_SAMPLE_ARRAYS]; ///< ApplyLfnstFlag[] + + struct { + TransformUnit *head; ///< RefStruct reference + TransformUnit *tail; ///< RefStruct reference + } tus; + + int8_t qp[4]; ///< QpY, Qp′Cb, Qp′Cr, Qp′CbCr + + PredictionUnit pu; + + struct CodingUnit *next; ///< RefStruct reference +} CodingUnit; + +typedef struct CTU { + CodingUnit *cus; + int max_y[2][VVC_MAX_REF_ENTRIES]; + int max_y_idx[2]; +} CTU; + +typedef struct ReconstructedArea { + int x; + int y; + int w; + int h; +} ReconstructedArea; + +typedef struct VVCCabacState { + uint16_t state[2]; + uint8_t shift[2]; +} VVCCabacState; + +// VVC_CONTEXTS matched with SYNTAX_ELEMENT_LAST, it's checked by cabac_init_state. +#define VVC_CONTEXTS 378 +typedef struct EntryPoint { + int8_t qp_y; ///< QpY + + int stat_coeff[VVC_MAX_SAMPLE_ARRAYS]; ///< StatCoeff + + VVCCabacState cabac_state[VVC_CONTEXTS]; + CABACContext cc; + + int ctu_start; + int ctu_end; + + uint8_t is_first_qg; // first quantization group + MvField hmvp[MAX_NUM_HMVP_CANDS]; ///< HmvpCandList + int num_hmvp; ///< NumHmvpCand +} EntryPoint; + +typedef struct VVCLocalContext { + uint8_t ctb_left_flag; + uint8_t ctb_up_flag; + uint8_t ctb_up_right_flag; + uint8_t ctb_up_left_flag; + int end_of_tiles_x; + int end_of_tiles_y; + + /* +7 is for subpixel interpolation, *2 for high bit depths */ + DECLARE_ALIGNED(32, uint8_t, edge_emu_buffer)[(MAX_PB_SIZE + 7) * EDGE_EMU_BUFFER_STRIDE * 2]; + /* The extended size between the new edge emu buffer is abused by SAO */ + DECLARE_ALIGNED(32, uint8_t, edge_emu_buffer2)[(MAX_PB_SIZE + 7) * EDGE_EMU_BUFFER_STRIDE * 2]; + DECLARE_ALIGNED(32, int16_t, tmp)[MAX_PB_SIZE * MAX_PB_SIZE]; + DECLARE_ALIGNED(32, int16_t, tmp1)[MAX_PB_SIZE * MAX_PB_SIZE]; + DECLARE_ALIGNED(32, int16_t, tmp2)[MAX_PB_SIZE * MAX_PB_SIZE]; + DECLARE_ALIGNED(32, uint8_t, ciip_tmp1)[MAX_PB_SIZE * MAX_PB_SIZE * 2]; + DECLARE_ALIGNED(32, uint8_t, ciip_tmp2)[MAX_PB_SIZE * MAX_PB_SIZE * 2]; + DECLARE_ALIGNED(32, uint8_t, sao_buffer)[(MAX_CTU_SIZE + 2 * SAO_PADDING_SIZE) * EDGE_EMU_BUFFER_STRIDE * 2]; + DECLARE_ALIGNED(32, uint8_t, alf_buffer_luma)[(MAX_CTU_SIZE + 2 * ALF_PADDING_SIZE) * EDGE_EMU_BUFFER_STRIDE * 2]; + DECLARE_ALIGNED(32, uint8_t, alf_buffer_chroma)[(MAX_CTU_SIZE + 2 * ALF_PADDING_SIZE) * EDGE_EMU_BUFFER_STRIDE * 2]; + DECLARE_ALIGNED(32, int32_t, alf_gradient_tmp)[ALF_GRADIENT_SIZE * ALF_GRADIENT_SIZE * ALF_NUM_DIR]; + + struct { + int sbt_num_fourths_tb0; ///< SbtNumFourthsTb0 + + uint8_t is_cu_qp_delta_coded; ///< IsCuQpDeltaCoded + int cu_qg_top_left_x; ///< CuQgTopLeftX + int cu_qg_top_left_y; ///< CuQgTopLeftY + int is_cu_chroma_qp_offset_coded; ///< IsCuChromaQpOffsetCoded + int chroma_qp_offset[3]; ///< CuQpOffsetCb, CuQpOffsetCr, CuQpOffsetCbCr + + int infer_tu_cbf_luma; ///< InferTuCbfLuma + int prev_tu_cbf_y; ///< prevTuCbfY; + + int lfnst_dc_only; ///< LfnstDcOnly + int lfnst_zero_out_sig_coeff_flag; ///< LfnstZeroOutSigCoeffFlag + + int mts_dc_only; ///< MtsDcOnly + int mts_zero_out_sig_coeff_flag; ///< MtsZeroOutSigCoeffFlag; + } parse; + + struct { + // lmcs cache, for recon only + int chroma_scale; + int x_vpdu; + int y_vpdu; + } lmcs; + + CodingUnit *cu; + ReconstructedArea ras[2][MAX_PARTS_IN_CTU]; + int num_ras[2]; + + NeighbourAvailable na; + +#define BOUNDARY_LEFT_SLICE (1 << 0) +#define BOUNDARY_LEFT_TILE (1 << 1) +#define BOUNDARY_UPPER_SLICE (1 << 2) +#define BOUNDARY_UPPER_TILE (1 << 3) + /* properties of the boundary of the current CTB for the purposes + * of the deblocking filter */ + int boundary_flags; + + SliceContext *sc; + VVCFrameContext *fc; + EntryPoint *ep; + int *coeffs; +} VVCLocalContext; + +typedef struct VVCAllowedSplit { + int qt; + int btv; + int bth; + int ttv; + int tth; +} VVCAllowedSplit; + +typedef struct SAOParams { + int offset_abs[3][4]; ///< sao_offset_abs + int offset_sign[3][4]; ///< sao_offset_sign + + uint8_t band_position[3]; ///< sao_band_position + + int eo_class[3]; ///< sao_eo_class + + int16_t offset_val[3][5]; /// X-Patchwork-Id: 44621 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:92a5:b0:181:818d:5e7f with SMTP id q37csp728076pzg; Sun, 12 Nov 2023 02:36:22 -0800 (PST) X-Google-Smtp-Source: AGHT+IFpYjT0HNmGwFs2wiHLcjVE4sOfZXSdKXm01kqG16bCcYegi/Sf9tIVAmfmutp04bC80I+s X-Received: by 2002:a17:906:793:b0:9bd:bbc1:1c5f with SMTP id l19-20020a170906079300b009bdbbc11c5fmr2677295ejc.35.1699785382418; Sun, 12 Nov 2023 02:36:22 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id jj1-20020a170907984100b009e0e6e2f720si1612011ejc.319.2023.11.12.02.36.21; Sun, 12 Nov 2023 02:36:22 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@outlook.com header.s=selector1 header.b=i6FYf2dq; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 6048568C9B0; Sun, 12 Nov 2023 12:36:13 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from APC01-PSA-obe.outbound.protection.outlook.com (mail-psaapc01olkn2055.outbound.protection.outlook.com [40.92.52.55]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id D771F68CA29 for ; Sun, 12 Nov 2023 12:36:05 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=LWdFJ2aQVjRWG0i9ZeyBKcmSVHklmR7y7bNL9afBGQovlwx95unUk4kHCa449JPISZLDhBtrkR5UH/6mWhWENOKgcKGO3s2MAoRBfKxwyk6kYy4oEKaWwqYkBcEGg2nfWot6AT305FBggKEdU8lZPCK4bznjgc50ZrOhrgjHgUvfw5j3WU9+TnyhqavSJnSZtP7vQLhuHRia/me6dyZ5DIxcUcbqhNDelLWsubfHUne4v/eYOpuCPAhTaD2MSvg1bxLYHJO8dtCAljsu3JeK4R3XoP8daRFQ59TXEKZcOr4JaqT3Di55O+sHpjivTPVtNXuld7/BFWrdD8JHRIPJjw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=IpI9IPdWFouLqs0xAK86sbppP6aqduu36WO9bxc2lXk=; b=e/IxoBH5fp8TzjKpHmw99HkHuzNvGqdqchQcUnAWl5YGYoqrqxfctIG38srfmKrl7IKkdBtMKPEwRktrnEpst3qVh77w9t0OdWSvo7iDYLhjCPHJA97joC8dv1I8+WowggypWc6kFbyplkMMko8HN8Sci1PpjegQyHEbRnsd9/5/PGKSbOBG90yd0sKqgK7jwfZMisCctv6U73xAT3XMgR9SyYck/wwlvk+T/bzMrl5XvEJXhH1+YljKHqD9bp5vepPkFw7H/7mx0NkOCRJEBR4bYgaXmV3tYEeiyJMIYbqEWJ3RKRE3qFmgFk1J9Qq4B3pROYSYlXRj44IXyjJtwA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=outlook.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=IpI9IPdWFouLqs0xAK86sbppP6aqduu36WO9bxc2lXk=; b=i6FYf2dqsT8L2Fp7hBmS1I2wiCLyuYt8gAjW6gnFIlpP/AVliLgTE1NSyBJ+20QbMwHXx0JEpeNo8pS65emgIV25gOjKp9iqUBfDIKxFc6tWABjeVhT87KeB5kC+M32mknwZ39536OCNo9SDtg6vmH8RDW73FOhctGwXEkoqttlUUkWwmEEu0ksSp5kbZMt2Z7CeXKr+OkmnX3fJe6PyJfutBVY6k8YPYUHm2d9ybtq2D2tGZY8m1GYfws2u6ibGEkgkSXaTfto/IkII7hpfAaDm+7D6pwgdb2mymTTmwF1H/03EuN8Kg2v3R0amoGFfHa/peIaIiWmnky3Uqr9kVw== Received: from TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) by TYSPR06MB6768.apcprd06.prod.outlook.com (2603:1096:400:475::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6954.29; Sun, 12 Nov 2023 10:35:57 +0000 Received: from TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f]) by TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f%4]) with mapi id 15.20.6954.028; Sun, 12 Nov 2023 10:35:57 +0000 From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Nov 2023 18:35:17 +0800 Message-ID: X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231112103526.11245-1-nuomi2021@gmail.com> References: <20231112103526.11245-1-nuomi2021@gmail.com> X-TMN: [d7N0zClFZ6uzlk4fwdsCeMbRZj1yDTY3] X-ClientProxiedBy: TYWP286CA0030.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:262::20) To TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) X-Microsoft-Original-Message-ID: <20231112103526.11245-5-nuomi2021@gmail.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 2 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: TYSPR06MB6433:EE_|TYSPR06MB6768:EE_ X-MS-Office365-Filtering-Correlation-Id: c14b8dd7-c097-43f7-2e37-08dbe36b27d1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: Pe2rvx7LggAOwsgFHJaRdte0svcoDssLyA+NrVcfwxqHJ4xYDOwQWm0zUedUPZrnB+id8v2CJGMryZasBaxYT1QJdNWayBk71vPXUohZ+3hVSNmSeA3EFOo7qRt424mz6ZBxgEsMHCjzZLU9I9smSZes/SZ8A7WRTr2U4AAQt0yyQOH0bUaFMbWk+zkL6YmAa1HGvEql3fb6ddjqDSn4ErKEof/9c/HT4WcWkdIHBJe8W0KSrEPWiUGkmAGoAQfxeOnRiso42VuHOZmGaoa8/ZwdKH4ghRWBbfE6muhINWW8tDP4NJXDuf5LDO3/AObEnVsexS0Wff5oL1h1nhLPj8R8holDCGbL0PW2sZ5DlyFNGodqS64Ltows2P/lj8sls2RoA3SQlywN7RLmvXkz1zYqaKiFvLkBovty8KadEs+Ntv0PX6tokn3L+AlC4Kj9OZyvYThJdd+knVMz/VtB9/Yir7n/wSKEdMAhL+S7rhft8pHaop4n0Cb/zx8yjv23linwLYy/P9P2IAPdaf1ojp99/ju33kAWQjEKxFcqFXnW207o7lQ5eFn4NoML1fERVzJ18y6dk5wAmdZQdOuj9Ue0b8U9a2cYkdkl9vibv3W70QSBaj/h3jX3FjDtU3s2 X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: yVCSgXmhWBFSDBEZgrU1jbigZGFpGcmDARC7fRHSYKtPsAzpZ4it96QDdNVVQ61SGI2d+xiaUV1cX4qZd845b/V+q7ONFBKE9qxjPT7mVzZViPCXxRs8s6FLsbe+bD3avxQ5YxC5lBYhRSvrvl3p58PINFy8x/y+E1WDFlrK5iwRblSFHuchI0+KLJlz8ynYGupxgybE4ZHX2g9g0ya+w6Vya+P+cRHFPcFKrZPRlUswNqHHXqoAXEyblU8KmNRmBLNT4/tbxb6DatvmIMqoV477cPBp6PnFe80AeXxgk7WCQ1F/owaC4tzm3PCPGstx2fPuVaci2q212CFU7jd/nlmAm0X1fNAp5ODBZ3DLPbRybwv0GyF6SZhb4KEYyvWvHFQW6LH9BlvPjyrgOSkeNpI+XD+/QBiDShU750QvS/aQHd1eCvWY8zSLjyv9opGr+IXzw2bWMDQxZjWoEzvL98sglPGrq+jQAxoho7KaTRfW94nqG5dpvnO6zW0Ow9G1cm6cv95SWl5a6kG9+SQNN7U+dcgJjJHGU41eBnE4LUvHEqaWPnbkZJOB4o6zVdYnURvUCiMGWH3+29f0Lp9LFxvs5bn2IuClAFhFXQY+57ekt24gzLKWOfnZyGp0K7jpkXTytE7KZcwCH5oyDEi6zhZx2r7cHTeTsWu59NFUFcEVHPR7inqyP/9TSa6gnsp9bnJ9hSHlBho0lLQ1VGUBF38l9pA3/OKAl+cKkOcfrUdFH1fas2Wnkex6NUktwMsMBPKpZvdmvufDBDp0dJROvtX1zDq/Zffv2gNPJoVOSdvewK8QWqufz4/k7UUjJ9h/VWFSasHcjB6x54uvosUnfylm/sagmRLAYgdRKR+itMirlzgFZq9w26HWFDhU4OKz12590oOAIN75nLNfVXj9g6hCvuHfwc0SVaKRw3L2lY2u25qUvGCEU8GG13m96+1r+yumMHZz5DuFP2GcVh+VLkOXaYqKMw3tzLYcQF4uUvEKNG1vMMvg7K32CBUGtjMmwJbh5OVvZkGcuypHABfocLCU16TKw7ve6nOGucUAj6N0CKroaAaWnCxPAWjHJ7oJ6M0s09XUhs7oLVGGBYHwFijNkSthgJWfVKLS7a2IDT5RRJDcl5V+K0Zj4p0tRL6lIgn4+aLYeeh7+xVbXlrVOXWf6x15FV3FRHYcr81kAyvuJI0WjZIUWmAhGFF4mUTdVTWnyhpjY8i81xod5iw/ZzfGb2BCQWWin7RTwjX88/QYQZoIuaS9RSKAVbc5N6CI X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: c14b8dd7-c097-43f7-2e37-08dbe36b27d1 X-MS-Exchange-CrossTenant-AuthSource: TYSPR06MB6433.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Nov 2023 10:35:57.4647 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYSPR06MB6768 Subject: [FFmpeg-devel] [PATCH v5 05/14] vvcdec: add reference management X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Nuo Mi Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: hU1DnQd1zSrQ --- libavcodec/vvc/Makefile | 1 + libavcodec/vvc/vvc_refs.c | 567 ++++++++++++++++++++++++++++++++++++++ libavcodec/vvc/vvc_refs.h | 57 ++++ 3 files changed, 625 insertions(+) create mode 100644 libavcodec/vvc/vvc_refs.c create mode 100644 libavcodec/vvc/vvc_refs.h diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile index 6c03ba19ac..1d89985d13 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -6,3 +6,4 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ vvc/vvc_ctu.o \ vvc/vvc_data.o \ vvc/vvc_ps.o \ + vvc/vvc_refs.o \ diff --git a/libavcodec/vvc/vvc_refs.c b/libavcodec/vvc/vvc_refs.c new file mode 100644 index 0000000000..5d2f92c877 --- /dev/null +++ b/libavcodec/vvc/vvc_refs.c @@ -0,0 +1,567 @@ +/* + * VVC reference management + * + * Copyright (C) 2023 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "libavutil/thread.h" +#include "libavcodec/refstruct.h" +#include "libavcodec/thread.h" + +#include "vvc_refs.h" + +#define VVC_FRAME_FLAG_OUTPUT (1 << 0) +#define VVC_FRAME_FLAG_SHORT_REF (1 << 1) +#define VVC_FRAME_FLAG_LONG_REF (1 << 2) +#define VVC_FRAME_FLAG_BUMPING (1 << 3) + +typedef struct FrameProgress { + atomic_int progress[VVC_PROGRESS_LAST]; + VVCProgressListener *listener[VVC_PROGRESS_LAST]; + AVMutex lock; + AVCond cond; + uint8_t has_lock; + uint8_t has_cond; +} FrameProgress; + +void ff_vvc_unref_frame(VVCFrameContext *fc, VVCFrame *frame, int flags) +{ + /* frame->frame can be NULL if context init failed */ + if (!frame->frame || !frame->frame->buf[0]) + return; + + frame->flags &= ~flags; + if (!frame->flags) { + av_frame_unref(frame->frame); + ff_refstruct_unref(&frame->progress); + + ff_refstruct_unref(&frame->tab_dmvr_mvf); + + ff_refstruct_unref(&frame->rpl); + frame->nb_rpl_elems = 0; + ff_refstruct_unref(&frame->rpl_tab); + + frame->collocated_ref = NULL; + } +} + +const RefPicList *ff_vvc_get_ref_list(const VVCFrameContext *fc, const VVCFrame *ref, int x0, int y0) +{ + int x_cb = x0 >> fc->ps.sps->ctb_log2_size_y; + int y_cb = y0 >> fc->ps.sps->ctb_log2_size_y; + int pic_width_cb = fc->ps.pps->ctb_width; + int ctb_addr_rs = y_cb * pic_width_cb + x_cb; + + return (const RefPicList *)ref->rpl_tab[ctb_addr_rs]; +} + +void ff_vvc_clear_refs(VVCFrameContext *fc) +{ + int i; + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) + ff_vvc_unref_frame(fc, &fc->DPB[i], + VVC_FRAME_FLAG_SHORT_REF | VVC_FRAME_FLAG_LONG_REF); +} + +static void free_progress(FFRefStructOpaque unused, void *obj) +{ + FrameProgress *p = (FrameProgress *)obj; + + if (p->has_cond) + ff_cond_destroy(&p->cond); + if (p->has_lock) + ff_mutex_destroy(&p->lock); +} + +static FrameProgress *alloc_progress(void) +{ + FrameProgress *p = ff_refstruct_alloc_ext(sizeof(*p), 0, NULL, free_progress); + + if (p) { + p->has_lock = !ff_mutex_init(&p->lock, NULL); + p->has_cond = !ff_cond_init(&p->cond, NULL); + if (!p->has_lock || !p->has_cond) + ff_refstruct_unref(&p); + } + return p; +} + +static VVCFrame *alloc_frame(VVCContext *s, VVCFrameContext *fc) +{ + const VVCPPS *pps = fc->ps.pps; + int i, j, ret; + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + if (frame->frame->buf[0]) + continue; + + ret = ff_thread_get_buffer(fc->avctx, frame->frame, + AV_GET_BUFFER_FLAG_REF); + if (ret < 0) + return NULL; + + frame->rpl = ff_refstruct_allocz(s->current_frame.nb_units * sizeof(RefPicListTab)); + if (!frame->rpl) + goto fail; + frame->nb_rpl_elems = s->current_frame.nb_units; + + frame->tab_dmvr_mvf = ff_refstruct_pool_get(fc->tab_dmvr_mvf_pool); + if (!frame->tab_dmvr_mvf) + goto fail; + + frame->rpl_tab = ff_refstruct_pool_get(fc->rpl_tab_pool); + if (!frame->rpl_tab) + goto fail; + frame->ctb_count = pps->ctb_width * pps->ctb_height; + for (j = 0; j < frame->ctb_count; j++) + frame->rpl_tab[j] = frame->rpl; + + frame->progress = alloc_progress(); + if (!frame->progress) + goto fail; + + return frame; +fail: + ff_vvc_unref_frame(fc, frame, ~0); + return NULL; + } + av_log(s->avctx, AV_LOG_ERROR, "Error allocating frame, DPB full.\n"); + return NULL; +} + +int ff_vvc_set_new_ref(VVCContext *s, VVCFrameContext *fc, AVFrame **frame) +{ + const VVCPH *ph= &fc->ps.ph; + const int poc = ph->poc; + VVCFrame *ref; + int i; + + /* check that this POC doesn't already exist */ + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + + if (frame->frame->buf[0] && frame->sequence == s->seq_decode && + frame->poc == poc) { + av_log(s->avctx, AV_LOG_ERROR, "Duplicate POC in a sequence: %d.\n", + poc); + return AVERROR_INVALIDDATA; + } + } + + ref = alloc_frame(s, fc); + if (!ref) + return AVERROR(ENOMEM); + + *frame = ref->frame; + fc->ref = ref; + + if (s->no_output_before_recovery_flag && (IS_RASL(s) || !GDR_IS_RECOVERED(s))) + ref->flags = 0; + else if (ph->r->ph_pic_output_flag) + ref->flags = VVC_FRAME_FLAG_OUTPUT; + + if (!ph->r->ph_non_ref_pic_flag) + ref->flags |= VVC_FRAME_FLAG_SHORT_REF; + + ref->poc = poc; + ref->sequence = s->seq_decode; + ref->frame->crop_left = fc->ps.pps->r->pps_conf_win_left_offset; + ref->frame->crop_right = fc->ps.pps->r->pps_conf_win_right_offset; + ref->frame->crop_top = fc->ps.pps->r->pps_conf_win_top_offset; + ref->frame->crop_bottom = fc->ps.pps->r->pps_conf_win_bottom_offset; + + return 0; +} + +int ff_vvc_output_frame(VVCContext *s, VVCFrameContext *fc, AVFrame *out, const int no_output_of_prior_pics_flag, int flush) +{ + const VVCSPS *sps = fc->ps.sps; + do { + int nb_output = 0; + int min_poc = INT_MAX; + int i, min_idx, ret; + + if (no_output_of_prior_pics_flag) { + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + if (!(frame->flags & VVC_FRAME_FLAG_BUMPING) && frame->poc != fc->ps.ph.poc && + frame->sequence == s->seq_output) { + ff_vvc_unref_frame(fc, frame, VVC_FRAME_FLAG_OUTPUT); + } + } + } + + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + if ((frame->flags & VVC_FRAME_FLAG_OUTPUT) && + frame->sequence == s->seq_output) { + nb_output++; + if (frame->poc < min_poc || nb_output == 1) { + min_poc = frame->poc; + min_idx = i; + } + } + } + + /* wait for more frames before output */ + if (!flush && s->seq_output == s->seq_decode && sps && + nb_output <= sps->r->sps_dpb_params.dpb_max_dec_pic_buffering_minus1[sps->r->sps_max_sublayers_minus1] + 1) + return 0; + + if (nb_output) { + VVCFrame *frame = &fc->DPB[min_idx]; + + ret = av_frame_ref(out, frame->frame); + if (frame->flags & VVC_FRAME_FLAG_BUMPING) + ff_vvc_unref_frame(fc, frame, VVC_FRAME_FLAG_OUTPUT | VVC_FRAME_FLAG_BUMPING); + else + ff_vvc_unref_frame(fc, frame, VVC_FRAME_FLAG_OUTPUT); + if (ret < 0) + return ret; + + av_log(s->avctx, AV_LOG_DEBUG, + "Output frame with POC %d.\n", frame->poc); + return 1; + } + + if (s->seq_output != s->seq_decode) + s->seq_output = (s->seq_output + 1) & 0xff; + else + break; + } while (1); + return 0; +} + +void ff_vvc_bump_frame(VVCContext *s, VVCFrameContext *fc) +{ + const VVCSPS *sps = fc->ps.sps; + const int poc = fc->ps.ph.poc; + int dpb = 0; + int min_poc = INT_MAX; + int i; + + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + if ((frame->flags) && + frame->sequence == s->seq_output && + frame->poc != poc) { + dpb++; + } + } + + if (sps && dpb >= sps->r->sps_dpb_params.dpb_max_dec_pic_buffering_minus1[sps->r->sps_max_sublayers_minus1] + 1) { + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + if ((frame->flags) && + frame->sequence == s->seq_output && + frame->poc != poc) { + if (frame->flags == VVC_FRAME_FLAG_OUTPUT && frame->poc < min_poc) { + min_poc = frame->poc; + } + } + } + + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + if (frame->flags & VVC_FRAME_FLAG_OUTPUT && + frame->sequence == s->seq_output && + frame->poc <= min_poc) { + frame->flags |= VVC_FRAME_FLAG_BUMPING; + } + } + + dpb--; + } +} + +static VVCFrame *find_ref_idx(VVCContext *s, VVCFrameContext *fc, int poc, uint8_t use_msb) +{ + int mask = use_msb ? ~0 : fc->ps.sps->max_pic_order_cnt_lsb - 1; + int i; + + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *ref = &fc->DPB[i]; + if (ref->frame->buf[0] && ref->sequence == s->seq_decode) { + if ((ref->poc & mask) == poc) + return ref; + } + } + return NULL; +} + +static void mark_ref(VVCFrame *frame, int flag) +{ + frame->flags &= ~(VVC_FRAME_FLAG_LONG_REF | VVC_FRAME_FLAG_SHORT_REF); + frame->flags |= flag; +} + +static VVCFrame *generate_missing_ref(VVCContext *s, VVCFrameContext *fc, int poc) +{ + const VVCSPS *sps = fc->ps.sps; + VVCFrame *frame; + int i, y; + + frame = alloc_frame(s, fc); + if (!frame) + return NULL; + + if (!s->avctx->hwaccel) { + if (!sps->pixel_shift) { + for (i = 0; frame->frame->buf[i]; i++) + memset(frame->frame->buf[i]->data, 1 << (sps->bit_depth - 1), + frame->frame->buf[i]->size); + } else { + for (i = 0; frame->frame->data[i]; i++) + for (y = 0; y < (sps->height >> sps->vshift[i]); y++) { + uint8_t *dst = frame->frame->data[i] + y * frame->frame->linesize[i]; + AV_WN16(dst, 1 << (sps->bit_depth - 1)); + av_memcpy_backptr(dst + 2, 2, 2*(sps->width >> sps->hshift[i]) - 2); + } + } + } + + frame->poc = poc; + frame->sequence = s->seq_decode; + frame->flags = 0; + + ff_vvc_report_frame_finished(frame); + + return frame; +} + +/* add a reference with the given poc to the list and mark it as used in DPB */ +static int add_candidate_ref(VVCContext *s, VVCFrameContext *fc, RefPicList *list, + int poc, int ref_flag, uint8_t use_msb) +{ + VVCFrame *ref = find_ref_idx(s, fc, poc, use_msb); + + if (ref == fc->ref || list->nb_refs >= VVC_MAX_REF_ENTRIES) + return AVERROR_INVALIDDATA; + + if (!ref) { + ref = generate_missing_ref(s, fc, poc); + if (!ref) + return AVERROR(ENOMEM); + } + + list->list[list->nb_refs] = poc; + list->ref[list->nb_refs] = ref; + list->isLongTerm[list->nb_refs] = ref_flag & VVC_FRAME_FLAG_LONG_REF; + list->nb_refs++; + + mark_ref(ref, ref_flag); + return 0; +} + +static int init_slice_rpl(const VVCFrameContext *fc, SliceContext *sc) +{ + VVCFrame *frame = fc->ref; + const VVCSH *sh = &sc->sh; + + if (sc->slice_idx >= frame->nb_rpl_elems) + return AVERROR_INVALIDDATA; + + for (int i = 0; i < sh->num_ctus_in_curr_slice; i++) { + const int rs = sh->ctb_addr_in_curr_slice[i]; + frame->rpl_tab[rs] = frame->rpl + sc->slice_idx; + } + + sc->rpl = frame->rpl_tab[sh->ctb_addr_in_curr_slice[0]]->refPicList; + + return 0; +} + +static int delta_poc_st(const H266RefPicListStruct *rpls, + const int lx, const int i, const VVCSPS *sps) +{ + int abs_delta_poc_st = rpls->abs_delta_poc_st[i]; + if (!((sps->r->sps_weighted_pred_flag || + sps->r->sps_weighted_bipred_flag) && i != 0)) + abs_delta_poc_st++; + return (1 - 2 * rpls->strp_entry_sign_flag[i]) * abs_delta_poc_st; +} + +static int poc_lt(int *prev_delta_poc_msb, const int poc, const H266RefPicLists *ref_lists, + const int lx, const int j, const int max_poc_lsb) +{ + const H266RefPicListStruct *rpls = ref_lists->rpl_ref_list + lx; + int lt_poc = rpls->ltrp_in_header_flag ? ref_lists->poc_lsb_lt[lx][j] : rpls->rpls_poc_lsb_lt[j]; + + if (ref_lists->delta_poc_msb_cycle_present_flag[lx][j]) { + const uint32_t delta = ref_lists->delta_poc_msb_cycle_lt[lx][j] + *prev_delta_poc_msb; + lt_poc += poc - delta * max_poc_lsb - (poc & (max_poc_lsb - 1)); + *prev_delta_poc_msb = delta; + } + return lt_poc; +} + +int ff_vvc_slice_rpl(VVCContext *s, VVCFrameContext *fc, SliceContext *sc) +{ + const VVCSPS *sps = fc->ps.sps; + const H266RawPPS *pps = fc->ps.pps->r; + const VVCPH *ph = &fc->ps.ph; + const H266RawSliceHeader *rsh = sc->sh.r; + const int max_poc_lsb = sps->max_pic_order_cnt_lsb; + const H266RefPicLists *ref_lists = + pps->pps_rpl_info_in_ph_flag ? &ph->r->ph_ref_pic_lists : &rsh->sh_ref_pic_lists; + int ret = 0; + + ret = init_slice_rpl(fc, sc); + if (ret < 0) + return ret; + + for (int lx = L0; lx <= L1; lx++) { + const H266RefPicListStruct *rpls = ref_lists->rpl_ref_list + lx; + RefPicList *rpl = sc->rpl + lx; + int poc_base = ph->poc; + int prev_delta_poc_msb = 0; + + rpl->nb_refs = 0; + for (int i = 0, j = 0; i < rpls->num_ref_entries; i++) { + int poc; + if (!rpls->inter_layer_ref_pic_flag[i]) { + int use_msb = 1; + int ref_flag; + if (rpls->st_ref_pic_flag[i]) { + poc = poc_base + delta_poc_st(rpls, lx, i, sps); + poc_base = poc; + ref_flag = VVC_FRAME_FLAG_SHORT_REF; + } else { + use_msb = ref_lists->delta_poc_msb_cycle_present_flag[lx][j]; + poc = poc_lt(&prev_delta_poc_msb, ph->poc, ref_lists, lx, j, max_poc_lsb); + ref_flag = VVC_FRAME_FLAG_LONG_REF; + j++; + } + ret = add_candidate_ref(s, fc, rpl, poc, ref_flag, use_msb); + if (ret < 0) + goto fail; + } else { + avpriv_request_sample(fc->avctx, "Inter layer ref"); + ret = AVERROR_PATCHWELCOME; + goto fail; + } + } + if ((!rsh->sh_collocated_from_l0_flag) == lx && + rsh->sh_collocated_ref_idx < rpl->nb_refs) + fc->ref->collocated_ref = rpl->ref[rsh->sh_collocated_ref_idx]; + } +fail: + return ret; +} + +int ff_vvc_frame_rpl(VVCContext *s, VVCFrameContext *fc, SliceContext *sc) +{ + int i, ret = 0; + + /* clear the reference flags on all frames except the current one */ + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + + if (frame == fc->ref) + continue; + + mark_ref(frame, 0); + } + + if ((ret = ff_vvc_slice_rpl(s, fc, sc)) < 0) + goto fail; + +fail: + /* release any frames that are now unused */ + for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) + ff_vvc_unref_frame(fc, &fc->DPB[i], 0); + return ret; +} + +void ff_vvc_report_frame_finished(VVCFrame *frame) +{ + ff_vvc_report_progress(frame, VVC_PROGRESS_MV, INT_MAX); + ff_vvc_report_progress(frame, VVC_PROGRESS_PIXEL, INT_MAX); +} + +static int is_progress_done(const FrameProgress *p, const VVCProgressListener *l) +{ + return p->progress[l->vp] > l->y; +} + +static void add_listener(VVCProgressListener **prev, VVCProgressListener *l) +{ + l->next = *prev; + *prev = l; +} + +static VVCProgressListener* remove_listener(VVCProgressListener **prev, VVCProgressListener *l) +{ + *prev = l->next; + l->next = NULL; + return l; +} + +static VVCProgressListener* get_done_listener(FrameProgress *p, const VVCProgress vp) +{ + VVCProgressListener *list = NULL; + VVCProgressListener **prev = &p->listener[vp]; + + while (*prev) { + if (is_progress_done(p, *prev)) { + VVCProgressListener *l = remove_listener(prev, *prev); + add_listener(&list, l); + } else { + prev = &(*prev)->next; + } + } + return list; +} + +void ff_vvc_report_progress(VVCFrame *frame, const VVCProgress vp, const int y) +{ + FrameProgress *p = frame->progress; + VVCProgressListener *l = NULL; + + ff_mutex_lock(&p->lock); + + av_assert0(p->progress[vp] < y || p->progress[vp] == INT_MAX); + p->progress[vp] = y; + l = get_done_listener(p, vp); + ff_cond_signal(&p->cond); + + ff_mutex_unlock(&p->lock); + + while (l) { + l->progress_done(l); + l = l->next; + } +} + +void ff_vvc_add_progress_listener(VVCFrame *frame, VVCProgressListener *l) +{ + FrameProgress *p = frame->progress; + + ff_mutex_lock(&p->lock); + + if (is_progress_done(p, l)) { + ff_mutex_unlock(&p->lock); + l->progress_done(l); + } else { + add_listener(p->listener + l->vp, l); + ff_mutex_unlock(&p->lock); + } +} diff --git a/libavcodec/vvc/vvc_refs.h b/libavcodec/vvc/vvc_refs.h new file mode 100644 index 0000000000..4eff6eea04 --- /dev/null +++ b/libavcodec/vvc/vvc_refs.h @@ -0,0 +1,57 @@ +/* + * VVC reference management + * + * Copyright (C) 2023 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VVC_REFS_H +#define AVCODEC_VVC_REFS_H + +#include "vvcdec.h" + +int ff_vvc_output_frame(VVCContext *s, VVCFrameContext *fc, struct AVFrame *out, int no_output_of_prior_pics_flag, int flush); +void ff_vvc_bump_frame(VVCContext *s, VVCFrameContext *fc); +int ff_vvc_set_new_ref(VVCContext *s, VVCFrameContext *fc, struct AVFrame **frame); +const RefPicList *ff_vvc_get_ref_list(const VVCFrameContext *fc, const VVCFrame *ref, int x0, int y0); +int ff_vvc_frame_rpl(VVCContext *s, VVCFrameContext *fc, SliceContext *sc); +int ff_vvc_slice_rpl(VVCContext *s, VVCFrameContext *fc, SliceContext *sc); +void ff_vvc_unref_frame(VVCFrameContext *fc, VVCFrame *frame, int flags); +void ff_vvc_clear_refs(VVCFrameContext *fc); + +typedef enum VVCProgress { + VVC_PROGRESS_MV, + VVC_PROGRESS_PIXEL, + VVC_PROGRESS_LAST, +} VVCProgress; + +typedef struct VVCProgressListener VVCProgressListener; +typedef void (*progress_done_fn)(VVCProgressListener *l); + +struct VVCProgressListener { + VVCProgress vp; + int y; + progress_done_fn progress_done; + VVCProgressListener *next; //used by ff_vvc_add_progress_listener only +}; + +void ff_vvc_report_frame_finished(VVCFrame *frame); +void ff_vvc_report_progress(VVCFrame *frame, VVCProgress vp, int y); +void ff_vvc_add_progress_listener(VVCFrame *frame, VVCProgressListener *l); + +#endif // AVCODEC_VVC_REFS_H From patchwork Sun Nov 12 10:35:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 44623 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:92a5:b0:181:818d:5e7f with SMTP id q37csp728191pzg; Sun, 12 Nov 2023 02:36:46 -0800 (PST) X-Google-Smtp-Source: AGHT+IEFrTTi80MEEhdIrJC7IktWGjXecwo2en8rtaXXay2P3PP4i4lkO/Q2LhInsegKN+mKkKnj X-Received: by 2002:a17:906:4b4d:b0:9bf:70ea:6926 with SMTP id j13-20020a1709064b4d00b009bf70ea6926mr2729480ejv.2.1699785406238; Sun, 12 Nov 2023 02:36:46 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id um25-20020a170906cf9900b009e6252bf26bsi1572225ejb.718.2023.11.12.02.36.45; Sun, 12 Nov 2023 02:36:46 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@outlook.com header.s=selector1 header.b=R1sQi1cx; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id C2F9A68CC58; Sun, 12 Nov 2023 12:36:18 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from APC01-PSA-obe.outbound.protection.outlook.com (mail-psaapc01olkn2055.outbound.protection.outlook.com [40.92.52.55]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 80C1868CC09 for ; Sun, 12 Nov 2023 12:36:11 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=FMXFQfXXz9xmf1tA9wxdMei5boDveRaehmsDHHb/c9rW90cxr8uHWzUD8bDtEdt8f4Zx1rXcu3UipIlXtb1BEa5vgwrcHJxhf/9q7oIwmPoPv9ovMBfkZmrd7pBqhnO7qN7izyAFAlDRQXlwptxWhr6wKgI9xCrKcn3M6gg3YJeIbH8SFdzyza0TSEmovehohIwYgnwu+VZTPwQVyno/h4T4hdsFD65VocDTZPVcCJL+fByMkcUVWbMJCj40fkSIZHN2XTM4/RSnoAuHy4MOr/TkDdKY1CkdaATmgzQIz8sqUr+BCsCKjCN9L24pQgCWQ/NaAKuwcF+AX3s4rjI5Ug== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=pkSOQc3V7i6ZygR7CvAu2iTEMcxQgkhSHV5VAUEQYR8=; b=KiorQ/W5HwFcG0iXFHMAwTTAE+cjluwq/mXyODzQFT6DsGCguo3ETRPgMxgjhDMti1WJnGn82q6d4Q4B5R8QwUlq66TLvPV84Yq+bn+iCWPV53erxTufh2EmvueTSyf8ASQLS0cOFmrhFEchcIj5cxqMl8uOLgBpo9CFhapd6KEUenKHYEp/5nobP0a0cX4etEEyUQmMB5hWffNCFyG+KxA8KZJLpv9D/aUSgzYWCJVhc4PJk/EmEQDFuhR04HrsbsUEeNaA7KoeoijBS8froH/nIqCrYC+Y3bwlUi7W3U9hBQY+CCUZf9T2TN/QKlYoih3qkJfJ/b9hnR+mhEKyDw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=outlook.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=pkSOQc3V7i6ZygR7CvAu2iTEMcxQgkhSHV5VAUEQYR8=; b=R1sQi1cxBROXJRfp3od+Wbnor5z9tKfDTZbEdod4CeWUTh8om+yJ9VTyJTtHuCSUj0PRmzaYS6jF8zkGyUzmK69+KWVTT7RGc1u/RagypSvVlBkpJ7mi5ENexNFiz7zRUyqdGO12rzUSygB5tU1WwP34odjw1WQSmSWnG8VviubnsaQxXEOdZ52BnuiJCNtv51lJrllzDOhUDd/7wg23WATIAIoDb5qGs3iV8h2F7XcF33lmUs3vTF60gGJXjYRLbn8ttpCRvHjW6NYsBp4xzUA4V6MXJtlQKspKT7M/oKgssxqojZZjoYEE/CU7zYJSgtJabMDif8BsFBxdTgFJeA== Received: from TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) by TYSPR06MB6768.apcprd06.prod.outlook.com (2603:1096:400:475::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6954.29; Sun, 12 Nov 2023 10:35:58 +0000 Received: from TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f]) by TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f%4]) with mapi id 15.20.6954.028; Sun, 12 Nov 2023 10:35:58 +0000 From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Nov 2023 18:35:18 +0800 Message-ID: X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231112103526.11245-1-nuomi2021@gmail.com> References: <20231112103526.11245-1-nuomi2021@gmail.com> X-TMN: [wbndZEWEbYJFOd1yQf3Dfy+jWS+zi5Fc] X-ClientProxiedBy: TYWP286CA0030.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:262::20) To TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) X-Microsoft-Original-Message-ID: <20231112103526.11245-6-nuomi2021@gmail.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 2 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: TYSPR06MB6433:EE_|TYSPR06MB6768:EE_ X-MS-Office365-Filtering-Correlation-Id: 6ba3f9f2-b6dc-4b8c-400f-08dbe36b2841 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: QdqD0zjK9cL/R7F3GFPEhb+b5mSqJowOX4Qr/PoJMWZPvxMzfgQYILdjlfCw+5a2+BBlIHDfP7/bDi42ioqaC7mS/IGLdkVtjFo4KxcqVhdiCgNhWMi+fjZazlofdWU1SRU7ihMl1Q7v8ZkdbYd7bSrNYHwV+/cuSIDFxTgJzIX22JunaHQ7C/FVxp6yWkCiW+u9+EIt30XKz2cL1o2NigVecElLMwaLznWUl0dIctcyvuLUntCswpydguw68gtdP45WvhkWfV1nyNRjEuFRNrpCDcLWRFGDjNPil9Wfod3GJTlLSCp1Tjq0++HoGhBgYnVMMMX7Za/u6ETRVbZvGvdp8AxNQOwgKRHz/TYPumjoQN+ridcFLsZPzh5NH8LdxVuIgB9UHH/qL1kLUTYYfG+am0TiNP7RDalcweW/1THSJIhAlWs2KVA1N7iMzZ1P85xQmPrHd3W45wYQPKn0H+GYt8yP9GEM7y2vaT4FW8ls6qNqtBs5ZOqO+PQPx8q96AWWIya4ZbR5Qw/CtyY792yypvFI3cr0U48ngZU7E61PDjRwIvrVtWs5FBLOV73Ptrwl4o1nr4OfwYkoxjXMEyXRMcciZiXYd+JSUqIxZV/SbqYbl2y9NLCEAyFr5kVx X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: TWpp/cn11ren/erwbXxmPMhj3DvP6U2K68T76q1mLLWGYdkDSGVSBDt94JdbAa4j+CBebBZOLdW4bPdq7hLAgFv3XMxvzHsslHxMUpiq7v5UCJPew5UpbibSkb/qxnRNERIYQmiAOdwmhrRoeKfQKO/DBw4So9BwQJpDGJ8mOid0or9Qizv43NUGFvodSs+cCVi8h1101QJjy5OHmREOZlwR8+CYgthBOK3jGMeGnb9RN8pnCYWTJ7LvVsPm/cuASghBsnQoZc3zOB/XWYOOjjvGEkRRr6j7oC1h8aTmmDOyNsZ7L9SEACe6VMUMhOvSfrtG6XSosJVAq8UgZStFdxd1O64shIX7YJK8N+368kfxKAtIuUBRliQSiSW0EMuZk3BQqe0Mie7MWYXb1hSeY90jBkMPFNzus/jpV/zrRPy9FWyaFmFFcLDHcZ5nYgev2Fzy/qu7a5HFLzR2Z1aUXeOM47n/ilmh6SCmgHilL/g5BDtwxl6o6bZjhbJ8YXu+HOeUvuRJsGdM1AFvBAX9UR5chMdObYiqizXVQ+V41tKsaqOh8O4WCm8A+RyoY5n70eHFz2OzwS8GOtEBBDW0Hg24tIu7HjPy7XoN58IWRZYyUlNroOU8/EoWVO/PeMYuufK/qcDxbd41kiJ6yePRJnb/OP7jc/zzNXoMXYO0nwU5/5pS7+bJFFRQ5G5Wj1i+0TFOUSeNal/+oLDuYs0JiyEc2lwsS6X5baPy4yZTQdRoB2p+iRdSJZrmUor7Liw6lH9RHO+uSX+O+fvpvGw/ZT0dlWJyM2yrPIzKkb18XMsH+cb7f5k8LdZ41Y7hEKuGQTtqx7t2yJMt4/akis2FxFjHe9lyPx759DrV9MZ1uUqXqG/J6NCFMUvHuCZNAtgEB61zvFGl0lqu8QtxdA2ce7n1hQ2M6GuLB9mE+Tkj5V9WN6/PCH+HzeOEdcV4UoX76UDFKPT7rhPqTzG2hJ5Rn++vVULyVxvV7J11zaOWEKjJbRzLVm56BJ09S8nVVLwWcRTujDx0iXpgxxoMKGUYSs4mcIKyObhhFp3r+FonWBngBlKM9sAqeiqVClpTDJbGNe9r58eOe6AQuSeq0AEizZ+SiE8M4Sid15NStF+9i3lJGcboBG5LnXG//+9qBLkyvfPWjH5uYR9ZF2eJSgNTxvh41eIpwFy2X7be+xVZl3B15DQKVnges9oru5GPmbIU6fKvjkM56PZg8FolaOZhoYlituWG5vWizokX/uMIoy5hmcHCMQGPAYmHi6DVF1BY X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 6ba3f9f2-b6dc-4b8c-400f-08dbe36b2841 X-MS-Exchange-CrossTenant-AuthSource: TYSPR06MB6433.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Nov 2023 10:35:58.2509 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYSPR06MB6768 Subject: [FFmpeg-devel] [PATCH v5 06/14] vvcdec: add motion vector decoder X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Nuo Mi Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 9GxSIQrTpJWl --- libavcodec/vvc/Makefile | 1 + libavcodec/vvc/vvc_ctu.c | 18 + libavcodec/vvc/vvc_ctu.h | 2 + libavcodec/vvc/vvc_mvs.c | 1799 ++++++++++++++++++++++++++++++++++++++ libavcodec/vvc/vvc_mvs.h | 46 + 5 files changed, 1866 insertions(+) create mode 100644 libavcodec/vvc/vvc_mvs.c create mode 100644 libavcodec/vvc/vvc_mvs.h diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile index 1d89985d13..912e9f516c 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -5,5 +5,6 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ vvc/vvc_cabac.o \ vvc/vvc_ctu.o \ vvc/vvc_data.o \ + vvc/vvc_mvs.o \ vvc/vvc_ps.o \ vvc/vvc_refs.o \ diff --git a/libavcodec/vvc/vvc_ctu.c b/libavcodec/vvc/vvc_ctu.c index 78b13ffb00..3d31b862ae 100644 --- a/libavcodec/vvc/vvc_ctu.c +++ b/libavcodec/vvc/vvc_ctu.c @@ -20,7 +20,25 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "vvc_cabac.h" #include "vvc_ctu.h" +#include "vvc_mvs.h" + +void ff_vvc_set_neighbour_available(VVCLocalContext *lc, + const int x0, const int y0, const int w, const int h) +{ + const int log2_ctb_size = lc->fc->ps.sps->ctb_log2_size_y; + const int x0b = av_mod_uintp2(x0, log2_ctb_size); + const int y0b = av_mod_uintp2(y0, log2_ctb_size); + + lc->na.cand_up = (lc->ctb_up_flag || y0b); + lc->na.cand_left = (lc->ctb_left_flag || x0b); + lc->na.cand_up_left = (x0b || y0b) ? lc->na.cand_left && lc->na.cand_up : lc->ctb_up_left_flag; + lc->na.cand_up_right_sap = + (x0b + w == 1 << log2_ctb_size) ? lc->ctb_up_right_flag && !y0b : lc->na.cand_up; + lc->na.cand_up_right = lc->na.cand_up_right_sap && (x0 + w) < lc->end_of_tiles_x; +} + void ff_vvc_ep_init_stat_coeff(EntryPoint *ep, const int bit_depth, const int persistent_rice_adaptation_enabled_flag) diff --git a/libavcodec/vvc/vvc_ctu.h b/libavcodec/vvc/vvc_ctu.h index eb917dcb55..37da5f0df4 100644 --- a/libavcodec/vvc/vvc_ctu.h +++ b/libavcodec/vvc/vvc_ctu.h @@ -458,6 +458,8 @@ typedef struct ALFParams { uint8_t applied[3]; } ALFParams; +//utils +void ff_vvc_set_neighbour_available(VVCLocalContext *lc, int x0, int y0, int w, int h); void ff_vvc_ep_init_stat_coeff(EntryPoint *ep, int bit_depth, int persistent_rice_adaptation_enabled_flag); #endif // AVCODEC_VVC_CTU_H diff --git a/libavcodec/vvc/vvc_mvs.c b/libavcodec/vvc/vvc_mvs.c new file mode 100644 index 0000000000..57a50f5112 --- /dev/null +++ b/libavcodec/vvc/vvc_mvs.c @@ -0,0 +1,1799 @@ +/* + * VVC motion vector decoder + * + * Copyright (C) 2023 Nuo Mi + * Copyright (C) 2022 Xu Mu + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "vvc_ctu.h" +#include "vvc_data.h" +#include "vvc_refs.h" +#include "vvc_mvs.h" + +#define IS_SAME_MV(a, b) (AV_RN64A(a) == AV_RN64A(b)) + +//check if the two luma locations belong to the same motion estimation region +static av_always_inline int is_same_mer(const VVCFrameContext *fc, const int xN, const int yN, const int xP, const int yP) +{ + const uint8_t plevel = fc->ps.sps->log2_parallel_merge_level; + + return xN >> plevel == xP >> plevel && + yN >> plevel == yP >> plevel; +} + +//return true if we have same mvs and ref_idxs +static av_always_inline int compare_mv_ref_idx(const MvField *n, const MvField *o) +{ + if (!o || n->pred_flag != o->pred_flag) + return 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (n->pred_flag & mask) { + const int same_ref_idx = n->ref_idx[i] == o->ref_idx[i]; + const int same_mv = IS_SAME_MV(n->mv + i, o->mv + i); + if (!same_ref_idx || !same_mv) + return 0; + } + } + return 1; +} + +// 8.5.2.15 Temporal motion buffer compression process for collocated motion vectors +static av_always_inline void mv_compression(Mv *motion) +{ + int mv[2] = {motion->x, motion->y}; + for (int i = 0; i < 2; i++) { + const int s = mv[i] >> 17; + const int f = av_log2((mv[i] ^ s) | 31) - 4; + const int mask = (-1 << f) >> 1; + const int round = (1 << f) >> 2; + mv[i] = (mv[i] + round) & mask; + } + motion->x = mv[0]; + motion->y = mv[1]; +} + +void ff_vvc_mv_scale(Mv *dst, const Mv *src, int td, int tb) +{ + int tx, scale_factor; + + td = av_clip_int8(td); + tb = av_clip_int8(tb); + tx = (0x4000 + (abs(td) >> 1)) / td; + scale_factor = av_clip_intp2((tb * tx + 32) >> 6, 12); + dst->x = av_clip_intp2((scale_factor * src->x + 127 + + (scale_factor * src->x < 0)) >> 8, 17); + dst->y = av_clip_intp2((scale_factor * src->y + 127 + + (scale_factor * src->y < 0)) >> 8, 17); +} + +//part of 8.5.2.12 Derivation process for collocated motion vectors +static int check_mvset(Mv *mvLXCol, Mv *mvCol, + int colPic, int poc, + const RefPicList *refPicList, int X, int refIdxLx, + const RefPicList *refPicList_col, int listCol, int refidxCol) +{ + int cur_lt = refPicList[X].isLongTerm[refIdxLx]; + int col_lt = refPicList_col[listCol].isLongTerm[refidxCol]; + int col_poc_diff, cur_poc_diff; + + if (cur_lt != col_lt) { + mvLXCol->x = 0; + mvLXCol->y = 0; + return 0; + } + + col_poc_diff = colPic - refPicList_col[listCol].list[refidxCol]; + cur_poc_diff = poc - refPicList[X].list[refIdxLx]; + + mv_compression(mvCol); + if (cur_lt || col_poc_diff == cur_poc_diff) { + mvLXCol->x = av_clip_intp2(mvCol->x, 17); + mvLXCol->y = av_clip_intp2(mvCol->y, 17); + } else { + ff_vvc_mv_scale(mvLXCol, mvCol, col_poc_diff, cur_poc_diff); + } + return 1; +} + +#define CHECK_MVSET(l) \ + check_mvset(mvLXCol, temp_col.mv + l, \ + colPic, fc->ps.ph.poc, \ + refPicList, X, refIdxLx, \ + refPicList_col, L ## l, temp_col.ref_idx[l]) + +//derive NoBackwardPredFlag +int ff_vvc_no_backward_pred_flag(const VVCLocalContext *lc) +{ + int check_diffpicount = 0; + int i, j; + const RefPicList *rpl = lc->sc->rpl; + + for (j = 0; j < 2; j++) { + for (i = 0; i < rpl[j].nb_refs; i++) { + if (rpl[j].list[i] > lc->fc->ps.ph.poc) { + check_diffpicount++; + break; + } + } + } + return !check_diffpicount; +} + +//8.5.2.12 Derivation process for collocated motion vectors +static int derive_temporal_colocated_mvs(const VVCLocalContext *lc, MvField temp_col, + int refIdxLx, Mv *mvLXCol, int X, + int colPic, const RefPicList *refPicList_col, int sb_flag) +{ + const VVCFrameContext *fc = lc->fc; + const SliceContext *sc = lc->sc; + RefPicList* refPicList = sc->rpl; + + if (temp_col.pred_flag == PF_INTRA) + return 0; + + if (sb_flag){ + if (X == 0) { + if (temp_col.pred_flag & PF_L0) + return CHECK_MVSET(0); + else if (ff_vvc_no_backward_pred_flag(lc) && (temp_col.pred_flag & PF_L1)) + return CHECK_MVSET(1); + } else { + if (temp_col.pred_flag & PF_L1) + return CHECK_MVSET(1); + else if (ff_vvc_no_backward_pred_flag(lc) && (temp_col.pred_flag & PF_L0)) + return CHECK_MVSET(0); + } + } else { + if (!(temp_col.pred_flag & PF_L0)) + return CHECK_MVSET(1); + else if (temp_col.pred_flag == PF_L0) + return CHECK_MVSET(0); + else if (temp_col.pred_flag == PF_BI) { + if (ff_vvc_no_backward_pred_flag(lc)) { + if (X == 0) + return CHECK_MVSET(0); + else + return CHECK_MVSET(1); + } else { + if (!lc->sc->sh.r->sh_collocated_from_l0_flag) + return CHECK_MVSET(0); + else + return CHECK_MVSET(1); + } + } + } + return 0; +} + +#define TAB_MVF(x, y) \ + tab_mvf[((y) >> MIN_PU_LOG2) * min_pu_width + ((x) >> MIN_PU_LOG2)] + +#define TAB_MVF_PU(v) \ + TAB_MVF(x ## v, y ## v) + +#define TAB_CP_MV(lx, x, y) \ + fc->tab.cp_mv[lx][((((y) >> min_cb_log2_size) * min_cb_width + ((x) >> min_cb_log2_size)) ) * MAX_CONTROL_POINTS] + + +#define DERIVE_TEMPORAL_COLOCATED_MVS(sb_flag) \ + derive_temporal_colocated_mvs(lc, temp_col, \ + refIdxLx, mvLXCol, X, colPic, \ + ff_vvc_get_ref_list(fc, ref, x, y), sb_flag) + +//8.5.2.11 Derivation process for temporal luma motion vector prediction +static int temporal_luma_motion_vector(const VVCLocalContext *lc, + const int refIdxLx, Mv *mvLXCol, const int X, int check_center, int sb_flag) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const CodingUnit *cu = lc->cu; + int x, y, colPic, availableFlagLXCol = 0; + int min_pu_width = fc->ps.pps->min_pu_width; + VVCFrame *ref = fc->ref->collocated_ref; + MvField *tab_mvf; + MvField temp_col; + + if (!ref) { + memset(mvLXCol, 0, sizeof(*mvLXCol)); + return 0; + } + + if (!fc->ps.ph.r->ph_temporal_mvp_enabled_flag || (cu->cb_width * cu->cb_height <= 32)) + return 0; + + tab_mvf = ref->tab_dmvr_mvf; + colPic = ref->poc; + + //bottom right collocated motion vector + x = cu->x0 + cu->cb_width; + y = cu->y0 + cu->cb_height; + + if (tab_mvf && + (cu->y0 >> sps->ctb_log2_size_y) == (y >> sps->ctb_log2_size_y) && + y < fc->ps.sps->height && + x < fc->ps.sps->width) { + x &= ~7; + y &= ~7; + temp_col = TAB_MVF(x, y); + availableFlagLXCol = DERIVE_TEMPORAL_COLOCATED_MVS(sb_flag); + } + if (check_center) { + // derive center collocated motion vector + if (tab_mvf && !availableFlagLXCol) { + x = cu->x0 + (cu->cb_width >> 1); + y = cu->y0 + (cu->cb_height >> 1); + x &= ~7; + y &= ~7; + temp_col = TAB_MVF(x, y); + availableFlagLXCol = DERIVE_TEMPORAL_COLOCATED_MVS(sb_flag); + } + } + return availableFlagLXCol; +} + +void ff_vvc_set_mvf(const VVCLocalContext *lc, const int x0, const int y0, const int w, const int h, const MvField *mvf) +{ + const VVCFrameContext *fc = lc->fc; + MvField *tab_mvf = fc->tab.mvf; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int min_pu_size = 1 << MIN_PU_LOG2; + for (int dy = 0; dy < h; dy += min_pu_size) { + for (int dx = 0; dx < w; dx += min_pu_size) { + const int x = x0 + dx; + const int y = y0 + dy; + TAB_MVF(x, y) = *mvf; + } + } +} + +void ff_vvc_set_intra_mvf(const VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + MvField *tab_mvf = fc->tab.mvf; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int min_pu_size = 1 << MIN_PU_LOG2; + for (int dy = 0; dy < cu->cb_height; dy += min_pu_size) { + for (int dx = 0; dx < cu->cb_width; dx += min_pu_size) { + const int x = cu->x0 + dx; + const int y = cu->y0 + dy; + TAB_MVF(x, y).pred_flag = PF_INTRA; + } + } +} + +//cbProfFlagLX from 8.5.5.9 Derivation process for motion vector arrays from affine control point motion vectors +static int derive_cb_prof_flag_lx(const VVCLocalContext *lc, const PredictionUnit* pu, int lx, int is_fallback) +{ + const MotionInfo* mi = &pu->mi; + const Mv* cp_mv = &mi->mv[lx][0]; + if (lc->fc->ps.ph.r->ph_prof_disabled_flag || is_fallback) + return 0; + if (mi->motion_model_idc == MOTION_4_PARAMS_AFFINE) { + if (IS_SAME_MV(cp_mv, cp_mv + 1)) + return 0; + } + if (mi->motion_model_idc == MOTION_6_PARAMS_AFFINE) { + if (IS_SAME_MV(cp_mv, cp_mv + 1) && IS_SAME_MV(cp_mv, cp_mv + 2)) + return 0; + } + //fixme: RprConstraintsActiveFlag + return 1; +} + +typedef struct SubblockParams { + int d_hor_x; + int d_ver_x; + int d_hor_y; + int d_ver_y; + int mv_scale_hor; + int mv_scale_ver; + int is_fallback; + + int cb_width; + int cb_height; +} SubblockParams; + +static int is_fallback_mode(const SubblockParams *sp, const PredFlag pred_flag) +{ + const int a = 4 * (2048 + sp->d_hor_x); + const int b = 4 * sp->d_hor_y; + const int c = 4 * (2048 + sp->d_ver_y); + const int d = 4 * sp->d_ver_x; + if (pred_flag == PF_BI) { + const int max_w4 = FFMAX(0, FFMAX(a, FFMAX(b, a + b))); + const int min_w4 = FFMIN(0, FFMIN(a, FFMIN(b, a + b))); + const int max_h4 = FFMAX(0, FFMAX(c, FFMAX(d, c + d))); + const int min_h4 = FFMIN(0, FFMIN(c, FFMIN(d, c + d))); + const int bx_wx4 = ((max_w4 - min_w4) >> 11) + 9; + const int bx_hx4 = ((max_h4 - min_h4) >> 11) + 9; + return bx_wx4 * bx_hx4 > 225; + } else { + const int bx_wxh = (FFABS(a) >> 11) + 9; + const int bx_hxh = (FFABS(d) >> 11) + 9; + const int bx_wxv = (FFABS(b) >> 11) + 9; + const int bx_hxv = (FFABS(c) >> 11) + 9; + if (bx_wxh * bx_hxh <= 165 && bx_wxv * bx_hxv <= 165) + return 0; + } + return 1; +} + +static void init_subblock_params(SubblockParams *sp, const MotionInfo* mi, + const int cb_width, const int cb_height, const int lx) +{ + const int log2_cbw = av_log2(cb_width); + const int log2_cbh = av_log2(cb_height); + const Mv* cp_mv = mi->mv[lx]; + const int num_cp_mv = mi->motion_model_idc + 1; + sp->d_hor_x = (cp_mv[1].x - cp_mv[0].x) << (MAX_CU_DEPTH - log2_cbw); + sp->d_ver_x = (cp_mv[1].y - cp_mv[0].y) << (MAX_CU_DEPTH - log2_cbw); + if (num_cp_mv == 3) { + sp->d_hor_y = (cp_mv[2].x - cp_mv[0].x) << (MAX_CU_DEPTH - log2_cbh); + sp->d_ver_y = (cp_mv[2].y - cp_mv[0].y) << (MAX_CU_DEPTH - log2_cbh); + } else { + sp->d_hor_y = -sp->d_ver_x; + sp->d_ver_y = sp->d_hor_x; + } + sp->mv_scale_hor = (cp_mv[0].x) << MAX_CU_DEPTH; + sp->mv_scale_ver = (cp_mv[0].y) << MAX_CU_DEPTH; + sp->cb_width = cb_width; + sp->cb_height = cb_height; + sp->is_fallback = is_fallback_mode(sp, mi->pred_flag); +} + +static void derive_subblock_diff_mvs(const VVCLocalContext *lc, PredictionUnit* pu, const SubblockParams* sp, const int lx) +{ + pu->cb_prof_flag[lx] = derive_cb_prof_flag_lx(lc, pu, lx, sp->is_fallback); + if (pu->cb_prof_flag[lx]) { + const int dmv_limit = 1 << 5; + const int pos_offset_x = 6 * (sp->d_hor_x + sp->d_hor_y); + const int pos_offset_y = 6 * (sp->d_ver_x + sp->d_ver_y); + for (int x = 0; x < AFFINE_MIN_BLOCK_SIZE; x++) { + for (int y = 0; y < AFFINE_MIN_BLOCK_SIZE; y++) { + Mv diff; + diff.x = x * (sp->d_hor_x << 2) + y * (sp->d_hor_y << 2) - pos_offset_x; + diff.y = x * (sp->d_ver_x << 2) + y * (sp->d_ver_y << 2) - pos_offset_y; + ff_vvc_round_mv(&diff, 0, 8); + pu->diff_mv_x[lx][AFFINE_MIN_BLOCK_SIZE * y + x] = av_clip(diff.x, -dmv_limit + 1, dmv_limit - 1); + pu->diff_mv_y[lx][AFFINE_MIN_BLOCK_SIZE * y + x] = av_clip(diff.y, -dmv_limit + 1, dmv_limit - 1); + } + } + } +} + +static void store_cp_mv(const VVCLocalContext *lc, const MotionInfo *mi, const int lx) +{ + VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const int log2_min_cb_size = fc->ps.sps->min_cb_log2_size_y; + const int min_cb_size = fc->ps.sps->min_cb_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int num_cp_mv = mi->motion_model_idc + 1; + + for (int dy = 0; dy < cu->cb_height; dy += min_cb_size) { + for (int dx = 0; dx < cu->cb_width; dx += min_cb_size) { + const int x_cb = (cu->x0 + dx) >> log2_min_cb_size; + const int y_cb = (cu->y0 + dy) >> log2_min_cb_size; + const int offset = (y_cb * min_cb_width + x_cb) * MAX_CONTROL_POINTS; + + memcpy(&fc->tab.cp_mv[lx][offset], mi->mv[lx], sizeof(Mv) * num_cp_mv); + SAMPLE_CTB(fc->tab.mmi, x_cb, y_cb) = mi->motion_model_idc; + } + } +} + +//8.5.5.9 Derivation process for motion vector arrays from affine control point motion vectors +void ff_vvc_store_sb_mvs(const VVCLocalContext *lc, PredictionUnit *pu) +{ + const CodingUnit *cu = lc->cu; + const MotionInfo *mi = &pu->mi; + const int sbw = cu->cb_width / mi->num_sb_x; + const int sbh = cu->cb_height / mi->num_sb_y; + SubblockParams params[2]; + MvField mvf; + + mvf.pred_flag = mi->pred_flag; + mvf.bcw_idx = mi->bcw_idx; + mvf.hpel_if_idx = mi->hpel_if_idx; + mvf.ciip_flag = 0; + for (int i = 0; i < 2; i++) { + const PredFlag mask = i + 1; + if (mi->pred_flag & mask) { + store_cp_mv(lc, mi, i); + init_subblock_params(params + i, mi, cu->cb_width, cu->cb_height, i); + derive_subblock_diff_mvs(lc, pu, params + i, i); + mvf.ref_idx[i] = mi->ref_idx[i]; + } + } + + for (int sby = 0; sby < mi->num_sb_y; sby++) { + for (int sbx = 0; sbx < mi->num_sb_x; sbx++) { + const int x0 = cu->x0 + sbx * sbw; + const int y0 = cu->y0 + sby * sbh; + for (int i = 0; i < 2; i++) { + const PredFlag mask = i + 1; + if (mi->pred_flag & mask) { + const SubblockParams* sp = params + i; + const int x_pos_cb = sp->is_fallback ? (cu->cb_width >> 1) : (2 + (sbx << MIN_CU_LOG2)); + const int y_pos_cb = sp->is_fallback ? (cu->cb_height >> 1) : (2 + (sby << MIN_CU_LOG2)); + Mv *mv = mvf.mv + i; + + mv->x = sp->mv_scale_hor + sp->d_hor_x * x_pos_cb + sp->d_hor_y * y_pos_cb; + mv->y = sp->mv_scale_ver + sp->d_ver_x * x_pos_cb + sp->d_ver_y * y_pos_cb; + ff_vvc_round_mv(mv, 0, MAX_CU_DEPTH); + ff_vvc_clip_mv(mv); + } + } + ff_vvc_set_mvf(lc, x0, y0, sbw, sbh, &mvf); + } + } +} + +void ff_vvc_store_gpm_mvf(const VVCLocalContext *lc, const PredictionUnit *pu) +{ + const CodingUnit *cu = lc->cu; + const int angle_idx = ff_vvc_gpm_angle_idx[pu->gpm_partition_idx]; + const int distance_idx = ff_vvc_gpm_distance_idx[pu->gpm_partition_idx]; + const int displacement_x = ff_vvc_gpm_distance_lut[angle_idx]; + const int displacement_y = ff_vvc_gpm_distance_lut[(angle_idx + 8) % 32]; + const int is_flip = angle_idx >= 13 &&angle_idx <= 27; + const int shift_hor = (angle_idx % 16 == 8 || (angle_idx % 16 && cu->cb_height >= cu->cb_width)) ? 0 : 1; + const int sign = angle_idx < 16 ? 1 : -1; + const int block_size = 4; + int offset_x = (-cu->cb_width) >> 1; + int offset_y = (-cu->cb_height) >> 1; + + if (!shift_hor) + offset_y += sign * ((distance_idx * cu->cb_height) >> 3); + else + offset_x += sign * ((distance_idx * cu->cb_width) >> 3); + + for (int y = 0; y < cu->cb_height; y += block_size) { + for (int x = 0; x < cu->cb_width; x += block_size) { + const int motion_idx = (((x + offset_x) << 1) + 5) * displacement_x + + (((y + offset_y) << 1) + 5) * displacement_y; + const int s_type = FFABS(motion_idx) < 32 ? 2 : (motion_idx <= 0 ? (1 - is_flip) : is_flip); + const int pred_flag = pu->gpm_mv[0].pred_flag | pu->gpm_mv[1].pred_flag; + const int x0 = cu->x0 + x; + const int y0 = cu->y0 + y; + + if (!s_type) + ff_vvc_set_mvf(lc, x0, y0, block_size, block_size, pu->gpm_mv + 0); + else if (s_type == 1 || (s_type == 2 && pred_flag != PF_BI)) + ff_vvc_set_mvf(lc, x0, y0, block_size, block_size, pu->gpm_mv + 1); + else { + MvField mvf = pu->gpm_mv[0]; + const MvField *mv1 = &pu->gpm_mv[1]; + const int lx = mv1->pred_flag - PF_L0; + mvf.pred_flag = PF_BI; + mvf.ref_idx[lx] = mv1->ref_idx[lx]; + mvf.mv[lx] = mv1->mv[lx]; + ff_vvc_set_mvf(lc, x0, y0, block_size, block_size, &mvf); + } + } + } +} + +void ff_vvc_store_mvf(const VVCLocalContext *lc, const MvField *mvf) +{ + const CodingUnit *cu = lc->cu; + ff_vvc_set_mvf(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height, mvf); +} + +void ff_vvc_store_mv(const VVCLocalContext *lc, const MotionInfo *mi) +{ + const CodingUnit *cu = lc->cu; + MvField mvf; + + mvf.hpel_if_idx = mi->hpel_if_idx; + mvf.bcw_idx = mi->bcw_idx; + mvf.pred_flag = mi->pred_flag; + mvf.ciip_flag = 0; + + for (int i = 0; i < 2; i++) { + const PredFlag mask = i + 1; + if (mvf.pred_flag & mask) { + mvf.mv[i] = mi->mv[i][0]; + mvf.ref_idx[i] = mi->ref_idx[i]; + } + } + ff_vvc_set_mvf(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height, &mvf); +} + +typedef enum NeighbourIdx { + A0, + A1, + A2, + B0, + B1, + B2, + B3, + NUM_NBS, + NB_IDX_NONE = NUM_NBS, +} NeighbourIdx; + +typedef struct Neighbour { + int x; + int y; + + int checked; + int available; +} Neighbour; + +typedef struct NeighbourContext { + Neighbour neighbours[NUM_NBS]; + const VVCLocalContext *lc; +} NeighbourContext; + +static int is_a0_available(const VVCLocalContext *lc, const CodingUnit *cu) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int x0b = av_mod_uintp2(cu->x0, sps->ctb_log2_size_y); + int cand_bottom_left; + + if (!x0b && !lc->ctb_left_flag) { + cand_bottom_left = 0; + } else { + const int log2_min_cb_size = sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int x = (cu->x0 - 1) >> log2_min_cb_size; + const int y = (cu->y0 + cu->cb_height) >> log2_min_cb_size; + const int max_y = FFMIN(fc->ps.pps->height, ((cu->y0 >> sps->ctb_log2_size_y) + 1) << sps->ctb_log2_size_y); + if (cu->y0 + cu->cb_height >= max_y) + cand_bottom_left = 0; + else + cand_bottom_left = SAMPLE_CTB(fc->tab.cb_width[0], x, y) != 0; + } + return cand_bottom_left; +} + +static void init_neighbour_context(NeighbourContext *ctx, const VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + const NeighbourAvailable *na = &lc->na; + const int x0 = cu->x0; + const int y0 = cu->y0; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + const int a0_available = is_a0_available(lc, cu); + + Neighbour neighbours[NUM_NBS] = { + { x0 - 1, y0 + cb_height, !a0_available }, //A0 + { x0 - 1, y0 + cb_height - 1, !na->cand_left }, //A1 + { x0 - 1, y0, !na->cand_left }, //A2 + { x0 + cb_width, y0 - 1, !na->cand_up_right }, //B0 + { x0 + cb_width - 1, y0 - 1, !na->cand_up }, //B1 + { x0 - 1, y0 - 1, !na->cand_up_left }, //B2 + { x0, y0 - 1, !na->cand_up }, //B3 + }; + + memcpy(ctx->neighbours, neighbours, sizeof(neighbours)); + ctx->lc = lc; +} + +static int check_available(Neighbour *n, const VVCLocalContext *lc, const int is_mvp) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const CodingUnit *cu = lc->cu; + const MvField *tab_mvf = fc->tab.mvf; + const int min_pu_width = fc->ps.pps->min_pu_width; + + if (!n->checked) { + n->checked = 1; + n->available = !sps->r->sps_entropy_coding_sync_enabled_flag || ((n->x >> sps->ctb_log2_size_y) <= (cu->x0 >> sps->ctb_log2_size_y)); + n->available &= TAB_MVF(n->x, n->y).pred_flag != PF_INTRA; + if (!is_mvp) + n->available &= !is_same_mer(fc, n->x, n->y, cu->x0, cu->y0); + } + return n->available; +} + +static const MvField *mv_merge_candidate(const VVCLocalContext *lc, const int x_cand, const int y_cand) +{ + const VVCFrameContext *fc = lc->fc; + const int min_pu_width = fc->ps.pps->min_pu_width; + const MvField* tab_mvf = fc->tab.mvf; + const MvField *mvf = &TAB_MVF(x_cand, y_cand); + + return mvf; +} + +static const MvField* mv_merge_from_nb(NeighbourContext *ctx, const NeighbourIdx nb) +{ + const VVCLocalContext *lc = ctx->lc; + const int is_mvp = 0; + Neighbour *n = &ctx->neighbours[nb]; + + if (check_available(n, lc, is_mvp)) + return mv_merge_candidate(lc, n->x, n->y); + return 0; +} +#define MV_MERGE_FROM_NB(nb) mv_merge_from_nb(&nctx, nb) + +//8.5.2.3 Derivation process for spatial merging candidates +static int mv_merge_spatial_candidates(const VVCLocalContext *lc, const int merge_idx, + const MvField **nb_list, MvField *cand_list, int *nb_merge_cand) +{ + const MvField *cand; + int num_cands = 0; + NeighbourContext nctx; + + static NeighbourIdx nbs[][2] = { + {B1, NB_IDX_NONE }, + {A1, B1 }, + {B0, B1 }, + {A0, A1 }, + }; + + init_neighbour_context(&nctx, lc); + for (int i = 0; i < FF_ARRAY_ELEMS(nbs); i++) { + NeighbourIdx nb = nbs[i][0]; + NeighbourIdx old = nbs[i][1]; + cand = nb_list[nb] = MV_MERGE_FROM_NB(nb); + if (cand && !compare_mv_ref_idx(cand, nb_list[old])) { + cand_list[num_cands] = *cand; + if (merge_idx == num_cands) + return 1; + num_cands++; + } + } + if (num_cands != 4) { + cand = MV_MERGE_FROM_NB(B2); + if (cand && !compare_mv_ref_idx(cand, nb_list[A1]) + && !compare_mv_ref_idx(cand, nb_list[B1])) { + cand_list[num_cands] = *cand; + if (merge_idx == num_cands) + return 1; + num_cands++; + } + } + *nb_merge_cand = num_cands; + return 0; +} + +static int mv_merge_temporal_candidate(const VVCLocalContext *lc, MvField *cand) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + + memset(cand, 0, sizeof(*cand)); + if (fc->ps.ph.r->ph_temporal_mvp_enabled_flag && (cu->cb_width * cu->cb_height > 32)) { + int available_l0 = temporal_luma_motion_vector(lc, 0, cand->mv + 0, 0, 1, 0); + int available_l1 = IS_B(lc->sc->sh.r) ? + temporal_luma_motion_vector(lc, 0, cand->mv + 1, 1, 1, 0) : 0; + cand->pred_flag = available_l0 + (available_l1 << 1); + } + return cand->pred_flag; +} + +//8.5.2.6 Derivation process for history-based merging candidates +static int mv_merge_history_candidates(const VVCLocalContext *lc, const int merge_idx, + const MvField **nb_list, MvField *cand_list, int *num_cands) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const EntryPoint* ep = lc->ep; + for (int i = 1; i <= ep->num_hmvp && (*num_cands < sps->max_num_merge_cand - 1); i++) { + const MvField *h = &ep->hmvp[ep->num_hmvp - i]; + const int same_motion = i <= 2 && (compare_mv_ref_idx(h, nb_list[A1]) || compare_mv_ref_idx(h, nb_list[B1])); + if (!same_motion) { + cand_list[*num_cands] = *h; + if (merge_idx == *num_cands) + return 1; + (*num_cands)++; + } + } + return 0; +} + +//8.5.2.4 Derivation process for pairwise average merging candidate +static int mv_merge_pairwise_candidate(MvField *cand_list, const int num_cands, const int is_b) +{ + if (num_cands > 1) { + const int num_ref_rists = is_b ? 2 : 1; + const MvField* p0 = cand_list + 0; + const MvField* p1 = cand_list + 1; + MvField* cand = cand_list + num_cands; + + cand->pred_flag = 0; + for (int i = 0; i < num_ref_rists; i++) { + PredFlag mask = i + 1; + if (p0->pred_flag & mask) { + cand->pred_flag |= mask; + cand->ref_idx[i] = p0->ref_idx[i]; + if (p1->pred_flag & mask) { + Mv *mv = cand->mv + i; + mv->x = p0->mv[i].x + p1->mv[i].x; + mv->y = p0->mv[i].y + p1->mv[i].y; + ff_vvc_round_mv(mv, 0, 1); + } else { + cand->mv[i] = p0->mv[i]; + } + } else if (p1->pred_flag & mask) { + cand->pred_flag |= mask; + cand->mv[i] = p1->mv[i]; + cand->ref_idx[i] = p1->ref_idx[i]; + } + } + if (cand->pred_flag) { + cand->hpel_if_idx = p0->hpel_if_idx == p1->hpel_if_idx ? p0->hpel_if_idx : 0; + cand->bcw_idx = 0; + cand->ciip_flag = 0; + return 1; + } + } + return 0; +} + +//8.5.2.5 Derivation process for zero motion vector merging candidates +static void mv_merge_zero_motion_candidate(const VVCLocalContext *lc, const int merge_idx, + MvField *cand_list, int num_cands) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int num_ref_idx = IS_P(rsh) ? + rsh->num_ref_idx_active[L0] : FFMIN(rsh->num_ref_idx_active[L0], rsh->num_ref_idx_active[L1]); + int zero_idx = 0; + + while (num_cands < sps->max_num_merge_cand) { + MvField *cand = cand_list + num_cands; + + cand->pred_flag = PF_L0 + (IS_B(rsh) << 1); + AV_ZERO64(cand->mv + 0); + AV_ZERO64(cand->mv + 1); + cand->ref_idx[0] = zero_idx < num_ref_idx ? zero_idx : 0; + cand->ref_idx[1] = zero_idx < num_ref_idx ? zero_idx : 0; + cand->bcw_idx = 0; + cand->hpel_if_idx = 0; + if (merge_idx == num_cands) + return; + num_cands++; + zero_idx++; + } +} + +static void mv_merge_mode(const VVCLocalContext *lc, const int merge_idx, MvField *cand_list) +{ + int num_cands = 0; + const MvField *nb_list[NUM_NBS + 1] = { NULL }; + + if (mv_merge_spatial_candidates(lc, merge_idx, nb_list, cand_list, &num_cands)) + return; + + if (mv_merge_temporal_candidate(lc, &cand_list[num_cands])) { + if (merge_idx == num_cands) + return; + num_cands++; + } + + if (mv_merge_history_candidates(lc, merge_idx, nb_list, cand_list, &num_cands)) + return; + + if (mv_merge_pairwise_candidate(cand_list, num_cands, IS_B(lc->sc->sh.r))) { + if (merge_idx == num_cands) + return; + num_cands++; + } + + mv_merge_zero_motion_candidate(lc, merge_idx, cand_list, num_cands); +} + +//8.5.2.2 Derivation process for luma motion vectors for merge mode +void ff_vvc_luma_mv_merge_mode(VVCLocalContext *lc, const int merge_idx, const int ciip_flag, MvField *mv) +{ + const CodingUnit *cu = lc->cu; + MvField cand_list[MRG_MAX_NUM_CANDS]; + + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + mv_merge_mode(lc, merge_idx, cand_list); + *mv = cand_list[merge_idx]; + //ciip flag in not inhritable + mv->ciip_flag = ciip_flag; +} + +//8.5.4.2 Derivation process for luma motion vectors for geometric partitioning merge mode +void ff_vvc_luma_mv_merge_gpm(VVCLocalContext *lc, const int merge_gpm_idx[2], MvField *mv) +{ + const CodingUnit *cu = lc->cu; + MvField cand_list[MRG_MAX_NUM_CANDS]; + + const int idx[] = { merge_gpm_idx[0], merge_gpm_idx[1] + (merge_gpm_idx[1] >= merge_gpm_idx[0]) }; + + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + mv_merge_mode(lc, FFMAX(idx[0], idx[1]), cand_list); + memset(mv, 0, 2 * sizeof(*mv)); + for (int i = 0; i < 2; i++) { + int lx = idx[i] & 1; + int mask = lx + PF_L0; + MvField *cand = cand_list + idx[i]; + if (!(cand->pred_flag & mask)) { + lx = !lx; + mask = lx + PF_L0; + } + mv[i].pred_flag = mask; + mv[i].ref_idx[lx] = cand->ref_idx[lx]; + mv[i].mv[lx] = cand->mv[lx]; + } + +} + +//8.5.5.5 Derivation process for luma affine control point motion vectors from a neighbouring block +static void affine_cps_from_nb(const VVCLocalContext *lc, + const int x_nb, int y_nb, const int nbw, const int nbh, const int lx, + Mv *cps, int num_cps) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const int x0 = cu->x0; + const int y0 = cu->y0; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + const MvField* tab_mvf = fc->tab.mvf; + const int min_cb_log2_size = fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + + const int log2_nbw = ff_log2(nbw); + const int log2_nbh = ff_log2(nbh); + const int is_ctb_boundary = !((y_nb + nbh) % fc->ps.sps->ctb_size_y) && (y_nb + nbh == y0); + const Mv *l, *r; + int mv_scale_hor, mv_scale_ver, d_hor_x, d_ver_x, d_hor_y, d_ver_y, motion_model_idc_nb; + if (is_ctb_boundary) { + const int min_pu_width = fc->ps.pps->min_pu_width; + l = &TAB_MVF(x_nb, y_nb + nbh - 1).mv[lx]; + r = &TAB_MVF(x_nb + nbw - 1, y_nb + nbh - 1).mv[lx]; + } else { + const int x = x_nb >> min_cb_log2_size; + const int y = y_nb >> min_cb_log2_size; + motion_model_idc_nb = SAMPLE_CTB(fc->tab.mmi, x, y); + + l = &TAB_CP_MV(lx, x_nb, y_nb); + r = &TAB_CP_MV(lx, x_nb + nbw - 1, y_nb) + 1; + } + mv_scale_hor = l->x << 7; + mv_scale_ver = l->y << 7; + d_hor_x = (r->x - l->x) << (7 - log2_nbw); + d_ver_x = (r->y - l->y) << (7 - log2_nbw); + if (!is_ctb_boundary && motion_model_idc_nb == MOTION_6_PARAMS_AFFINE) { + const Mv* lb = &TAB_CP_MV(lx, x_nb, y_nb + nbh - 1) + 2; + d_hor_y = (lb->x - l->x) << (7 - log2_nbh); + d_ver_y = (lb->y - l->y) << (7 - log2_nbh); + } else { + d_hor_y = -d_ver_x; + d_ver_y = d_hor_x; + } + + if (is_ctb_boundary) { + y_nb = y0; + } + cps[0].x = mv_scale_hor + d_hor_x * (x0 - x_nb) + d_hor_y * (y0 - y_nb); + cps[0].y = mv_scale_ver + d_ver_x * (x0 - x_nb) + d_ver_y * (y0 - y_nb); + cps[1].x = mv_scale_hor + d_hor_x * (x0 + cb_width - x_nb) + d_hor_y * (y0 - y_nb); + cps[1].y = mv_scale_ver + d_ver_x * (x0 + cb_width - x_nb) + d_ver_y * (y0 - y_nb); + if (num_cps == 3) { + cps[2].x = mv_scale_hor + d_hor_x * (x0 - x_nb) + d_hor_y * (y0 + cb_height - y_nb); + cps[2].y = mv_scale_ver + d_ver_x * (x0 - x_nb) + d_ver_y * (y0 + cb_height - y_nb); + } + for (int i = 0; i < num_cps; i++) { + ff_vvc_round_mv(cps + i, 0, 7); + ff_vvc_clip_mv(cps + i); + } +} + +//derive affine neighbour's postion, width and height, +static int affine_neighbour_cb(const VVCFrameContext *fc, const int x_nb, const int y_nb, int *x_cb, int *y_cb, int *cbw, int *cbh) +{ + const int log2_min_cb_size = fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int x = x_nb >> log2_min_cb_size; + const int y = y_nb >> log2_min_cb_size; + const int motion_model_idc = SAMPLE_CTB(fc->tab.mmi, x, y); + if (motion_model_idc) { + *x_cb = SAMPLE_CTB(fc->tab.cb_pos_x[0], x, y); + *y_cb = SAMPLE_CTB(fc->tab.cb_pos_y[0], x, y); + *cbw = SAMPLE_CTB(fc->tab.cb_width[0], x, y); + *cbh = SAMPLE_CTB(fc->tab.cb_height[0], x, y); + } + return motion_model_idc; +} + +//part of 8.5.5.2 Derivation process for motion vectors and reference indices in subblock merge mode +static int affine_merge_candidate(const VVCLocalContext *lc, const int x_cand, const int y_cand, MotionInfo* mi) +{ + const VVCFrameContext *fc = lc->fc; + int x, y, w, h, motion_model_idc; + + motion_model_idc = affine_neighbour_cb(fc, x_cand, y_cand, &x, &y, &w, &h); + if (motion_model_idc) { + const int min_pu_width = fc->ps.pps->min_pu_width; + const MvField* tab_mvf = fc->tab.mvf; + const MvField *mvf = &TAB_MVF(x, y); + + mi->bcw_idx = mvf->bcw_idx; + mi->pred_flag = mvf->pred_flag; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (mi->pred_flag & mask) { + affine_cps_from_nb(lc, x, y, w, h, i, &mi->mv[i][0], motion_model_idc + 1); + } + mi->ref_idx[i] = mvf->ref_idx[i]; + } + mi->motion_model_idc = motion_model_idc; + } + return motion_model_idc; +} + +static int affine_merge_from_nbs(NeighbourContext *ctx, const NeighbourIdx *nbs, const int num_nbs, MotionInfo* cand) +{ + const VVCLocalContext *lc = ctx->lc; + const int is_mvp = 0; + for (int i = 0; i < num_nbs; i++) { + Neighbour *n = &ctx->neighbours[nbs[i]]; + if (check_available(n, lc, is_mvp) && affine_merge_candidate(lc, n->x, n->y, cand)) + return 1; + } + return 0; +} +#define AFFINE_MERGE_FROM_NBS(nbs) affine_merge_from_nbs(&nctx, nbs, FF_ARRAY_ELEMS(nbs), mi) + + +static const MvField* derive_corner_mvf(NeighbourContext *ctx, const NeighbourIdx *neighbour, const int num_neighbour) +{ + const VVCFrameContext *fc = ctx->lc->fc; + const MvField *tab_mvf = fc->tab.mvf; + const int min_pu_width = fc->ps.pps->min_pu_width; + for (int i = 0; i < num_neighbour; i++) { + Neighbour *n = &ctx->neighbours[neighbour[i]]; + if (check_available(n, ctx->lc, 0)) { + return &TAB_MVF(n->x, n->y); + } + } + return NULL; +} + +#define DERIVE_CORNER_MV(nbs) derive_corner_mvf(nctx, nbs, FF_ARRAY_ELEMS(nbs)) + +// check if the mv's and refidx are the same between A and B +static av_always_inline int compare_pf_ref_idx(const MvField *A, const struct MvField *B, const struct MvField *C, const int lx) +{ + + const PredFlag mask = (lx + 1) & A->pred_flag; + if (!(B->pred_flag & mask)) + return 0; + if (A->ref_idx[lx] != B->ref_idx[lx]) + return 0; + if (C) { + if (!(C->pred_flag & mask)) + return 0; + if (A->ref_idx[lx] != C->ref_idx[lx]) + return 0; + } + return 1; +} + +static av_always_inline void sb_clip_location(const VVCFrameContext *fc, + const int x_ctb, const int y_ctb, const Mv* temp_mv, int *x, int *y) +{ + const VVCPPS *pps = fc->ps.pps; + const int ctb_log2_size = fc->ps.sps->ctb_log2_size_y; + *y = av_clip(*y + temp_mv->y, y_ctb, FFMIN(pps->height - 1, y_ctb + (1 << ctb_log2_size) - 1)) & ~7; + *x = av_clip(*x + temp_mv->x, x_ctb, FFMIN(pps->width - 1, x_ctb + (1 << ctb_log2_size) + 3)) & ~7; +} + +static void sb_temproal_luma_motion(const VVCLocalContext *lc, + const int x_ctb, const int y_ctb, const Mv *temp_mv, + int x, int y, uint8_t *pred_flag, Mv *mv) +{ + MvField temp_col; + Mv* mvLXCol; + const int refIdxLx = 0; + const VVCFrameContext *fc = lc->fc; + const VVCSH *sh = &lc->sc->sh; + const int min_pu_width = fc->ps.pps->min_pu_width; + VVCFrame *ref = fc->ref->collocated_ref; + MvField *tab_mvf = ref->tab_dmvr_mvf; + int colPic = ref->poc; + int X = 0; + + sb_clip_location(fc, x_ctb, y_ctb, temp_mv, &x, &y); + + temp_col = TAB_MVF(x, y); + mvLXCol = mv + 0; + *pred_flag = DERIVE_TEMPORAL_COLOCATED_MVS(1); + if (IS_B(sh->r)) { + X = 1; + mvLXCol = mv + 1; + *pred_flag |= (DERIVE_TEMPORAL_COLOCATED_MVS(1)) << 1; + } +} + +//8.5.5.4 Derivation process for subblock-based temporal merging base motion data +static int sb_temporal_luma_motion_data(const VVCLocalContext *lc, const MvField *a1, + const int x_ctb, const int y_ctb, MvField *ctr_mvf, Mv *temp_mv) +{ + const VVCFrameContext *fc = lc->fc; + const RefPicList *rpl = lc->sc->rpl; + const CodingUnit *cu = lc->cu; + const int x = cu->x0 + cu->cb_width / 2; + const int y = cu->y0 + cu->cb_height / 2; + const VVCFrame *ref = fc->ref->collocated_ref; + + int colPic; + + memset(temp_mv, 0, sizeof(*temp_mv)); + + if (!ref) { + memset(ctr_mvf, 0, sizeof(*ctr_mvf)); + return 0; + } + + colPic = ref->poc; + + AV_ZERO64(temp_mv); + if (a1) { + if ((a1->pred_flag & PF_L0) && colPic == rpl[0].list[a1->ref_idx[0]]) + *temp_mv = a1->mv[0]; + else if ((a1->pred_flag & PF_L1) && colPic == rpl[1].list[a1->ref_idx[1]]) + *temp_mv = a1->mv[1]; + ff_vvc_round_mv(temp_mv, 0, 4); + } + sb_temproal_luma_motion(lc, x_ctb, y_ctb, temp_mv, x, y, &ctr_mvf->pred_flag , ctr_mvf->mv); + + return ctr_mvf->pred_flag; +} + + +//8.5.5.3 Derivation process for subblock-based temporal merging candidates +static int sb_temporal_merge_candidate(const VVCLocalContext* lc, NeighbourContext *nctx, PredictionUnit *pu) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const VVCSPS *sps = fc->ps.sps; + const VVCPH *ph = &fc->ps.ph; + MotionInfo *mi = &pu->mi; + const int ctb_log2_size = sps->ctb_log2_size_y; + const int x0 = cu->x0; + const int y0 = cu->y0; + const NeighbourIdx n = A1; + const MvField *a1; + MvField ctr_mvf; + Mv temp_mv; + const int x_ctb = (x0 >> ctb_log2_size) << ctb_log2_size; + const int y_ctb = (y0 >> ctb_log2_size) << ctb_log2_size; + + + if (!ph->r->ph_temporal_mvp_enabled_flag || + !sps->r->sps_sbtmvp_enabled_flag || + (cu->cb_width < 8 && cu->cb_height < 8)) + return 0; + + mi->num_sb_x = cu->cb_width >> 3; + mi->num_sb_y = cu->cb_height >> 3; + + a1 = derive_corner_mvf(nctx, &n, 1); + if (sb_temporal_luma_motion_data(lc, a1, x_ctb, y_ctb, &ctr_mvf, &temp_mv)) { + const int sbw = cu->cb_width / mi->num_sb_x; + const int sbh = cu->cb_height / mi->num_sb_y; + MvField mvf = {0}; + for (int sby = 0; sby < mi->num_sb_y; sby++) { + for (int sbx = 0; sbx < mi->num_sb_x; sbx++) { + int x = x0 + sbx * sbw; + int y = y0 + sby * sbh; + sb_temproal_luma_motion(lc, x_ctb, y_ctb, &temp_mv, x + sbw / 2, y + sbh / 2, &mvf.pred_flag, mvf.mv); + if (!mvf.pred_flag) { + mvf.pred_flag = ctr_mvf.pred_flag; + memcpy(mvf.mv, ctr_mvf.mv, sizeof(mvf.mv)); + } + ff_vvc_set_mvf(lc, x, y, sbw, sbh, &mvf); + } + } + return 1; + } + return 0; +} + +static int affine_merge_const1(const MvField *c0, const MvField *c1, const MvField *c2, MotionInfo *mi) +{ + if (c0 && c1 && c2) { + mi->pred_flag = 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (compare_pf_ref_idx(c0, c1, c2, i)) { + mi->pred_flag |= mask; + mi->ref_idx[i] = c0->ref_idx[i]; + mi->mv[i][0] = c0->mv[i]; + mi->mv[i][1] = c1->mv[i]; + mi->mv[i][2] = c2->mv[i]; + } + } + if (mi->pred_flag) { + if (mi->pred_flag == PF_BI) + mi->bcw_idx = c0->bcw_idx; + mi->motion_model_idc = MOTION_6_PARAMS_AFFINE; + return 1; + } + } + return 0; +} + +static int affine_merge_const2(const MvField *c0, const MvField *c1, const MvField *c3, MotionInfo *mi) +{ + if (c0 && c1 && c3) { + mi->pred_flag = 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (compare_pf_ref_idx(c0, c1, c3, i)) { + mi->pred_flag |= mask; + mi->ref_idx[i] = c0->ref_idx[i]; + mi->mv[i][0] = c0->mv[i]; + mi->mv[i][1] = c1->mv[i]; + mi->mv[i][2].x = c3->mv[i].x + c0->mv[i].x - c1->mv[i].x; + mi->mv[i][2].y = c3->mv[i].y + c0->mv[i].y - c1->mv[i].y; + ff_vvc_clip_mv(&mi->mv[i][2]); + } + } + if (mi->pred_flag) { + mi->bcw_idx = mi->pred_flag == PF_BI ? c0->bcw_idx : 0; + mi->motion_model_idc = MOTION_6_PARAMS_AFFINE; + return 1; + } + } + return 0; +} + +static int affine_merge_const3(const MvField *c0, const MvField *c2, const MvField *c3, MotionInfo *mi) +{ + if (c0 && c2 && c3) { + mi->pred_flag = 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (compare_pf_ref_idx(c0, c2, c3, i)) { + mi->pred_flag |= mask; + mi->ref_idx[i] = c0->ref_idx[i]; + mi->mv[i][0] = c0->mv[i]; + mi->mv[i][1].x = c3->mv[i].x + c0->mv[i].x - c2->mv[i].x; + mi->mv[i][1].y = c3->mv[i].y + c0->mv[i].y - c2->mv[i].y; + ff_vvc_clip_mv(&mi->mv[i][1]); + mi->mv[i][2] = c2->mv[i]; + } + } + if (mi->pred_flag) { + mi->bcw_idx = mi->pred_flag == PF_BI ? c0->bcw_idx : 0; + mi->motion_model_idc = MOTION_6_PARAMS_AFFINE; + return 1; + } + } + return 0; +} + +static int affine_merge_const4(const MvField *c1, const MvField *c2, const MvField *c3, MotionInfo *mi) +{ + if (c1 && c2 && c3) { + mi->pred_flag = 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (compare_pf_ref_idx(c1, c2, c3, i)) { + mi->pred_flag |= mask; + mi->ref_idx[i] = c1->ref_idx[i]; + mi->mv[i][0].x = c1->mv[i].x + c2->mv[i].x - c3->mv[i].x; + mi->mv[i][0].y = c1->mv[i].y + c2->mv[i].y - c3->mv[i].y; + ff_vvc_clip_mv(&mi->mv[i][0]); + mi->mv[i][1] = c1->mv[i]; + mi->mv[i][2] = c2->mv[i]; + } + } + if (mi->pred_flag) { + mi->bcw_idx = mi->pred_flag == PF_BI ? c1->bcw_idx : 0; + mi->motion_model_idc = MOTION_6_PARAMS_AFFINE; + return 1; + } + } + return 0; + +} + +static int affine_merge_const5(const MvField *c0, const MvField *c1, MotionInfo *mi) +{ + if (c0 && c1) { + mi->pred_flag = 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (compare_pf_ref_idx(c0, c1, NULL, i)) { + mi->pred_flag |= mask; + mi->ref_idx[i] = c0->ref_idx[i]; + mi->mv[i][0] = c0->mv[i]; + mi->mv[i][1] = c1->mv[i]; + } + } + if (mi->pred_flag) { + if (mi->pred_flag == PF_BI) + mi->bcw_idx = c0->bcw_idx; + mi->motion_model_idc = MOTION_4_PARAMS_AFFINE; + return 1; + } + } + return 0; +} + +static int affine_merge_const6(const MvField* c0, const MvField* c2, const int cb_width, const int cb_height, MotionInfo *mi) +{ + if (c0 && c2) { + const int shift = 7 + av_log2(cb_width) - av_log2(cb_height); + mi->pred_flag = 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (compare_pf_ref_idx(c0, c2, NULL, i)) { + mi->pred_flag |= mask; + mi->ref_idx[i] = c0->ref_idx[i]; + mi->mv[i][0] = c0->mv[i]; + mi->mv[i][1].x = (c0->mv[i].x << 7) + ((c2->mv[i].y - c0->mv[i].y) << shift); + mi->mv[i][1].y = (c0->mv[i].y << 7) - ((c2->mv[i].x - c0->mv[i].x) << shift); + ff_vvc_round_mv(&mi->mv[i][1], 0, 7); + ff_vvc_clip_mv(&mi->mv[i][1]); + } + } + if (mi->pred_flag) { + if (mi->pred_flag == PF_BI) + mi->bcw_idx = c0->bcw_idx; + mi->motion_model_idc = MOTION_4_PARAMS_AFFINE; + return 1; + } + } + return 0; +} + +static void affine_merge_zero_motion(const VVCLocalContext *lc, MotionInfo *mi) +{ + const CodingUnit *cu = lc->cu; + + memset(mi, 0, sizeof(*mi)); + mi->pred_flag = PF_L0 + (IS_B(lc->sc->sh.r) << 1); + mi->motion_model_idc = MOTION_4_PARAMS_AFFINE; + mi->num_sb_x = cu->cb_width >> MIN_PU_LOG2; + mi->num_sb_y = cu->cb_height >> MIN_PU_LOG2; +} + +//8.5.5.6 Derivation process for constructed affine control point motion vector merging candidates +static int affine_merge_const_candidates(const VVCLocalContext *lc, MotionInfo *mi, + NeighbourContext *nctx, const int merge_subblock_idx, int num_cands) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const NeighbourIdx tl[] = { B2, B3, A2 }; + const NeighbourIdx tr[] = { B1, B0}; + const NeighbourIdx bl[] = { A1, A0}; + const MvField *c0, *c1, *c2; + + c0 = DERIVE_CORNER_MV(tl); + c1 = DERIVE_CORNER_MV(tr); + c2 = DERIVE_CORNER_MV(bl); + + if (fc->ps.sps->r->sps_6param_affine_enabled_flag) { + MvField corner3, *c3 = NULL; + //Const1 + if (affine_merge_const1(c0, c1, c2, mi)) { + if (merge_subblock_idx == num_cands) + return 1; + num_cands++; + } + + memset(&corner3, 0, sizeof(corner3)); + if (fc->ps.ph.r->ph_temporal_mvp_enabled_flag){ + const int available_l0 = temporal_luma_motion_vector(lc, 0, corner3.mv + 0, 0, 0, 0); + const int available_l1 = (lc->sc->sh.r->sh_slice_type == VVC_SLICE_TYPE_B) ? + temporal_luma_motion_vector(lc, 0, corner3.mv + 1, 1, 0, 0) : 0; + + corner3.pred_flag = available_l0 + (available_l1 << 1); + if (corner3.pred_flag) + c3 = &corner3; + } + + //Const2 + if (affine_merge_const2(c0, c1, c3, mi)) { + if (merge_subblock_idx == num_cands) + return 1; + num_cands++; + } + + //Const3 + if (affine_merge_const3(c0, c2, c3, mi)) { + if (merge_subblock_idx == num_cands) + return 1; + num_cands++; + } + + //Const4 + if (affine_merge_const4(c1, c2, c3, mi)) { + if (merge_subblock_idx == num_cands) + return 1; + num_cands++; + } + } + + //Const5 + if (affine_merge_const5(c0, c1, mi)) { + if (merge_subblock_idx == num_cands) + return 1; + num_cands++; + } + + if (affine_merge_const6(c0, c2, cu->cb_width, cu->cb_height, mi)) { + if (merge_subblock_idx == num_cands) + return 1; + } + return 0; +} + +//8.5.5.2 Derivation process for motion vectors and reference indices in subblock merge mode +//return 1 if candidate is SbCol +static int sb_mv_merge_mode(const VVCLocalContext *lc, const int merge_subblock_idx, PredictionUnit *pu) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const CodingUnit *cu = lc->cu; + MotionInfo *mi = &pu->mi; + int num_cands = 0; + NeighbourContext nctx; + + init_neighbour_context(&nctx, lc); + + //SbCol + if (sb_temporal_merge_candidate(lc, &nctx, pu)) { + if (merge_subblock_idx == num_cands) + return 1; + num_cands++; + } + + pu->inter_affine_flag = 1; + mi->num_sb_x = cu->cb_width >> MIN_PU_LOG2; + mi->num_sb_y = cu->cb_height >> MIN_PU_LOG2; + + if (sps->r->sps_affine_enabled_flag) { + const NeighbourIdx ak[] = { A0, A1 }; + const NeighbourIdx bk[] = { B0, B1, B2 }; + //A + if (AFFINE_MERGE_FROM_NBS(ak)) { + if (merge_subblock_idx == num_cands) + return 0; + num_cands++; + } + + //B + if (AFFINE_MERGE_FROM_NBS(bk)) { + if (merge_subblock_idx == num_cands) + return 0; + num_cands++; + } + + //Const1 to Const6 + if (affine_merge_const_candidates(lc, mi, &nctx, merge_subblock_idx, num_cands)) + return 0; + } + //Zero + affine_merge_zero_motion(lc, mi); + return 0; +} + +void ff_vvc_sb_mv_merge_mode(VVCLocalContext *lc, const int merge_subblock_idx, PredictionUnit *pu) +{ + const CodingUnit *cu = lc->cu; + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + if (!sb_mv_merge_mode(lc, merge_subblock_idx, pu)) { + ff_vvc_store_sb_mvs(lc, pu); + } +} + +static int mvp_candidate(const VVCLocalContext *lc, const int x_cand, const int y_cand, + const int lx, const int8_t *ref_idx, Mv *mv) +{ + const VVCFrameContext *fc = lc->fc; + const RefPicList *rpl = lc->sc->rpl; + const int min_pu_width = fc->ps.pps->min_pu_width; + const MvField* tab_mvf = fc->tab.mvf; + const MvField *mvf = &TAB_MVF(x_cand, y_cand); + const PredFlag maskx = lx + 1; + const int poc = rpl[lx].list[ref_idx[lx]]; + int available = 0; + + if ((mvf->pred_flag & maskx) && rpl[lx].list[mvf->ref_idx[lx]] == poc) { + available = 1; + *mv = mvf->mv[lx]; + } else { + const int ly = !lx; + const PredFlag masky = ly + 1; + if ((mvf->pred_flag & masky) && rpl[ly].list[mvf->ref_idx[ly]] == poc) { + available = 1; + *mv = mvf->mv[ly]; + } + } + + return available; +} + +static int affine_mvp_candidate(const VVCLocalContext *lc, + const int x_cand, const int y_cand, const int lx, const int8_t *ref_idx, + Mv *cps, const int num_cp) +{ + const VVCFrameContext *fc = lc->fc; + int x_nb, y_nb, nbw, nbh, motion_model_idc, available = 0; + + motion_model_idc = affine_neighbour_cb(fc, x_cand, y_cand, &x_nb, &y_nb, &nbw, &nbh); + if (motion_model_idc) { + const int min_pu_width = fc->ps.pps->min_pu_width; + const MvField* tab_mvf = fc->tab.mvf; + const MvField *mvf = &TAB_MVF(x_nb, y_nb); + RefPicList* rpl = lc->sc->rpl; + const PredFlag maskx = lx + 1; + const int poc = rpl[lx].list[ref_idx[lx]]; + + if ((mvf->pred_flag & maskx) && rpl[lx].list[mvf->ref_idx[lx]] == poc) { + available = 1; + affine_cps_from_nb(lc, x_nb, y_nb, nbw, nbh, lx, cps, num_cp); + } else { + const int ly = !lx; + const PredFlag masky = ly + 1; + if ((mvf->pred_flag & masky) && rpl[ly].list[mvf->ref_idx[ly]] == poc) { + available = 1; + affine_cps_from_nb(lc, x_nb, y_nb, nbw, nbh, ly, cps, num_cp); + } + } + + } + return available; +} + +static int mvp_from_nbs(NeighbourContext *ctx, + const NeighbourIdx *nbs, const int num_nbs, const int lx, const int8_t *ref_idx, const int amvr_shift, + Mv *cps, const int num_cps) +{ + const VVCLocalContext *lc = ctx->lc; + const int is_mvp = 1; + int available = 0; + + for (int i = 0; i < num_nbs; i++) { + Neighbour *n = &ctx->neighbours[nbs[i]]; + if (check_available(n, lc, is_mvp)) { + if (num_cps > 1) + available = affine_mvp_candidate(lc, n->x, n->y, lx, ref_idx, cps, num_cps); + else + available = mvp_candidate(lc, n->x, n->y, lx, ref_idx, cps); + if (available) { + for (int c = 0; c < num_cps; c++) + ff_vvc_round_mv(cps + c, amvr_shift, amvr_shift); + return 1; + } + } + } + return 0; +} + +//get mvp from neighbours +#define AFFINE_MVP_FROM_NBS(nbs) \ + mvp_from_nbs(&nctx, nbs, FF_ARRAY_ELEMS(nbs), lx, ref_idx, amvr_shift, cps, num_cp) \ + +#define MVP_FROM_NBS(nbs) \ + mvp_from_nbs(&nctx, nbs, FF_ARRAY_ELEMS(nbs), lx, ref_idx, amvr_shift, mv, 1) \ + +static int mvp_spatial_candidates(const VVCLocalContext *lc, + const int mvp_lx_flag, const int lx, const int8_t* ref_idx, const int amvr_shift, + Mv* mv, int *nb_merge_cand) +{ + const NeighbourIdx ak[] = { A0, A1 }; + const NeighbourIdx bk[] = { B0, B1, B2 }; + NeighbourContext nctx; + int available_a, num_cands = 0; + Mv mv_a; + + init_neighbour_context(&nctx, lc); + + available_a = MVP_FROM_NBS(ak); + if (available_a) { + if (mvp_lx_flag == num_cands) + return 1; + num_cands++; + mv_a = *mv; + } + if (MVP_FROM_NBS(bk)) { + if (!available_a || !IS_SAME_MV(&mv_a, mv)) { + if (mvp_lx_flag == num_cands) + return 1; + num_cands++; + } + } + *nb_merge_cand = num_cands; + return 0; +} + +static int mvp_temporal_candidates(const VVCLocalContext* lc, + const int mvp_lx_flag, const int lx, const int8_t *ref_idx, const int amvr_shift, + Mv* mv, int *num_cands) +{ + if (temporal_luma_motion_vector(lc, ref_idx[lx], mv, lx, 1, 0)) { + if (mvp_lx_flag == *num_cands) { + ff_vvc_round_mv(mv, amvr_shift, amvr_shift); + return 1; + } + (*num_cands)++; + } + return 0; + +} + +static int mvp_history_candidates(const VVCLocalContext *lc, + const int mvp_lx_flag, const int lx, const int8_t ref_idx, const int amvr_shift, + Mv *mv, int num_cands) +{ + const EntryPoint* ep = lc->ep; + const RefPicList* rpl = lc->sc->rpl; + const int poc = rpl[lx].list[ref_idx]; + + if (ep->num_hmvp == 0) + return 0; + for (int i = 1; i <= FFMIN(4, ep->num_hmvp); i++) { + const MvField* h = &ep->hmvp[i - 1]; + for (int j = 0; j < 2; j++) { + const int ly = (j ? !lx : lx); + PredFlag mask = PF_L0 + ly; + if ((h->pred_flag & mask) && poc == rpl[ly].list[h->ref_idx[ly]]) { + if (mvp_lx_flag == num_cands) { + *mv = h->mv[ly]; + ff_vvc_round_mv(mv, amvr_shift, amvr_shift); + return 1; + } + num_cands++; + } + } + } + return 0; +} + +//8.5.2.8 Derivation process for luma motion vector prediction +static void mvp(const VVCLocalContext *lc, const int mvp_lx_flag, const int lx, + const int8_t *ref_idx, const int amvr_shift, Mv *mv) +{ + int num_cands; + + if (mvp_spatial_candidates(lc, mvp_lx_flag, lx, ref_idx, amvr_shift, mv, &num_cands)) + return; + + if (mvp_temporal_candidates(lc, mvp_lx_flag, lx, ref_idx, amvr_shift, mv, &num_cands)) + return; + + if (mvp_history_candidates(lc, mvp_lx_flag, lx, ref_idx[lx], amvr_shift, mv, num_cands)) + return; + + memset(mv, 0, sizeof(*mv)); +} + +void ff_vvc_mvp(VVCLocalContext *lc, const int *mvp_lx_flag, const int amvr_shift, MotionInfo *mi) +{ + const CodingUnit *cu = lc->cu; + mi->num_sb_x = 1; + mi->num_sb_y = 1; + + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + if (mi->pred_flag != PF_L1) + mvp(lc, mvp_lx_flag[L0], L0, mi->ref_idx, amvr_shift, &mi->mv[L0][0]); + if (mi->pred_flag != PF_L0) + mvp(lc, mvp_lx_flag[L1], L1, mi->ref_idx, amvr_shift, &mi->mv[L1][0]); +} + +static int affine_mvp_constructed_cp(NeighbourContext *ctx, + const NeighbourIdx *neighbour, const int num_neighbour, + const int lx, const int8_t ref_idx, const int amvr_shift, Mv *cp) +{ + const VVCLocalContext *lc = ctx->lc; + const VVCFrameContext *fc = lc->fc; + const MvField *tab_mvf = fc->tab.mvf; + const int min_pu_width = fc->ps.pps->min_pu_width; + const RefPicList* rpl = lc->sc->rpl; + const int is_mvp = 1; + int available = 0; + + for (int i = 0; i < num_neighbour; i++) { + Neighbour *n = &ctx->neighbours[neighbour[i]]; + if (check_available(n, ctx->lc, is_mvp)) { + const PredFlag maskx = lx + 1; + const MvField* mvf = &TAB_MVF(n->x, n->y); + const int poc = rpl[lx].list[ref_idx]; + if ((mvf->pred_flag & maskx) && rpl[lx].list[mvf->ref_idx[lx]] == poc) { + available = 1; + *cp = mvf->mv[lx]; + } else { + const int ly = !lx; + const PredFlag masky = ly + 1; + if ((mvf->pred_flag & masky) && rpl[ly].list[mvf->ref_idx[ly]] == poc) { + available = 1; + *cp = mvf->mv[ly]; + } + } + if (available) { + ff_vvc_round_mv(cp, amvr_shift, amvr_shift); + return 1; + } + } + } + return 0; +} + +#define AFFINE_MVP_CONSTRUCTED_CP(cands, cp) \ + affine_mvp_constructed_cp(nctx, cands, FF_ARRAY_ELEMS(cands), lx, ref_idx, \ + amvr_shift, cp) + +//8.5.5.8 Derivation process for constructed affine control point motion vector prediction candidates +static int affine_mvp_const1(NeighbourContext* nctx, + const int lx, const int8_t ref_idx, const int amvr_shift, + Mv *cps, int *available) +{ + const NeighbourIdx tl[] = { B2, B3, A2 }; + const NeighbourIdx tr[] = { B1, B0 }; + const NeighbourIdx bl[] = { A1, A0 }; + + available[0] = AFFINE_MVP_CONSTRUCTED_CP(tl, cps + 0); + available[1] = AFFINE_MVP_CONSTRUCTED_CP(tr, cps + 1); + available[2] = AFFINE_MVP_CONSTRUCTED_CP(bl, cps + 2); + return available[0] && available[1]; +} + +//8.5.5.7 item 7 +static void affine_mvp_const2(const int idx, Mv *cps, const int num_cp) +{ + const Mv mv = cps[idx]; + for (int j = 0; j < num_cp; j++) + cps[j] = mv; +} + +//8.5.5.7 Derivation process for luma affine control point motion vector predictors +static void affine_mvp(const VVCLocalContext *lc, + const int mvp_lx_flag, const int lx, const int8_t *ref_idx, const int amvr_shift, + MotionModelIdc motion_model_idc, Mv *cps) +{ + const NeighbourIdx ak[] = { A0, A1 }; + const NeighbourIdx bk[] = { B0, B1, B2 }; + const int num_cp = motion_model_idc + 1; + NeighbourContext nctx; + int available[MAX_CONTROL_POINTS]; + int num_cands = 0; + + init_neighbour_context(&nctx, lc); + //Ak + if (AFFINE_MVP_FROM_NBS(ak)) { + if (mvp_lx_flag == num_cands) + return; + num_cands++; + } + //Bk + if (AFFINE_MVP_FROM_NBS(bk)) { + if (mvp_lx_flag == num_cands) + return; + num_cands++; + } + + //Const1 + if (affine_mvp_const1(&nctx, lx, ref_idx[lx], amvr_shift, cps, available)) { + if (available[2] || motion_model_idc == MOTION_4_PARAMS_AFFINE) { + if (mvp_lx_flag == num_cands) + return; + num_cands++; + } + } + + //Const2 + for (int i = 2; i >= 0; i--) { + if (available[i]) { + if (mvp_lx_flag == num_cands) { + affine_mvp_const2(i, cps, num_cp); + return; + } + num_cands++; + } + } + if (temporal_luma_motion_vector(lc, ref_idx[lx], cps, lx, 1, 0)) { + if (mvp_lx_flag == num_cands) { + ff_vvc_round_mv(cps, amvr_shift, amvr_shift); + for (int i = 1; i < num_cp; i++) + cps[i] = cps[0]; + return; + } + num_cands++; + } + + //Zero Mv + memset(cps, 0, num_cp * sizeof(Mv)); +} + +void ff_vvc_affine_mvp(VVCLocalContext *lc, const int *mvp_lx_flag, const int amvr_shift, MotionInfo *mi) +{ + const CodingUnit *cu = lc->cu; + + mi->num_sb_x = cu->cb_width >> MIN_PU_LOG2; + mi->num_sb_y = cu->cb_height >> MIN_PU_LOG2; + + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + if (mi->pred_flag != PF_L1) + affine_mvp(lc, mvp_lx_flag[L0], L0, mi->ref_idx, amvr_shift, mi->motion_model_idc, &mi->mv[L0][0]); + if (mi->pred_flag != PF_L0) + affine_mvp(lc, mvp_lx_flag[L1], L1, mi->ref_idx, amvr_shift, mi->motion_model_idc, &mi->mv[L1][0]); +} + +//8.5.2.14 Rounding process for motion vectors +void ff_vvc_round_mv(Mv *mv, const int lshift, const int rshift) +{ + if (rshift) { + const int offset = 1 << (rshift - 1); + mv->x = ((mv->x + offset - (mv->x >= 0)) >> rshift) << lshift; + mv->y = ((mv->y + offset - (mv->y >= 0)) >> rshift) << lshift; + } else { + mv->x = mv->x << lshift; + mv->y = mv->y << lshift; + } +} + +void ff_vvc_clip_mv(Mv *mv) +{ + mv->x = av_clip(mv->x, -(1 << 17), (1 << 17) - 1); + mv->y = av_clip(mv->y, -(1 << 17), (1 << 17) - 1); +} + +//8.5.2.1 Derivation process for motion vector components and reference indices +static av_always_inline int is_greater_mer(const VVCFrameContext *fc, const int x0, const int y0, const int x0_br, const int y0_br) +{ + const uint8_t plevel = fc->ps.sps->log2_parallel_merge_level; + + return x0_br >> plevel > x0 >> plevel && + y0_br >> plevel > y0 >> plevel; +} + +//8.5.2.16 Updating process for the history-based motion vector predictor candidate list +void ff_vvc_update_hmvp(VVCLocalContext *lc, const MotionInfo *mi) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const int min_pu_width = fc->ps.pps->min_pu_width; + const MvField* tab_mvf = fc->tab.mvf; + EntryPoint* ep = lc->ep; + const MvField *mvf; + int i; + + if (!is_greater_mer(fc, cu->x0, cu->y0, cu->x0 + cu->cb_width, cu->y0 + cu->cb_height)) + return; + mvf = &TAB_MVF(cu->x0, cu->y0); + + for (i = 0; i < ep->num_hmvp; i++) { + if (compare_mv_ref_idx(mvf, ep->hmvp + i)) { + ep->num_hmvp--; + break; + } + } + if (i == MAX_NUM_HMVP_CANDS) { + ep->num_hmvp--; + i = 0; + } + + memmove(ep->hmvp + i, ep->hmvp + i + 1, (ep->num_hmvp - i) * sizeof(MvField)); + ep->hmvp[ep->num_hmvp++] = *mvf; +} + +MvField* ff_vvc_get_mvf(const VVCFrameContext *fc, const int x0, const int y0) +{ + const int min_pu_width = fc->ps.pps->min_pu_width; + MvField* tab_mvf = fc->tab.mvf; + + return &TAB_MVF(x0, y0); +} diff --git a/libavcodec/vvc/vvc_mvs.h b/libavcodec/vvc/vvc_mvs.h new file mode 100644 index 0000000000..f87fcfe0ca --- /dev/null +++ b/libavcodec/vvc/vvc_mvs.h @@ -0,0 +1,46 @@ +/* + * VVC motion vector decoder + * + * Copyright (C) 2023 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VVC_MVS_H +#define AVCODEC_VVC_MVS_H + +#include "vvcdec.h" + +void ff_vvc_round_mv(Mv *mv, int lshift, int rshift); +void ff_vvc_clip_mv(Mv *mv); +void ff_vvc_mv_scale(Mv *dst, const Mv *src, int td, int tb); +void ff_vvc_luma_mv_merge_mode(VVCLocalContext *lc, int merge_idx, int ciip_flag, MvField *mv); +void ff_vvc_luma_mv_merge_gpm(VVCLocalContext *lc, const int merge_gpm_idx[2], MvField *mv); +void ff_vvc_mvp(VVCLocalContext *lc, const int *mvp_lx_flag, const int amvr_shift, MotionInfo *mi); +void ff_vvc_sb_mv_merge_mode(VVCLocalContext *lc, int merge_subblock_idx, PredictionUnit *pu); +void ff_vvc_affine_mvp(VVCLocalContext *lc, const int *mvp_lx_flag, const int amvr_shift, MotionInfo* mi); +void ff_vvc_store_sb_mvs(const VVCLocalContext *lc, PredictionUnit *pu); +void ff_vvc_store_mv(const VVCLocalContext *lc, const MotionInfo *mi); +void ff_vvc_store_mvf(const VVCLocalContext *lc, const MvField *mvf); +void ff_vvc_store_gpm_mvf(const VVCLocalContext *lc, const PredictionUnit* pu); +void ff_vvc_update_hmvp(VVCLocalContext *lc, const MotionInfo *mi); +int ff_vvc_no_backward_pred_flag(const VVCLocalContext *lc); +MvField* ff_vvc_get_mvf(const VVCFrameContext *fc, const int x0, const int y0); +void ff_vvc_set_mvf(const VVCLocalContext *lc, const int x0, const int y0, const int w, const int h, const MvField *mvf); +void ff_vvc_set_intra_mvf(const VVCLocalContext *lc); + +#endif //AVCODEC_VVC_MVS_H From patchwork Sun Nov 12 10:35:19 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 44629 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:92a5:b0:181:818d:5e7f with SMTP id q37csp728548pzg; Sun, 12 Nov 2023 02:37:57 -0800 (PST) X-Google-Smtp-Source: AGHT+IHUCqMRNv62aknuQpNvAUVDIn6GoxAp/WhRw7So1EF3qR8J2R+l47q9+VycMJl58/5qWRSO X-Received: by 2002:a17:906:d90:b0:9e4:6d0a:c37a with SMTP id m16-20020a1709060d9000b009e46d0ac37amr3561463eji.19.1699785477207; Sun, 12 Nov 2023 02:37:57 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id p9-20020a170906b20900b009e5c8fc6f41si1598629ejz.3.2023.11.12.02.37.56; Sun, 12 Nov 2023 02:37:57 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@outlook.com header.s=selector1 header.b=R96RpHv2; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id D465468CCA3; Sun, 12 Nov 2023 12:36:26 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from APC01-PSA-obe.outbound.protection.outlook.com (mail-psaapc01olkn2055.outbound.protection.outlook.com [40.92.52.55]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 2DDB768CC40 for ; Sun, 12 Nov 2023 12:36:17 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=SFXyttpriYETwZRnS38iwb4cjhGl3X7NGJgSCYAHNVrO1ZmlV6KY2Bdo/vKmurxnM6RPkScRUf9f+Ltlzo3xz6E0HMJr56d+XMY/hb2SonlQU3QWrQDAhgpyqA+VHyUkVIJonzg3E4u9bl+2N63m0TM3rUSrxCie5LTRpHue0tWtw74+fqjVBG+dA9ODEYEX4RbDxeYp4u6nwEnAr5Qv/0lcv7khUAIiZ7mWEJgDM0EE7DlWQ0PNkOIQB/EGVx3+hBV/5Kt9X/jJDVzPd3O9CPuTx4MrMndBtHtdSHwfzP+m/V4H6EoVrs85xUgG17fKk20d+vBvdZvf5XioA5fb9g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=VlRE4Fq2efz/u6FMXS3gQfSmtdniXhnNAbaRHOC1DwM=; b=TiFnp1aw2gcEb62zQZLD2mGxWbo7B98WNUfh4AhnQL76cSqDtTadDyTnD7sCXw9ZitrhpOE8FwIJMNOQKdtU5115+FxRowrdTo4Yvj2/wR5edAN+TvIkhU+VE3YOzXwfHM2O4UMJX84RPewYmbZHMX9WBdCX3SSJRAkk4Y8ZlTKgTDOGPtM6sfiik+yKptQfWPX0f0U6KIfXmLOa8WUS/p1KKouo0bDPWjCTaG9RRMk8GhmBIL8y+NQVhhtUJPr5PCuK+IkPz/CuZOCoCRDHVVmnc6DO/N3hWZgNR32Illq98aMrFosXbL6JY6htU7+smfVgsqL5+gLHC6OOZRQC1w== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=outlook.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=VlRE4Fq2efz/u6FMXS3gQfSmtdniXhnNAbaRHOC1DwM=; b=R96RpHv2Cujqm1CQrhOFegzyONN6uJOJPUlp3uNL98+JM/7FwNnooDkvyeuv0e+x9B+Qgl9i6U27Dlbiajtvh/xkdjHBewCfCT3U+GDxEXdqemgt41diUeKmurmnH/3DG0vTDg0sx2GJ6M2OV0Zy/guq/Hg6At7IB+flVs0LZdllBRt8su+VFgDeNAwQYZOzYwpMO/0/pzt4k8uQNPg/x3NTbIoGRb2iwDsN7ZUVq9IDLsbT8rGbAnNk4DUw5BSm85hzSGV0EKQXsr5C8ZKfNJ/ca1A7gTiIDyf+AvmRwo07ldXL7L8ECJkn4QQ7R+WibqD+BFfOGBI2AFOdlm28OQ== Received: from TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) by TYSPR06MB6768.apcprd06.prod.outlook.com (2603:1096:400:475::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6954.29; Sun, 12 Nov 2023 10:35:59 +0000 Received: from TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f]) by TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f%4]) with mapi id 15.20.6954.028; Sun, 12 Nov 2023 10:35:59 +0000 From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Nov 2023 18:35:19 +0800 Message-ID: X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231112103526.11245-1-nuomi2021@gmail.com> References: <20231112103526.11245-1-nuomi2021@gmail.com> X-TMN: [Dpl4R0AhwXSlMyyDsv2+qs7IQcDfqcpQ] X-ClientProxiedBy: TYWP286CA0030.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:262::20) To TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) X-Microsoft-Original-Message-ID: <20231112103526.11245-7-nuomi2021@gmail.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 2 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: TYSPR06MB6433:EE_|TYSPR06MB6768:EE_ X-MS-Office365-Filtering-Correlation-Id: c580c859-381b-4f7d-791d-08dbe36b28b8 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 5KfXk5cYhGmhCVrTTwEvl2XpYe/4uD75m+jCZZulTWBKuqqxA4dhcFVu3Xbsqj+/8vnsbEACaTQsPpsKBwFUXDygjtqZA22uS+3TD0geMAERhRB729ttpQL4BD/3FOtgXuQFvsW9Ewsi/C4roElut+mBY7DUIcehY8h4cyGqL4U+K206D9wJu/qb5p6Ql7obSfZmWJh61UelYqBMtKb4T/Ywb7K9IJVTWoLIWBAj+wgMrDpwV+Niy2GOsLf7mzz+xuE8pmlmeRYPm7Ad1Y91ck3D+q2zrRTyztQIRjqfPeggRz3LtnuShZ5jSrcDslNYRPae6oKGzxmgVlsMtgf//b2cB99CaRsvrlSTADRY+PytNs+jD9PTslpAbnfIoIhzmIPhL0dP5EPiT/QybOcOI7a8DNWssP3sXqkeOT0Mx5EWs+kfQMYP/DzrdUV6mug6OD28gYwI63fZAJf0GUKn3PmTlJgd8sVASd8XCYWT2LdfuH3QizHyGODClStClHWgNwlbqmpu6rvspKSudV55n6b5hUVo63chg9m8UAKyXyn2LoIk/OQ1a7EYWmCEhG9q6gaRUAwE4OXAeTq7Rd8lUKbm02ebAdjuLTTYHR+BSB5wEpgIjx4kDyY2RIoNScDB X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: bVZKa0dh0hGfswVlhw02OW4Rz9gFFg63S01Kqql0JWXGXv/eJLX3N36U2l4WQ2RBjazF7Uhu2YeJYvXEE9QfiibYyvRjdp05Mvb8aSLp+Y/eeknWE2Rw25DIzvWQBgbMj3EWvLWXnntKNBCFWnWVLlBBBn1WWS9uLuPTvdoudGYTtgbluwrI7xJ9zMrSeYBtbjbkQUVS8rNMO8jMNUQUr1rDzn6IwVqRX+MMMK/SQPhLbfifIwcAcA0tHnFKTRAjMEY6G2sdzn15MuTT9Tt2V+FCtwNvC3DEyeQ/byYkJypvA57B66LRlvzu0HMNiuC119BcG79HIoWjCjSH9G5e/gNBBUiBrTYBOfI8RUqDeviTgPMFk0QfMYnYIs2Ew80sT3eNuojbzxEDfNqtI41u0jxBYrTAE7BftaLPuh/+oT5QI/8OsFok8OO7Dq8Dou8HxkcpgYu4Nx5jNapoU8QvcngrHBj8uC1Te7neBImI51gWSLdvUkw4TsGSE4x1dcZ869jizGIaj15W4/i35/nTahmUeRtD0Mw40+hLDv0cj/sj/LYKtrUPLfs6d6ALT8Dirw+vPpRAml5xPKZQTvt8VIr6NVThFRQ+XXCTUhL0Fv4jATzbVHJz4Ow8WTzipT4jY0xEBpQS73IJf0Jr047XSQIEviJ/Bn72h8YfEUiVSrcIhO2UtgIHBc84RKxkBsPEOeNJ25ijTdXn0XFGp9vCTCXdrnZIA/O+0O5oo6Q4pQ+8c93lgH/ilDhLcKJhw7/+KwIQ53G2fTABr9CsC95EKILlUxK/dTUpf8d30G+EG/IjQ2FDoASI7Iu6Rb54Xc2KXZ8p6mW4hMw0SqPfq0I42SGajbIQdicqbUaq7vCZOTOmzkvKE1cAyyuDuu5U7dUbjDN8f3IzYgQ4vdGVsXVfoiuMGLXO0ag8opxUi9uQqni+6j3wmJNmoCCzXwz7hDULz0cta+LCT+lF3Do/pNf4N4G5jAcKKT8Wb8tLt+UodtYDgENeAld9Iox/g/q0fV2057cwut/bHmaYnlmoUyJMlPo0vDruHHC9YOdij03jNH4U9d0NR4f508zV/w/o3S1NTc3wV+MXPq1xe6W4SKqw0+5juimnWWe6NWjsWs4HpstYS5ODQFGe8/icYlcjoJwVgxs47bDnLUbnSiQmckcrTaACrcdc+/nBlWlBr9aa3n6Pihn+f46sLRD2x/3+y43NxMVmdkrKdSKBunernkmxQTmP3jnp2EF6Ajy511CQ5z0iz5t6vEEJ4j5Fc8tzxWqm X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: c580c859-381b-4f7d-791d-08dbe36b28b8 X-MS-Exchange-CrossTenant-AuthSource: TYSPR06MB6433.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Nov 2023 10:35:58.9818 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYSPR06MB6768 Subject: [FFmpeg-devel] [PATCH v5 07/14] vvcdec: add inter prediction X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Nuo Mi Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: ouAhubcOas7t --- libavcodec/vvc/Makefile | 1 + libavcodec/vvc/vvc_inter.c | 939 ++++++++++++++++++++++++ libavcodec/vvc/vvc_inter.h | 42 ++ libavcodec/vvc/vvc_inter_template.c | 1023 +++++++++++++++++++++++++++ libavcodec/vvc/vvcdec.h | 5 + libavcodec/vvc/vvcdsp.h | 170 +++++ 6 files changed, 2180 insertions(+) create mode 100644 libavcodec/vvc/vvc_inter.c create mode 100644 libavcodec/vvc/vvc_inter.h create mode 100644 libavcodec/vvc/vvc_inter_template.c create mode 100644 libavcodec/vvc/vvcdsp.h diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile index 912e9f516c..61b1ab39de 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -5,6 +5,7 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ vvc/vvc_cabac.o \ vvc/vvc_ctu.o \ vvc/vvc_data.o \ + vvc/vvc_inter.o \ vvc/vvc_mvs.o \ vvc/vvc_ps.o \ vvc/vvc_refs.o \ diff --git a/libavcodec/vvc/vvc_inter.c b/libavcodec/vvc/vvc_inter.c new file mode 100644 index 0000000000..c494089c6b --- /dev/null +++ b/libavcodec/vvc/vvc_inter.c @@ -0,0 +1,939 @@ +/* + * VVC inter prediction + * + * Copyright (C) 2022 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "libavutil/frame.h" + +#include "vvc_data.h" +#include "vvc_inter.h" +#include "vvc_mvs.h" +#include "vvc_refs.h" + +// +1 is enough, + 32 for asm alignment +#define PROF_TEMP_OFFSET (MAX_PB_SIZE + 32) +static const int bcw_w_lut[] = {4, 5, 3, 10, -2}; + +static int emulated_edge(const VVCFrameContext *fc, uint8_t *dst, const uint8_t **src, ptrdiff_t *src_stride, + const int x_off, const int y_off, const int block_w, const int block_h, const int is_luma) +{ + const int extra_before = is_luma ? LUMA_EXTRA_BEFORE : CHROMA_EXTRA_BEFORE; + const int extra_after = is_luma ? LUMA_EXTRA_AFTER : CHROMA_EXTRA_AFTER; + const int extra = is_luma ? LUMA_EXTRA : CHROMA_EXTRA; + const int pic_width = is_luma ? fc->ps.pps->width : (fc->ps.pps->width >> fc->ps.sps->hshift[1]); + const int pic_height = is_luma ? fc->ps.pps->height : (fc->ps.pps->height >> fc->ps.sps->vshift[1]); + + if (x_off < extra_before || y_off < extra_before || + x_off >= pic_width - block_w - extra_after || + y_off >= pic_height - block_h - extra_after) { + const ptrdiff_t edge_emu_stride = EDGE_EMU_BUFFER_STRIDE << fc->ps.sps->pixel_shift; + int offset = extra_before * *src_stride + (extra_before << fc->ps.sps->pixel_shift); + int buf_offset = extra_before * edge_emu_stride + (extra_before << fc->ps.sps->pixel_shift); + + fc->vdsp.emulated_edge_mc(dst, *src - offset, edge_emu_stride, *src_stride, + block_w + extra, block_h + extra, x_off - extra_before, y_off - extra_before, + pic_width, pic_height); + + *src = dst + buf_offset; + *src_stride = edge_emu_stride; + return 1; + } + return 0; +} + +static void emulated_edge_dmvr(const VVCFrameContext *fc, uint8_t *dst, const uint8_t **src, ptrdiff_t *src_stride, + const int x_sb, const int y_sb, const int x_off, const int y_off, const int block_w, const int block_h, const int is_luma) +{ + const int extra_before = is_luma ? LUMA_EXTRA_BEFORE : CHROMA_EXTRA_BEFORE; + const int extra_after = is_luma ? LUMA_EXTRA_AFTER : CHROMA_EXTRA_AFTER; + const int extra = is_luma ? LUMA_EXTRA : CHROMA_EXTRA; + const int pic_width = is_luma ? fc->ps.pps->width : (fc->ps.pps->width >> fc->ps.sps->hshift[1]); + const int pic_height = is_luma ? fc->ps.pps->height : (fc->ps.pps->height >> fc->ps.sps->vshift[1]); + + if (x_off < extra_before || y_off < extra_before || + x_off >= pic_width - block_w - extra_after || + y_off >= pic_height - block_h - extra_after|| + (x_off != x_sb || y_off != y_sb)) { + const int ps = fc->ps.sps->pixel_shift; + const ptrdiff_t edge_emu_stride = EDGE_EMU_BUFFER_STRIDE << ps; + const int offset = extra_before * *src_stride + (extra_before << ps); + const int buf_offset = extra_before * edge_emu_stride + (extra_before << ps); + + const int start_x = FFMIN(FFMAX(x_sb - extra_before, 0), pic_width - 1); + const int start_y = FFMIN(FFMAX(y_sb - extra_before, 0), pic_height - 1); + const int width = FFMAX(FFMIN(pic_width, x_sb + block_w + extra_after) - start_x, 1); + const int height = FFMAX(FFMIN(pic_height, y_sb + block_h + extra_after) - start_y, 1); + + fc->vdsp.emulated_edge_mc(dst, *src - offset, edge_emu_stride, *src_stride, block_w + extra, block_h + extra, + x_off - start_x - extra_before, y_off - start_y - extra_before, width, height); + + *src = dst + buf_offset; + *src_stride = edge_emu_stride; + } +} + +static void emulated_edge_bilinear(const VVCFrameContext *fc, uint8_t *dst, const uint8_t **src, ptrdiff_t *src_stride, + const int x_off, const int y_off, const int block_w, const int block_h) +{ + int pic_width = fc->ps.pps->width; + int pic_height = fc->ps.pps->height; + + if (x_off < BILINEAR_EXTRA_BEFORE || y_off < BILINEAR_EXTRA_BEFORE || + x_off >= pic_width - block_w - BILINEAR_EXTRA_AFTER || + y_off >= pic_height - block_h - BILINEAR_EXTRA_AFTER) { + const ptrdiff_t edge_emu_stride = EDGE_EMU_BUFFER_STRIDE << fc->ps.sps->pixel_shift; + const int offset = BILINEAR_EXTRA_BEFORE * *src_stride + (BILINEAR_EXTRA_BEFORE << fc->ps.sps->pixel_shift); + const int buf_offset = BILINEAR_EXTRA_BEFORE * edge_emu_stride + (BILINEAR_EXTRA_BEFORE << fc->ps.sps->pixel_shift); + + fc->vdsp.emulated_edge_mc(dst, *src - offset, edge_emu_stride, *src_stride, block_w + BILINEAR_EXTRA, block_h + BILINEAR_EXTRA, + x_off - BILINEAR_EXTRA_BEFORE, y_off - BILINEAR_EXTRA_BEFORE, pic_width, pic_height); + + *src = dst + buf_offset; + *src_stride = edge_emu_stride; + } +} + + +#define EMULATED_EDGE_LUMA(dst, src, src_stride, x_off, y_off) \ + emulated_edge(fc, dst, src, src_stride, x_off, y_off, block_w, block_h, 1) + +#define EMULATED_EDGE_CHROMA(dst, src, src_stride, x_off, y_off) \ + emulated_edge(fc, dst, src, src_stride, x_off, y_off, block_w, block_h, 0) + +#define EMULATED_EDGE_DMVR_LUMA(dst, src, src_stride, x_sb, y_sb, x_off, y_off) \ + emulated_edge_dmvr(fc, dst, src, src_stride, x_sb, y_sb, x_off, y_off, block_w, block_h, 1) + +#define EMULATED_EDGE_DMVR_CHROMA(dst, src, src_stride, x_sb, y_sb, x_off, y_off) \ + emulated_edge_dmvr(fc, dst, src, src_stride, x_sb, y_sb, x_off, y_off, block_w, block_h, 0) + +#define EMULATED_EDGE_BILINEAR(dst, src, src_stride, x_off, y_off) \ + emulated_edge_bilinear(fc, dst, src, src_stride, x_off, y_off, pred_w, pred_h) + +// part of 8.5.6.6 Weighted sample prediction process +static int derive_weight_uni(int *denom, int *wx, int *ox, + const VVCLocalContext *lc, const MvField *mvf, const int c_idx) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const VVCSH *sh = &lc->sc->sh; + const int weight_flag = (IS_P(sh->r) && pps->r->pps_weighted_pred_flag) || + (IS_B(sh->r) && pps->r->pps_weighted_bipred_flag); + if (weight_flag) { + const int lx = mvf->pred_flag - PF_L0; + const PredWeightTable *w = pps->r->pps_wp_info_in_ph_flag ? &fc->ps.ph.pwt : &sh->pwt; + + *denom = w->log2_denom[c_idx > 0]; + *wx = w->weight[lx][c_idx][mvf->ref_idx[lx]]; + *ox = w->offset[lx][c_idx][mvf->ref_idx[lx]]; + } + return weight_flag; +} + +// part of 8.5.6.6 Weighted sample prediction process +static int derive_weight(int *denom, int *w0, int *w1, int *o0, int *o1, + const VVCLocalContext *lc, const MvField *mvf, const int c_idx, const int dmvr_flag) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const VVCSH *sh = &lc->sc->sh; + const int bcw_idx = mvf->bcw_idx; + const int weight_flag = (IS_P(sh->r) && pps->r->pps_weighted_pred_flag) || + (IS_B(sh->r) && pps->r->pps_weighted_bipred_flag && !dmvr_flag); + if ((!weight_flag && !bcw_idx) || (bcw_idx && lc->cu->ciip_flag)) + return 0; + + if (bcw_idx) { + *denom = 2; + *w1 = bcw_w_lut[bcw_idx]; + *w0 = 8 - *w1; + *o0 = *o1 = 0; + } else { + const VVCPPS *pps = fc->ps.pps; + const PredWeightTable *w = pps->r->pps_wp_info_in_ph_flag ? &fc->ps.ph.pwt : &sh->pwt; + + *denom = w->log2_denom[c_idx > 0]; + *w0 = w->weight[L0][c_idx][mvf->ref_idx[L0]]; + *w1 = w->weight[L1][c_idx][mvf->ref_idx[L1]]; + *o0 = w->offset[L0][c_idx][mvf->ref_idx[L0]]; + *o1 = w->offset[L1][c_idx][mvf->ref_idx[L1]]; + } + return 1; +} + +static void luma_mc(VVCLocalContext *lc, int16_t *dst, const AVFrame *ref, const Mv *mv, + int x_off, int y_off, const int block_w, const int block_h) +{ + const VVCFrameContext *fc = lc->fc; + const uint8_t *src = ref->data[0]; + ptrdiff_t src_stride = ref->linesize[0]; + const int idx = av_log2(block_w) - 1; + const int mx = mv->x & 0xf; + const int my = mv->y & 0xf; + const int8_t *hf = ff_vvc_inter_luma_filters[0][mx]; + const int8_t *vf = ff_vvc_inter_luma_filters[0][my]; + + x_off += mv->x >> 4; + y_off += mv->y >> 4; + src += y_off * src_stride + (x_off << fc->ps.sps->pixel_shift); + + EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off); + + fc->vvcdsp.inter.put[LUMA][idx][!!my][!!mx](dst, src, src_stride, block_h, hf, vf, block_w); +} + +static void chroma_mc(VVCLocalContext *lc, int16_t *dst, const AVFrame *ref, const Mv *mv, + int x_off, int y_off, const int block_w, const int block_h, const int c_idx) +{ + const VVCFrameContext *fc = lc->fc; + const uint8_t *src = ref->data[c_idx]; + ptrdiff_t src_stride = ref->linesize[c_idx]; + int hs = fc->ps.sps->hshift[c_idx]; + int vs = fc->ps.sps->vshift[c_idx]; + const int idx = av_log2(block_w) - 1; + const intptr_t mx = av_mod_uintp2(mv->x, 4 + hs) << (1 - hs); + const intptr_t my = av_mod_uintp2(mv->y, 4 + vs) << (1 - vs); + const int8_t *hf = ff_vvc_inter_chroma_filters[0][mx]; + const int8_t *vf = ff_vvc_inter_chroma_filters[0][my]; + + x_off += mv->x >> (4 + hs); + y_off += mv->y >> (4 + vs); + src += y_off * src_stride + (x_off << fc->ps.sps->pixel_shift); + + EMULATED_EDGE_CHROMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off); + fc->vvcdsp.inter.put[CHROMA][idx][!!my][!!mx](dst, src, src_stride, block_h, hf, vf, block_w); +} + +static void luma_mc_uni(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const AVFrame *ref, const MvField *mvf, int x_off, int y_off, const int block_w, const int block_h, + const int hf_idx, const int vf_idx) +{ + const VVCFrameContext *fc = lc->fc; + const int lx = mvf->pred_flag - PF_L0; + const Mv *mv = mvf->mv + lx; + const uint8_t *src = ref->data[0]; + ptrdiff_t src_stride = ref->linesize[0]; + const int idx = av_log2(block_w) - 1; + const int mx = mv->x & 0xf; + const int my = mv->y & 0xf; + const int8_t *hf = ff_vvc_inter_luma_filters[hf_idx][mx]; + const int8_t *vf = ff_vvc_inter_luma_filters[vf_idx][my]; + int denom, wx, ox; + + x_off += mv->x >> 4; + y_off += mv->y >> 4; + src += y_off * src_stride + (x_off << fc->ps.sps->pixel_shift); + + EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off); + + if (derive_weight_uni(&denom, &wx, &ox, lc, mvf, LUMA)) { + fc->vvcdsp.inter.put_uni_w[LUMA][idx][!!my][!!mx](dst, dst_stride, src, src_stride, + block_h, denom, wx, ox, hf, vf, block_w); + } else { + fc->vvcdsp.inter.put_uni[LUMA][idx][!!my][!!mx](dst, dst_stride, src, src_stride, + block_h, hf, vf, block_w); + } +} + +static void luma_mc_bi(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const AVFrame *ref0, const Mv *mv0, const int x_off, const int y_off, const int block_w, const int block_h, + const AVFrame *ref1, const Mv *mv1, const MvField *mvf, const int hf_idx, const int vf_idx, + const MvField *orig_mv, const int sb_bdof_flag) +{ + const VVCFrameContext *fc = lc->fc; + const PredictionUnit *pu = &lc->cu->pu; + const int idx = av_log2(block_w) - 1; + const AVFrame *ref[] = { ref0, ref1 }; + int16_t *tmp[] = { lc->tmp + sb_bdof_flag * PROF_TEMP_OFFSET, lc->tmp1 + sb_bdof_flag * PROF_TEMP_OFFSET }; + int denom, w0, w1, o0, o1; + const int weight_flag = derive_weight(&denom, &w0, &w1, &o0, &o1, lc, mvf, LUMA, pu->dmvr_flag); + + for (int i = L0; i <= L1; i++) { + const Mv *mv = mvf->mv + i; + const int mx = mv->x & 0xf; + const int my = mv->y & 0xf; + const int ox = x_off + (mv->x >> 4); + const int oy = y_off + (mv->y >> 4); + ptrdiff_t src_stride = ref[i]->linesize[0]; + const uint8_t *src = ref[i]->data[0] + oy * src_stride + (ox << fc->ps.sps->pixel_shift); + const int8_t *hf = ff_vvc_inter_luma_filters[hf_idx][mx]; + const int8_t *vf = ff_vvc_inter_luma_filters[vf_idx][my]; + + if (pu->dmvr_flag) { + const int x_sb = x_off + (orig_mv->mv[i].x >> 4); + const int y_sb = y_off + (orig_mv->mv[i].y >> 4); + + EMULATED_EDGE_DMVR_LUMA(lc->edge_emu_buffer, &src, &src_stride, x_sb, y_sb, ox, oy); + } else { + EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, ox, oy); + } + fc->vvcdsp.inter.put[LUMA][idx][!!my][!!mx](tmp[i], src, src_stride, block_h, hf, vf, block_w); + if (sb_bdof_flag) + fc->vvcdsp.inter.bdof_fetch_samples(tmp[i], src, src_stride, mx, my, block_w, block_h); + } + + if (sb_bdof_flag) + fc->vvcdsp.inter.apply_bdof(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h); + else if (weight_flag) + fc->vvcdsp.inter.w_avg(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h, denom, w0, w1, o0, o1); + else + fc->vvcdsp.inter.avg(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h); +} + +static void chroma_mc_uni(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const uint8_t *src, ptrdiff_t src_stride, int x_off, int y_off, + const int block_w, const int block_h, const MvField *mvf, const int c_idx, + const int hf_idx, const int vf_idx) +{ + const VVCFrameContext *fc = lc->fc; + const int lx = mvf->pred_flag - PF_L0; + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + const int idx = av_log2(block_w) - 1; + const Mv *mv = &mvf->mv[lx]; + const intptr_t mx = av_mod_uintp2(mv->x, 4 + hs) << (1 - hs); + const intptr_t my = av_mod_uintp2(mv->y, 4 + vs) << (1 - vs); + const int8_t *hf = ff_vvc_inter_chroma_filters[hf_idx][mx]; + const int8_t *vf = ff_vvc_inter_chroma_filters[vf_idx][my]; + int denom, wx, ox; + + x_off += mv->x >> (4 + hs); + y_off += mv->y >> (4 + vs); + src += y_off * src_stride + (x_off << fc->ps.sps->pixel_shift); + + + EMULATED_EDGE_CHROMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off); + if (derive_weight_uni(&denom, &wx, &ox, lc, mvf, c_idx)) { + fc->vvcdsp.inter.put_uni_w[CHROMA][idx][!!my][!!mx](dst, dst_stride, src, src_stride, + block_h, denom, wx, ox, hf, vf, block_w); + } else { + fc->vvcdsp.inter.put_uni[CHROMA][idx][!!my][!!mx](dst, dst_stride, src, src_stride, + block_h, hf, vf, block_w); + } +} + +static void chroma_mc_bi(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const AVFrame *ref0, const AVFrame *ref1, const int x_off, const int y_off, + const int block_w, const int block_h, const MvField *mvf, const int c_idx, + const int hf_idx, const int vf_idx, const MvField *orig_mv, const int dmvr_flag, const int ciip_flag) +{ + const VVCFrameContext *fc = lc->fc; + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + const int idx = av_log2(block_w) - 1; + const AVFrame *ref[] = { ref0, ref1 }; + int16_t *tmp[] = { lc->tmp, lc->tmp1 }; + int denom, w0, w1, o0, o1; + const int weight_flag = derive_weight(&denom, &w0, &w1, &o0, &o1, lc, mvf, c_idx, dmvr_flag); + + for (int i = L0; i <= L1; i++) { + const Mv *mv = mvf->mv + i; + const int mx = av_mod_uintp2(mv->x, 4 + hs) << (1 - hs); + const int my = av_mod_uintp2(mv->y, 4 + vs) << (1 - vs); + const int ox = x_off + (mv->x >> (4 + hs)); + const int oy = y_off + (mv->y >> (4 + vs)); + ptrdiff_t src_stride = ref[i]->linesize[c_idx]; + const uint8_t *src = ref[i]->data[c_idx] + oy * src_stride + (ox << fc->ps.sps->pixel_shift); + const int8_t *hf = ff_vvc_inter_chroma_filters[hf_idx][mx]; + const int8_t *vf = ff_vvc_inter_chroma_filters[vf_idx][my]; + if (dmvr_flag) { + const int x_sb = x_off + (orig_mv->mv[i].x >> (4 + hs)); + const int y_sb = y_off + (orig_mv->mv[i].y >> (4 + vs)); + EMULATED_EDGE_DMVR_CHROMA(lc->edge_emu_buffer, &src, &src_stride, x_sb, y_sb, ox, oy); + } else { + EMULATED_EDGE_CHROMA(lc->edge_emu_buffer, &src, &src_stride, ox, oy); + } + fc->vvcdsp.inter.put[CHROMA][idx][!!my][!!mx](tmp[i], src, src_stride, block_h, hf, vf, block_w); + } + if (weight_flag) + fc->vvcdsp.inter.w_avg(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h, denom, w0, w1, o0, o1); + else + fc->vvcdsp.inter.avg(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h); +} + +static void luma_prof_uni(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const AVFrame *ref, const MvField *mvf, int x_off, int y_off, const int block_w, const int block_h, + const int cb_prof_flag, const int16_t *diff_mv_x, const int16_t *diff_mv_y) +{ + const VVCFrameContext *fc = lc->fc; + const uint8_t *src = ref->data[0]; + ptrdiff_t src_stride = ref->linesize[0]; + uint16_t *prof_tmp = lc->tmp + PROF_TEMP_OFFSET; + const int idx = av_log2(block_w) - 1; + const int lx = mvf->pred_flag - PF_L0; + const Mv *mv = mvf->mv + lx; + const int mx = mv->x & 0xf; + const int my = mv->y & 0xf; + const int8_t *hf = ff_vvc_inter_luma_filters[2][mx]; + const int8_t *vf = ff_vvc_inter_luma_filters[2][my]; + int denom, wx, ox; + const int weight_flag = derive_weight_uni(&denom, &wx, &ox, lc, mvf, LUMA); + + x_off += mv->x >> 4; + y_off += mv->y >> 4; + src += y_off * src_stride + (x_off << fc->ps.sps->pixel_shift); + + EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off); + if (cb_prof_flag) { + fc->vvcdsp.inter.put[LUMA][idx][!!my][!!mx](prof_tmp, src, src_stride, AFFINE_MIN_BLOCK_SIZE, hf, vf, AFFINE_MIN_BLOCK_SIZE); + fc->vvcdsp.inter.fetch_samples(prof_tmp, src, src_stride, mx, my); + if (!weight_flag) + fc->vvcdsp.inter.apply_prof_uni(dst, dst_stride, prof_tmp, diff_mv_x, diff_mv_y); + else + fc->vvcdsp.inter.apply_prof_uni_w(dst, dst_stride, prof_tmp, diff_mv_x, diff_mv_y, denom, wx, ox); + } else { + if (!weight_flag) + fc->vvcdsp.inter.put_uni[LUMA][idx][!!my][!!mx](dst, dst_stride, src, src_stride, block_h, hf, vf, block_w); + else + fc->vvcdsp.inter.put_uni_w[LUMA][idx][!!my][!!mx](dst, dst_stride, src, src_stride, block_h, denom, wx, ox, hf, vf, block_w); + } +} + +static void luma_prof_bi(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const AVFrame *ref0, const AVFrame *ref1, const MvField *mvf, const int x_off, const int y_off, + const int block_w, const int block_h) +{ + const VVCFrameContext *fc = lc->fc; + const PredictionUnit *pu = &lc->cu->pu; + const AVFrame *ref[] = { ref0, ref1 }; + int16_t *tmp[] = { lc->tmp, lc->tmp1 }; + uint16_t *prof_tmp = lc->tmp2 + PROF_TEMP_OFFSET; + const int idx = av_log2(block_w) - 1; + int denom, w0, w1, o0, o1; + const int weight_flag = derive_weight(&denom, &w0, &w1, &o0, &o1, lc, mvf, LUMA, 0); + + for (int i = L0; i <= L1; i++) { + const Mv *mv = mvf->mv + i; + const int mx = mv->x & 0xf; + const int my = mv->y & 0xf; + const int ox = x_off + (mv->x >> 4); + const int oy = y_off + (mv->y >> 4); + ptrdiff_t src_stride = ref[i]->linesize[0]; + const uint8_t *src = ref[i]->data[0] + oy * src_stride + (ox << fc->ps.sps->pixel_shift); + const int8_t *hf = ff_vvc_inter_luma_filters[2][mx]; + const int8_t *vf = ff_vvc_inter_luma_filters[2][my]; + + EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, ox, oy); + if (!pu->cb_prof_flag[i]) { + fc->vvcdsp.inter.put[LUMA][idx][!!my][!!mx](tmp[i], src, src_stride, block_h, hf, vf, block_w); + } else { + fc->vvcdsp.inter.put[LUMA][idx][!!my][!!mx](prof_tmp, src, src_stride, AFFINE_MIN_BLOCK_SIZE, hf, vf, AFFINE_MIN_BLOCK_SIZE); + fc->vvcdsp.inter.fetch_samples(prof_tmp, src, src_stride, mx, my); + fc->vvcdsp.inter.apply_prof(tmp[i], prof_tmp, pu->diff_mv_x[i], pu->diff_mv_y[i]); + } + } + + if (weight_flag) + fc->vvcdsp.inter.w_avg(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h, denom, w0, w1, o0, o1); + else + fc->vvcdsp.inter.avg(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h); +} + +static int pred_get_refs(const VVCLocalContext *lc, VVCFrame *ref[2], const MvField *mv) +{ + const RefPicList *rpl = lc->sc->rpl; + + for (int mask = PF_L0; mask <= PF_L1; mask++) { + if (mv->pred_flag & mask) { + const int lx = mask - PF_L0; + ref[lx] = rpl[lx].ref[mv->ref_idx[lx]]; + if (!ref[lx]) + return AVERROR_INVALIDDATA; + } + } + return 0; +} + +#define POS(c_idx, x, y) \ + &fc->frame->data[c_idx][((y) >> fc->ps.sps->vshift[c_idx]) * fc->frame->linesize[c_idx] + \ + (((x) >> fc->ps.sps->hshift[c_idx]) << fc->ps.sps->pixel_shift)] + +static void pred_gpm_blk(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const PredictionUnit *pu = &cu->pu; + + const uint8_t angle_idx = ff_vvc_gpm_angle_idx[pu->gpm_partition_idx]; + const uint8_t weights_idx = ff_vvc_gpm_angle_to_weights_idx[angle_idx]; + const int w = av_log2(cu->cb_width) - 3; + const int h = av_log2(cu->cb_height) - 3; + const uint8_t off_x = ff_vvc_gpm_weights_offset_x[pu->gpm_partition_idx][h][w]; + const uint8_t off_y = ff_vvc_gpm_weights_offset_y[pu->gpm_partition_idx][h][w]; + const uint8_t mirror_type = ff_vvc_gpm_angle_to_mirror[angle_idx]; + const uint8_t *weights; + + const int c_end = fc->ps.sps->r->sps_chroma_format_idc ? 3 : 1; + + int16_t *tmp[2] = {lc->tmp, lc->tmp1}; + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int hs = fc->ps.sps->hshift[c_idx]; + const int vs = fc->ps.sps->vshift[c_idx]; + const int x = lc->cu->x0 >> hs; + const int y = lc->cu->y0 >> vs; + const int width = cu->cb_width >> hs; + const int height = cu->cb_height >> vs; + uint8_t *dst = POS(c_idx, lc->cu->x0, lc->cu->y0); + ptrdiff_t dst_stride = fc->frame->linesize[c_idx]; + + int step_x = 1 << hs; + int step_y = VVC_GPM_WEIGHT_SIZE << vs; + if (!mirror_type) { + weights = &ff_vvc_gpm_weights[weights_idx][off_y * VVC_GPM_WEIGHT_SIZE + off_x]; + } else if (mirror_type == 1) { + step_x = -step_x; + weights = &ff_vvc_gpm_weights[weights_idx][off_y * VVC_GPM_WEIGHT_SIZE + VVC_GPM_WEIGHT_SIZE - 1- off_x]; + } else { + step_y = -step_y; + weights = &ff_vvc_gpm_weights[weights_idx][(VVC_GPM_WEIGHT_SIZE - 1 - off_y) * VVC_GPM_WEIGHT_SIZE + off_x]; + } + + for (int i = 0; i < 2; i++) { + const MvField *mv = pu->gpm_mv + i; + const int lx = mv->pred_flag - PF_L0; + VVCFrame *ref = lc->sc->rpl[lx].ref[mv->ref_idx[lx]]; + if (!ref) + return; + if (c_idx) + chroma_mc(lc, tmp[i], ref->frame, mv->mv + lx, x, y, width, height, c_idx); + else + luma_mc(lc, tmp[i], ref->frame, mv->mv + lx, x, y, width, height); + } + fc->vvcdsp.inter.put_gpm(dst, dst_stride, width, height, tmp[0], tmp[1], weights, step_x, step_y); + } + return; +} + +static int ciip_derive_intra_weight(const VVCLocalContext *lc, const int x0, const int y0, + const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int x0b = av_mod_uintp2(x0, sps->ctb_log2_size_y); + const int y0b = av_mod_uintp2(y0, sps->ctb_log2_size_y); + const int available_l = lc->ctb_left_flag || x0b; + const int available_u = lc->ctb_up_flag || y0b; + const int min_pu_width = fc->ps.pps->min_pu_width; + + int w = 1; + + if (available_u &&fc->tab.mvf[((y0 - 1) >> MIN_PU_LOG2) * min_pu_width + ((x0 - 1 + width)>> MIN_PU_LOG2)].pred_flag == PF_INTRA) + w++; + + if (available_l && fc->tab.mvf[((y0 - 1 + height)>> MIN_PU_LOG2) * min_pu_width + ((x0 - 1) >> MIN_PU_LOG2)].pred_flag == PF_INTRA) + w++; + + return w; +} + +static void pred_regular_luma(VVCLocalContext *lc, const int hf_idx, const int vf_idx, const MvField *mv, + const int x0, const int y0, const int sbw, const int sbh, const MvField *orig_mv, const int sb_bdof_flag) +{ + const SliceContext *sc = lc->sc; + const VVCFrameContext *fc = lc->fc; + const int ciip_flag = lc->cu->ciip_flag; + uint8_t *dst = POS(0, x0, y0); + const ptrdiff_t dst_stride = fc->frame->linesize[0]; + uint8_t *inter = ciip_flag ? (uint8_t *)lc->ciip_tmp1 : dst; + const ptrdiff_t inter_stride = ciip_flag ? (MAX_PB_SIZE * sizeof(uint16_t)) : dst_stride; + VVCFrame *ref[2]; + + if (pred_get_refs(lc, ref, mv) < 0) + return; + + if (mv->pred_flag != PF_BI) { + const int lx = mv->pred_flag - PF_L0; + luma_mc_uni(lc, inter, inter_stride, ref[lx]->frame, + mv, x0, y0, sbw, sbh, hf_idx, vf_idx); + } else { + luma_mc_bi(lc, inter, inter_stride, ref[0]->frame, + &mv->mv[0], x0, y0, sbw, sbh, ref[1]->frame, &mv->mv[1], mv, + hf_idx, vf_idx, orig_mv, sb_bdof_flag); + } + + if (ciip_flag) { + const int intra_weight = ciip_derive_intra_weight(lc, x0, y0, sbw, sbh); + fc->vvcdsp.intra.intra_pred(lc, x0, y0, sbw, sbh, 0); + if (sc->sh.r->sh_lmcs_used_flag) + fc->vvcdsp.lmcs.filter(inter, inter_stride, sbw, sbh, fc->ps.lmcs.fwd_lut); + fc->vvcdsp.inter.put_ciip(dst, dst_stride, sbw, sbh, inter, inter_stride, intra_weight); + + } +} + +static void pred_regular_chroma(VVCLocalContext *lc, const MvField *mv, + const int x0, const int y0, const int sbw, const int sbh, const MvField *orig_mv, const int dmvr_flag) +{ + const VVCFrameContext *fc = lc->fc; + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + const int x0_c = x0 >> hs; + const int y0_c = y0 >> vs; + const int w_c = sbw >> hs; + const int h_c = sbh >> vs; + const int do_ciip = lc->cu->ciip_flag && (w_c > 2); + + uint8_t* dst1 = POS(1, x0, y0); + uint8_t* dst2 = POS(2, x0, y0); + const ptrdiff_t dst1_stride = fc->frame->linesize[1]; + const ptrdiff_t dst2_stride = fc->frame->linesize[2]; + + uint8_t *inter1 = do_ciip ? (uint8_t *)lc->ciip_tmp1 : dst1; + const ptrdiff_t inter1_stride = do_ciip ? (MAX_PB_SIZE * sizeof(uint16_t)) : dst1_stride; + + uint8_t *inter2 = do_ciip ? (uint8_t *)lc->ciip_tmp2 : dst2; + const ptrdiff_t inter2_stride = do_ciip ? (MAX_PB_SIZE * sizeof(uint16_t)) : dst2_stride; + + //fix me + const int hf_idx = 0; + const int vf_idx = 0; + VVCFrame *ref[2]; + + if (pred_get_refs(lc, ref, mv) < 0) + return; + + if (mv->pred_flag != PF_BI) { + const int lx = mv->pred_flag - PF_L0; + if (!ref[lx]) + return; + + chroma_mc_uni(lc, inter1, inter1_stride, ref[lx]->frame->data[1], ref[lx]->frame->linesize[1], + x0_c, y0_c, w_c, h_c, mv, CB, hf_idx, vf_idx); + chroma_mc_uni(lc, inter2, inter2_stride, ref[lx]->frame->data[2], ref[lx]->frame->linesize[2], + x0_c, y0_c, w_c, h_c, mv, CR, hf_idx, vf_idx); + } else { + if (!ref[0] || !ref[1]) + return; + + chroma_mc_bi(lc, inter1, inter1_stride, ref[0]->frame, ref[1]->frame, + x0_c, y0_c, w_c, h_c, mv, CB, hf_idx, vf_idx, orig_mv, dmvr_flag, lc->cu->ciip_flag); + + chroma_mc_bi(lc, inter2, inter2_stride, ref[0]->frame, ref[1]->frame, + x0_c, y0_c, w_c, h_c, mv, CR, hf_idx, vf_idx, orig_mv, dmvr_flag, lc->cu->ciip_flag); + + } + if (do_ciip) { + const int intra_weight = ciip_derive_intra_weight(lc, x0, y0, sbw, sbh); + fc->vvcdsp.intra.intra_pred(lc, x0, y0, sbw, sbh, 1); + fc->vvcdsp.intra.intra_pred(lc, x0, y0, sbw, sbh, 2); + fc->vvcdsp.inter.put_ciip(dst1, dst1_stride, w_c, h_c, inter1, inter1_stride, intra_weight); + fc->vvcdsp.inter.put_ciip(dst2, dst2_stride, w_c, h_c, inter2, inter2_stride, intra_weight); + + } +} + +// 8.5.3.5 Parametric motion vector refinement process +static int parametric_mv_refine(const int *sad, const int stride) +{ + const int sad_minus = sad[-stride]; + const int sad_center = sad[0]; + const int sad_plus = sad[stride]; + int dmvc; + int denom = (( sad_minus + sad_plus) - (sad_center << 1 ) ) << 3; + if (!denom) + dmvc = 0; + else { + if (sad_minus == sad_center) + dmvc = -8; + else if (sad_plus == sad_center) + dmvc = 8; + else { + int num = ( sad_minus - sad_plus ) << 4; + int sign_num = 0; + int quotient = 0; + int counter = 3; + if (num < 0 ) { + num = - num; + sign_num = 1; + } + while (counter > 0) { + counter = counter - 1; + quotient = quotient << 1; + if ( num >= denom ) { + num = num - denom; + quotient = quotient + 1; + } + denom = (denom >> 1); + } + if (sign_num == 1 ) + dmvc = -quotient; + else + dmvc = quotient; + } + } + return dmvc; +} + +#define SAD_ARRAY_SIZE 5 +//8.5.3 Decoder-side motion vector refinement process +static void dmvr_mv_refine(VVCLocalContext *lc, MvField *mvf, MvField *orig_mv, int *sb_bdof_flag, + const AVFrame *ref0, const AVFrame *ref1, const int x_off, const int y_off, const int block_w, const int block_h) +{ + const VVCFrameContext *fc = lc->fc; + const int sr_range = 2; + const AVFrame *ref[] = { ref0, ref1 }; + int16_t *tmp[] = { lc->tmp, lc->tmp1 }; + int sad[SAD_ARRAY_SIZE][SAD_ARRAY_SIZE]; + int min_dx, min_dy, min_sad, dx, dy; + + *orig_mv = *mvf; + min_dx = min_dy = dx = dy = 2; + + for (int i = L0; i <= L1; i++) { + const int pred_w = block_w + 2 * sr_range; + const int pred_h = block_h + 2 * sr_range; + const Mv *mv = mvf->mv + i; + const int mx = mv->x & 0xf; + const int my = mv->y & 0xf; + const int ox = x_off + (mv->x >> 4) - sr_range; + const int oy = y_off + (mv->y >> 4) - sr_range; + ptrdiff_t src_stride = ref[i]->linesize[LUMA]; + const uint8_t *src = ref[i]->data[LUMA] + oy * src_stride + (ox << fc->ps.sps->pixel_shift); + EMULATED_EDGE_BILINEAR(lc->edge_emu_buffer, &src, &src_stride, ox, oy); + fc->vvcdsp.inter.dmvr[!!my][!!mx](tmp[i], src, src_stride, pred_h, mx, my, pred_w); + } + + min_sad = fc->vvcdsp.inter.sad(tmp[L0], tmp[L1], dx, dy, block_w, block_h); + min_sad -= min_sad >> 2; + sad[dy][dx] = min_sad; + + if (min_sad >= block_w * block_h) { + int dmv[2]; + // 8.5.3.4 Array entry selection process + for (dy = 0; dy < SAD_ARRAY_SIZE; dy++) { + for (dx = 0; dx < SAD_ARRAY_SIZE; dx++) { + if (dx != sr_range || dy != sr_range) { + sad[dy][dx] = fc->vvcdsp.inter.sad(lc->tmp, lc->tmp1, dx, dy, block_w, block_h); + if (sad[dy][dx] < min_sad) { + min_sad = sad[dy][dx]; + min_dx = dx; + min_dy = dy; + } + } + } + } + dmv[0] = (min_dx - sr_range) << 4; + dmv[1] = (min_dy - sr_range) << 4; + if (min_dx != 0 && min_dx != 4 && min_dy != 0 && min_dy != 4) { + dmv[0] += parametric_mv_refine(&sad[min_dy][min_dx], 1); + dmv[1] += parametric_mv_refine(&sad[min_dy][min_dx], SAD_ARRAY_SIZE); + } + + for (int i = L0; i <= L1; i++) { + Mv *mv = mvf->mv + i; + mv->x += (1 - 2 * i) * dmv[0]; + mv->y += (1 - 2 * i) * dmv[1]; + ff_vvc_clip_mv(mv); + } + } + if (min_sad < 2 * block_w * block_h) { + *sb_bdof_flag = 0; + } +} + +static void set_dmvr_info(VVCFrameContext *fc, const int x0, const int y0, + const int width, const int height, const MvField *mvf) + +{ + const VVCPPS *pps = fc->ps.pps; + + for (int y = y0; y < y0 + height; y += MIN_PU_SIZE) { + for (int x = x0; x < x0 + width; x += MIN_PU_SIZE) { + const int idx = pps->min_pu_width * (y >> MIN_PU_LOG2) + (x >> MIN_PU_LOG2); + fc->ref->tab_dmvr_mvf[idx] = *mvf; + } + } +} + +static void fill_dmvr_info(const VVCFrameContext *fc, const int x0, const int y0, + const int width, const int height) +{ + const VVCPPS *pps = fc->ps.pps; + const int w = width >> MIN_PU_LOG2; + + for (int y = y0 >> MIN_PU_LOG2; y < (y0 + height) >> MIN_PU_LOG2; y++) { + const int idx = pps->min_pu_width * y + (x0 >> MIN_PU_LOG2); + const MvField *mvf = fc->tab.mvf + idx; + MvField *dmvr_mvf = fc->ref->tab_dmvr_mvf + idx; + memcpy(dmvr_mvf, mvf, sizeof(MvField) * w); + } +} + +static void derive_sb_mv(VVCLocalContext *lc, MvField *mv, MvField *orig_mv, int *sb_bdof_flag, + const int x0, const int y0, const int sbw, const int sbh) +{ + VVCFrameContext *fc = lc->fc; + const PredictionUnit *pu = &lc->cu->pu; + + *orig_mv = *mv = *ff_vvc_get_mvf(fc, x0, y0); + if (pu->bdof_flag) + *sb_bdof_flag = 1; + if (pu->dmvr_flag) { + VVCFrame* ref[2]; + if (pred_get_refs(lc, ref, mv) < 0) + return; + dmvr_mv_refine(lc, mv, orig_mv, sb_bdof_flag, ref[0]->frame, ref[1]->frame, x0, y0, sbw, sbh); + set_dmvr_info(fc, x0, y0, sbw, sbh, mv); + } +} + +static void pred_regular_blk(VVCLocalContext *lc, const int skip_ciip) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + const MotionInfo *mi = &pu->mi; + MvField mv, orig_mv; + int sbw, sbh, sb_bdof_flag = 0; + + if (cu->ciip_flag && skip_ciip) + return; + + sbw = cu->cb_width / mi->num_sb_x; + sbh = cu->cb_height / mi->num_sb_y; + + for (int sby = 0; sby < mi->num_sb_y; sby++) { + for (int sbx = 0; sbx < mi->num_sb_x; sbx++) { + const int x0 = cu->x0 + sbx * sbw; + const int y0 = cu->y0 + sby * sbh; + + if (cu->ciip_flag) + ff_vvc_set_neighbour_available(lc, x0, y0, sbw, sbh); + + derive_sb_mv(lc, &mv, &orig_mv, &sb_bdof_flag, x0, y0, sbw, sbh); + pred_regular_luma(lc, mi->hpel_if_idx, mi->hpel_if_idx, &mv, x0, y0, sbw, sbh, &orig_mv, sb_bdof_flag); + if (fc->ps.sps->r->sps_chroma_format_idc) + pred_regular_chroma(lc, &mv, x0, y0, sbw, sbh, &orig_mv, pu->dmvr_flag); + } + } +} + +static void derive_affine_mvc(MvField *mvc, const VVCFrameContext *fc, const MvField *mv, + const int x0, const int y0, const int sbw, const int sbh) +{ + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + const MvField* mv2 = ff_vvc_get_mvf(fc, x0 + hs * sbw, y0 + vs * sbh); + *mvc = *mv; + mvc->mv[0].x += mv2->mv[0].x; + mvc->mv[0].y += mv2->mv[0].y; + mvc->mv[1].x += mv2->mv[1].x; + mvc->mv[1].y += mv2->mv[1].y; + ff_vvc_round_mv(mvc->mv + 0, 0, 1); + ff_vvc_round_mv(mvc->mv + 1, 0, 1); +} + +static void pred_affine_blk(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const PredictionUnit *pu = &cu->pu; + const MotionInfo *mi = &pu->mi; + const int x0 = cu->x0; + const int y0 = cu->y0; + const int sbw = cu->cb_width / mi->num_sb_x; + const int sbh = cu->cb_height / mi->num_sb_y; + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + + for (int sby = 0; sby < mi->num_sb_y; sby++) { + for (int sbx = 0; sbx < mi->num_sb_x; sbx++) { + const int x = x0 + sbx * sbw; + const int y = y0 + sby * sbh; + + uint8_t *dst0 = POS(0, x, y); + const MvField *mv = ff_vvc_get_mvf(fc, x, y); + VVCFrame *ref[2]; + + if (pred_get_refs(lc, ref, mv) < 0) + return; + + if (mi->pred_flag != PF_BI) { + const int lx = mi->pred_flag - PF_L0; + luma_prof_uni(lc, dst0, fc->frame->linesize[0], ref[lx]->frame, + mv, x, y, sbw, sbh, pu->cb_prof_flag[lx], + pu->diff_mv_x[lx], pu->diff_mv_y[lx]); + } else { + luma_prof_bi(lc, dst0, fc->frame->linesize[0], ref[0]->frame, ref[1]->frame, + mv, x, y, sbw, sbh); + } + if (fc->ps.sps->r->sps_chroma_format_idc) { + if (!av_mod_uintp2(sby, vs) && !av_mod_uintp2(sbx, hs)) { + MvField mvc; + derive_affine_mvc(&mvc, fc, mv, x, y, sbw, sbh); + pred_regular_chroma(lc, &mvc, x, y, sbw<fc; + const CodingUnit *cu = lc->cu; + const PredictionUnit *pu = &cu->pu; + + if (pu->merge_gpm_flag) + pred_gpm_blk(lc); + else if (pu->inter_affine_flag) + pred_affine_blk(lc); + else + pred_regular_blk(lc, 1); //intra block is not ready yet, skip ciip + + if (!pu->dmvr_flag) + fill_dmvr_info(fc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + if (lc->sc->sh.r->sh_lmcs_used_flag && !cu->ciip_flag) { + uint8_t* dst0 = POS(0, cu->x0, cu->y0); + fc->vvcdsp.lmcs.filter(dst0, fc->frame->linesize[LUMA], cu->cb_width, cu->cb_height, fc->ps.lmcs.fwd_lut); + } +} + +static int has_inter_luma(const CodingUnit *cu) +{ + return cu->pred_mode != MODE_INTRA && cu->pred_mode != MODE_PLT && cu->tree_type != DUAL_TREE_CHROMA; +} + +int ff_vvc_predict_inter(VVCLocalContext *lc, const int rs) +{ + const VVCFrameContext *fc = lc->fc; + const CTU *ctu = fc->tab.ctus + rs; + CodingUnit *cu = ctu->cus; + + while (cu) { + lc->cu = cu; + if (has_inter_luma(cu)) + predict_inter(lc); + cu = cu->next; + } + + return 0; +} + +void ff_vvc_predict_ciip(VVCLocalContext *lc) +{ + av_assert0(lc->cu->ciip_flag); + + //todo: refact out ciip from pred_regular_blk + pred_regular_blk(lc, 0); +} + +#undef POS diff --git a/libavcodec/vvc/vvc_inter.h b/libavcodec/vvc/vvc_inter.h new file mode 100644 index 0000000000..25bfb35cd9 --- /dev/null +++ b/libavcodec/vvc/vvc_inter.h @@ -0,0 +1,42 @@ +/* + * VVC inter prediction + * + * Copyright (C) 2023 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VVC_INTER_H +#define AVCODEC_VVC_INTER_H + +#include "vvc_ctu.h" + +/** + * Loop entire CTU to predict all inter coding blocks + * @param lc local context for CTU + * @param rs raster order for the CTU + * @return AVERROR + */ +int ff_vvc_predict_inter(VVCLocalContext *lc, int rs); + +/** + * CIIP(Combined Inter-Intra Prediction) for a coding block + * @param lc local context for CTU + */ +void ff_vvc_predict_ciip(VVCLocalContext *lc); + +#endif // AVCODEC_VVC_INTER_H diff --git a/libavcodec/vvc/vvc_inter_template.c b/libavcodec/vvc/vvc_inter_template.c new file mode 100644 index 0000000000..b67b66a2dc --- /dev/null +++ b/libavcodec/vvc/vvc_inter_template.c @@ -0,0 +1,1023 @@ +/* + * VVC inter prediction DSP + * + * Copyright (C) 2022 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +static void FUNC(put_pixels)(int16_t *dst, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = src[x] << (14 - BIT_DEPTH); + src += src_stride; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_uni_pixels)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int height, + const int8_t *hf, const int8_t *vf, const int width) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + + for (int y = 0; y < height; y++) { + memcpy(dst, src, width * sizeof(pixel)); + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_uni_w_pixels)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int height, + const int denom, const int wx, const int _ox, const int8_t *hf, const int8_t *vf, + const int width) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + const int ox = _ox * (1 << (BIT_DEPTH - 8)); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int v = (src[x] << (14 - BIT_DEPTH)); + dst[x] = av_clip_pixel(((v * wx + offset) >> shift) + ox); + } + src += src_stride; + dst += dst_stride; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +#define LUMA_FILTER(src, stride) \ + (filter[0] * src[x - 3 * stride] + \ + filter[1] * src[x - 2 * stride] + \ + filter[2] * src[x - stride] + \ + filter[3] * src[x ] + \ + filter[4] * src[x + stride] + \ + filter[5] * src[x + 2 * stride] + \ + filter[6] * src[x + 3 * stride] + \ + filter[7] * src[x + 4 * stride]) + +static void FUNC(put_luma_h)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = hf; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_luma_v)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + const pixel *src = (pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = vf; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = LUMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8); + src += src_stride; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_luma_hv)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = hf; + + src -= LUMA_EXTRA_BEFORE * src_stride; + for (int y = 0; y < height + LUMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + LUMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = vf; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = LUMA_FILTER(tmp, MAX_PB_SIZE) >> 6; + tmp += MAX_PB_SIZE; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_uni_luma_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = hf; + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int val = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + dst[x] = av_clip_pixel((val + offset) >> shift); + } + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_uni_luma_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = vf; + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int val = LUMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8); + dst[x] = av_clip_pixel((val + offset) >> shift); + } + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_uni_luma_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = hf; + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + src -= LUMA_EXTRA_BEFORE * src_stride; + for (int y = 0; y < height + LUMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + LUMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = vf; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int val = LUMA_FILTER(tmp, MAX_PB_SIZE) >> 6; + dst[x] = av_clip_pixel((val + offset) >> shift); + } + tmp += MAX_PB_SIZE; + dst += dst_stride; + } + +} + +static void FUNC(put_uni_luma_w_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, int height, + const int denom, const int wx, const int _ox, const int8_t *hf, const int8_t *vf, + const int width) +{ + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = hf; + const int ox = _ox * (1 << (BIT_DEPTH - 8)); + const int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel((((LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_uni_luma_w_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int height, + const int denom, const int wx, const int _ox, const int8_t *hf, const int8_t *vf, + const int width) +{ + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = vf; + const int ox = _ox * (1 << (BIT_DEPTH - 8)); + const int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel((((LUMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_uni_luma_w_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int height, const int denom, + const int wx, const int _ox, const int8_t *hf, const int8_t *vf, const int width) +{ + int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = hf; + const int ox = _ox * (1 << (BIT_DEPTH - 8)); + const int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + src -= LUMA_EXTRA_BEFORE * src_stride; + for (int y = 0; y < height + LUMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + LUMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = vf; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel((((LUMA_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx + offset) >> shift) + ox); + tmp += MAX_PB_SIZE; + dst += dst_stride; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +#define CHROMA_FILTER(src, stride) \ + (filter[0] * src[x - stride] + \ + filter[1] * src[x] + \ + filter[2] * src[x + stride] + \ + filter[3] * src[x + 2 * stride]) + +static void FUNC(put_chroma_h)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = hf; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_chroma_v)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = vf; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = CHROMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8); + src += src_stride; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_chroma_hv)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = hf; + + src -= CHROMA_EXTRA_BEFORE * src_stride; + + for (int y = 0; y < height + CHROMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + CHROMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = vf; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = CHROMA_FILTER(tmp, MAX_PB_SIZE) >> 6; + tmp += MAX_PB_SIZE; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_uni_chroma_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = hf; + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8)) + offset) >> shift); + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_uni_chroma_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = vf; + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((CHROMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8)) + offset) >> shift); + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_uni_chroma_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = hf; + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + src -= CHROMA_EXTRA_BEFORE * src_stride; + + for (int y = 0; y < height + CHROMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + CHROMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = vf; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((CHROMA_FILTER(tmp, MAX_PB_SIZE) >> 6) + offset) >> shift); + tmp += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(put_uni_chroma_w_h)(uint8_t *_dst, ptrdiff_t _dst_stride, + const uint8_t *_src, ptrdiff_t _src_stride, int height, int denom, int wx, int ox, + const int8_t *hf, const int8_t *vf, int width) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = hf; + const int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + ox = ox * (1 << (BIT_DEPTH - 8)); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + dst[x] = av_clip_pixel((((CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); + } + dst += dst_stride; + src += src_stride; + } +} + +static void FUNC(put_uni_chroma_w_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int height, + const int denom, const int wx, const int _ox, const int8_t *hf, const int8_t *vf, + const int width) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = vf; + const int shift = denom + 14 - BIT_DEPTH; + const int ox = _ox * (1 << (BIT_DEPTH - 8)); +#if BIT_DEPTH < 14 + int offset = 1 << (shift - 1); +#else + int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + dst[x] = av_clip_pixel((((CHROMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); + } + dst += dst_stride; + src += src_stride; + } +} + +static void FUNC(put_uni_chroma_w_hv)(uint8_t *_dst, ptrdiff_t _dst_stride, + const uint8_t *_src, ptrdiff_t _src_stride, int height, int denom, int wx, int ox, + const int8_t *hf, const int8_t *vf, int width) +{ + int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = hf; + const int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + src -= CHROMA_EXTRA_BEFORE * src_stride; + + for (int y = 0; y < height + CHROMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + CHROMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = vf; + + ox = ox * (1 << (BIT_DEPTH - 8)); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel((((CHROMA_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx + offset) >> shift) + ox); + tmp += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(avg)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const int16_t *src0, const int16_t *src1, const int width, const int height) +{ + pixel *dst = (pixel*)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int shift = FFMAX(3, 15 - BIT_DEPTH); + const int offset = 1 << (shift - 1); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel((src0[x] + src1[x] + offset) >> shift); + src0 += MAX_PB_SIZE; + src1 += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(w_avg)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const int16_t *src0, const int16_t *src1, const int width, const int height, + const int denom, const int w0, const int w1, const int o0, const int o1) +{ + pixel *dst = (pixel*)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int shift = denom + FFMAX(3, 15 - BIT_DEPTH); + const int offset = (((o0 + o1) << (BIT_DEPTH - 8)) + 1) << (shift - 1); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel((src0[x] * w0 + src1[x] * w1 + offset) >> shift); + src0 += MAX_PB_SIZE; + src1 += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(put_ciip)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const int width, const int height, + const uint8_t *_inter, const ptrdiff_t _inter_stride, const int intra_weight) +{ + pixel *dst = (pixel *)_dst; + pixel *inter = (pixel *)_inter; + const size_t dst_stride = _dst_stride / sizeof(pixel); + const size_t inter_stride = _inter_stride / sizeof(pixel); + const int inter_weight = 4 - intra_weight; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = (dst[x] * intra_weight + inter[x] * inter_weight + 2) >> 2; + dst += dst_stride; + inter += inter_stride; + } +} + +static void FUNC(put_gpm)(uint8_t *_dst, ptrdiff_t dst_stride, + const int width, const int height, + const int16_t *src0, const int16_t *src1, + const uint8_t *weights, const int step_x, const int step_y) +{ + const int shift = FFMAX(5, 17 - BIT_DEPTH); + const int offset = 1 << (shift - 1); + pixel *dst = (pixel *)_dst; + + dst_stride /= sizeof(pixel); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const uint8_t w = weights[x * step_x]; + dst[x] = av_clip_pixel((src0[x] * w + src1[x] * (8 - w) + offset) >> shift); + } + dst += dst_stride; + src0 += MAX_PB_SIZE; + src1 += MAX_PB_SIZE; + weights += step_y; + } +} + +//8.5.6.3.3 Luma integer sample fetching process, add one extra pad line +static void FUNC(bdof_fetch_samples)(int16_t *_dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int x_frac, const int y_frac, const int width, const int height) +{ + const int x_off = (x_frac >> 3) - 1; + const int y_off = (y_frac >> 3) - 1; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const pixel *src = (pixel*)_src + (x_off) + y_off * src_stride; + int16_t *dst = _dst - 1 - MAX_PB_SIZE; + const int shift = 14 - BIT_DEPTH; + const int bdof_width = width + 2 * BDOF_BORDER_EXT; + + // top + for (int i = 0; i < bdof_width; i++) + dst[i] = src[i] << shift; + + dst += MAX_PB_SIZE; + src += src_stride; + + for (int i = 0; i < height; i++) { + dst[0] = src[0] << shift; + dst[1 + width] = src[1 + width] << shift; + dst += MAX_PB_SIZE; + src += src_stride; + } + for (int i = 0; i < bdof_width; i++) + dst[i] = src[i] << shift; +} + +//8.5.6.3.3 Luma integer sample fetching process +static void FUNC(fetch_samples)(int16_t *_dst, const uint8_t *_src, const ptrdiff_t _src_stride, const int x_frac, const int y_frac) +{ + FUNC(bdof_fetch_samples)(_dst, _src, _src_stride, x_frac, y_frac, AFFINE_MIN_BLOCK_SIZE, AFFINE_MIN_BLOCK_SIZE); +} + +static void FUNC(prof_grad_filter)(int16_t *_gradient_h, int16_t *_gradient_v, const ptrdiff_t gradient_stride, + const int16_t *_src, const ptrdiff_t src_stride, const int width, const int height, const int pad) +{ + const int shift = 6; + const int16_t *src = _src; + int16_t *gradient_h = _gradient_h + pad * (1 + gradient_stride); + int16_t *gradient_v = _gradient_v + pad * (1 + gradient_stride); + + for (int y = 0; y < height; y++) { + const int16_t *p = src; + for (int x = 0; x < width; x++) { + gradient_h[x] = (p[1] >> shift) - (p[-1] >> shift); + gradient_v[x] = (p[src_stride] >> shift) - (p[-src_stride] >> shift); + p++; + } + gradient_h += gradient_stride; + gradient_v += gradient_stride; + src += src_stride; + } + if (pad) { + pad_int16(_gradient_h + 1 + gradient_stride, gradient_stride, width, height); + pad_int16(_gradient_v + 1 + gradient_stride, gradient_stride, width, height); + } +} + +static void FUNC(apply_prof)(int16_t *dst, const int16_t *src, const int16_t *diff_mv_x, const int16_t *diff_mv_y) +{ + const int limit = (1 << FFMAX(13, BIT_DEPTH + 1)); ///< dILimit + + int16_t gradient_h[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + int16_t gradient_v[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + FUNC(prof_grad_filter)(gradient_h, gradient_v, AFFINE_MIN_BLOCK_SIZE, src, MAX_PB_SIZE, AFFINE_MIN_BLOCK_SIZE, AFFINE_MIN_BLOCK_SIZE, 0); + + for (int y = 0; y < AFFINE_MIN_BLOCK_SIZE; y++) { + for (int x = 0; x < AFFINE_MIN_BLOCK_SIZE; x++) { + const int o = y * AFFINE_MIN_BLOCK_SIZE + x; + const int di = gradient_h[o] * diff_mv_x[o] + gradient_v[o] * diff_mv_y[o]; + const int val = src[x] + av_clip(di, -limit, limit - 1); + dst[x] = val; + + } + src += MAX_PB_SIZE; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(apply_prof_uni)(uint8_t *_dst, const ptrdiff_t _dst_stride, const int16_t *src, const int16_t *diff_mv_x, const int16_t *diff_mv_y) +{ + const int limit = (1 << FFMAX(13, BIT_DEPTH + 1)); ///< dILimit + pixel *dst = (pixel*)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + int16_t gradient_h[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + int16_t gradient_v[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + + FUNC(prof_grad_filter)(gradient_h, gradient_v, AFFINE_MIN_BLOCK_SIZE, src, MAX_PB_SIZE, AFFINE_MIN_BLOCK_SIZE, AFFINE_MIN_BLOCK_SIZE, 0); + + for (int y = 0; y < AFFINE_MIN_BLOCK_SIZE; y++) { + for (int x = 0; x < AFFINE_MIN_BLOCK_SIZE; x++) { + const int o = y * AFFINE_MIN_BLOCK_SIZE + x; + const int di = gradient_h[o] * diff_mv_x[o] + gradient_v[o] * diff_mv_y[o]; + const int val = src[x] + av_clip(di, -limit, limit - 1); + dst[x] = av_clip_pixel((val + offset) >> shift); + + } + src += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(apply_prof_uni_w)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const int16_t *src, const int16_t *diff_mv_x, const int16_t *diff_mv_y, + const int denom, const int wx, const int _ox) +{ + const int limit = (1 << FFMAX(13, BIT_DEPTH + 1)); ///< dILimit + pixel *dst = (pixel*)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int shift = denom + FFMAX(2, 14 - BIT_DEPTH); + const int offset = 1 << (shift - 1); + const int ox = _ox * (1 << (BIT_DEPTH - 8)); + int16_t gradient_h[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + int16_t gradient_v[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + + FUNC(prof_grad_filter)(gradient_h, gradient_v, AFFINE_MIN_BLOCK_SIZE, src, MAX_PB_SIZE, AFFINE_MIN_BLOCK_SIZE, AFFINE_MIN_BLOCK_SIZE, 0); + + for (int y = 0; y < AFFINE_MIN_BLOCK_SIZE; y++) { + for (int x = 0; x < AFFINE_MIN_BLOCK_SIZE; x++) { + const int o = y * AFFINE_MIN_BLOCK_SIZE + x; + const int di = gradient_h[o] * diff_mv_x[o] + gradient_v[o] * diff_mv_y[o]; + const int val = src[x] + av_clip(di, -limit, limit - 1); + dst[x] = av_clip_pixel(((val * wx + offset) >> shift) + ox); + } + src += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(derive_bdof_vx_vy)(const int16_t *_src0, const int16_t *_src1, + const int16_t **gradient_h, const int16_t **gradient_v, ptrdiff_t gradient_stride, + int* vx, int* vy) +{ + const int shift2 = 4; + const int shift3 = 1; + const int thres = 1 << 4; + int sgx2 = 0, sgy2 = 0, sgxgy = 0, sgxdi = 0, sgydi = 0; + const int16_t *src0 = _src0 - 1 - MAX_PB_SIZE; + const int16_t *src1 = _src1 - 1 - MAX_PB_SIZE; + + for (int y = 0; y < BDOF_GRADIENT_SIZE; y++) { + for (int x = 0; x < BDOF_GRADIENT_SIZE; x++) { + const int diff = (src0[x] >> shift2) - (src1[x] >> shift2); + const int idx = gradient_stride * y + x; + const int temph = (gradient_h[0][idx] + gradient_h[1][idx]) >> shift3; + const int tempv = (gradient_v[0][idx] + gradient_v[1][idx]) >> shift3; + sgx2 += FFABS(temph); + sgy2 += FFABS(tempv); + sgxgy += VVC_SIGN(tempv) * temph; + sgxdi += -VVC_SIGN(temph) * diff; + sgydi += -VVC_SIGN(tempv) * diff; + } + src0 += MAX_PB_SIZE; + src1 += MAX_PB_SIZE; + } + *vx = sgx2 > 0 ? av_clip((sgxdi << 2) >> av_log2(sgx2) , -thres + 1, thres - 1) : 0; + *vy = sgy2 > 0 ? av_clip(((sgydi << 2) - ((*vx * sgxgy) >> 1)) >> av_log2(sgy2), -thres + 1, thres - 1) : 0; +} + +static void FUNC(apply_bdof_min_block)(pixel* dst, const ptrdiff_t dst_stride, const int16_t *src0, const int16_t *src1, + const int16_t **gradient_h, const int16_t **gradient_v, const int vx, const int vy) +{ + const int shift4 = 15 - BIT_DEPTH; + const int offset4 = 1 << (shift4 - 1); + + const int16_t* gh[] = { gradient_h[0] + 1 + BDOF_PADDED_SIZE, gradient_h[1] + 1 + BDOF_PADDED_SIZE }; + const int16_t* gv[] = { gradient_v[0] + 1 + BDOF_PADDED_SIZE, gradient_v[1] + 1 + BDOF_PADDED_SIZE }; + + for (int y = 0; y < BDOF_BLOCK_SIZE; y++) { + for (int x = 0; x < BDOF_BLOCK_SIZE; x++) { + const int idx = y * BDOF_PADDED_SIZE + x; + const int bdof_offset = vx * (gh[0][idx] - gh[1][idx]) + vy * (gv[0][idx] - gv[1][idx]); + dst[x] = av_clip_pixel((src0[x] + offset4 + src1[x] + bdof_offset) >> shift4); + } + dst += dst_stride; + src0 += MAX_PB_SIZE; + src1 += MAX_PB_SIZE; + } +} + +static void FUNC(apply_bdof)(uint8_t *_dst, const ptrdiff_t _dst_stride, int16_t *_src0, int16_t *_src1, + const int block_w, const int block_h) +{ + int16_t gradient_h[2][BDOF_PADDED_SIZE * BDOF_PADDED_SIZE]; + int16_t gradient_v[2][BDOF_PADDED_SIZE * BDOF_PADDED_SIZE]; + int vx, vy; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + pixel* dst = (pixel*)_dst; + + FUNC(prof_grad_filter)(gradient_h[0], gradient_v[0], BDOF_PADDED_SIZE, + _src0, MAX_PB_SIZE, block_w, block_h, 1); + pad_int16(_src0, MAX_PB_SIZE, block_w, block_h); + FUNC(prof_grad_filter)(gradient_h[1], gradient_v[1], BDOF_PADDED_SIZE, + _src1, MAX_PB_SIZE, block_w, block_h, 1); + pad_int16(_src1, MAX_PB_SIZE, block_w, block_h); + + for (int y = 0; y < block_h; y += BDOF_BLOCK_SIZE) { + for (int x = 0; x < block_w; x += BDOF_BLOCK_SIZE) { + const int16_t* src0 = _src0 + y * MAX_PB_SIZE + x; + const int16_t* src1 = _src1 + y * MAX_PB_SIZE + x; + pixel *d = dst + x; + const int idx = BDOF_PADDED_SIZE * y + x; + const int16_t* gh[] = { gradient_h[0] + idx, gradient_h[1] + idx }; + const int16_t* gv[] = { gradient_v[0] + idx, gradient_v[1] + idx }; + FUNC(derive_bdof_vx_vy)(src0, src1, gh, gv, BDOF_PADDED_SIZE, &vx, &vy); + FUNC(apply_bdof_min_block)(d, dst_stride, src0, src1, gh, gv, vx, vy); + } + dst += BDOF_BLOCK_SIZE * dst_stride; + } +} + +#define DMVR_FILTER(src, stride) \ + (filter[0] * src[x] + \ + filter[1] * src[x + stride]) + +//8.5.3.2.2 Luma sample bilinear interpolation process +static void FUNC(dmvr)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); +#if BIT_DEPTH > 10 + const int shift4 = BIT_DEPTH - 10; + const int offset4 = 1 << (shift4 - 1); + #define DMVR_SHIFT(s) (((s) + offset4) >> shift4) +#else + #define DMVR_SHIFT(s) ((s) << (10 - BIT_DEPTH)) +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = DMVR_SHIFT(src[x]); + src += src_stride; + dst += MAX_PB_SIZE; + } +#undef DMVR_SHIFT +} + +//8.5.3.2.2 Luma sample bilinear interpolation process +static void FUNC(dmvr_h)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width) +{ + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_inter_luma_dmvr_filters[mx]; + const int shift1 = BIT_DEPTH - 6; + const int offset1 = 1 << (shift1 - 1); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = (DMVR_FILTER(src, 1) + offset1) >> shift1; + src += src_stride; + dst += MAX_PB_SIZE; + } +} + +//8.5.3.2.2 Luma sample bilinear interpolation process +static void FUNC(dmvr_v)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width) +{ + const pixel *src = (pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_inter_luma_dmvr_filters[my]; + const int shift1 = BIT_DEPTH - 6; + const int offset1 = 1 << (shift1 - 1); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = (DMVR_FILTER(src, src_stride) + offset1) >> shift1; + src += src_stride; + dst += MAX_PB_SIZE; + } + +} + +//8.5.3.2.2 Luma sample bilinear interpolation process +static void FUNC(dmvr_hv)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width) +{ + int16_t tmp_array[(MAX_PB_SIZE + BILINEAR_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_inter_luma_dmvr_filters[mx]; + const int shift1 = BIT_DEPTH - 6; + const int offset1 = 1 << (shift1 - 1); + const int shift2 = 4; + const int offset2 = 1 << (shift2 - 1); + + src -= BILINEAR_EXTRA_BEFORE * src_stride; + for (int y = 0; y < height + BILINEAR_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = (DMVR_FILTER(src, 1) + offset1) >> shift1; + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + BILINEAR_EXTRA_BEFORE * MAX_PB_SIZE; + filter = ff_vvc_inter_luma_dmvr_filters[my]; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = (DMVR_FILTER(tmp, MAX_PB_SIZE) + offset2) >> shift2; + tmp += MAX_PB_SIZE; + dst += MAX_PB_SIZE; + } +} + +#define PEL_FUNC(dst, C, idx1, idx2, a) \ + do { \ + for (int w = 0; w < 7; w++) \ + inter->dst[C][w][idx1][idx2] = FUNC(a); \ + } while (0) \ + +#define DIR_FUNCS(d, C, c) \ + PEL_FUNC(put_##d, C, 0, 0, put_##d##_pixels); \ + PEL_FUNC(put_##d, C, 0, 1, put_##d##_##c##_h); \ + PEL_FUNC(put_##d, C, 1, 0, put_##d##_##c##_v); \ + PEL_FUNC(put_##d, C, 1, 1, put_##d##_##c##_hv); \ + PEL_FUNC(put_##d##_w, C, 0, 0, put_##d##_w_pixels); \ + PEL_FUNC(put_##d##_w, C, 0, 1, put_##d##_##c##_w_h); \ + PEL_FUNC(put_##d##_w, C, 1, 0, put_##d##_##c##_w_v); \ + PEL_FUNC(put_##d##_w, C, 1, 1, put_##d##_##c##_w_hv); + +#define FUNCS(C, c) \ + PEL_FUNC(put, C, 0, 0, put_pixels); \ + PEL_FUNC(put, C, 0, 1, put_##c##_h); \ + PEL_FUNC(put, C, 1, 0, put_##c##_v); \ + PEL_FUNC(put, C, 1, 1, put_##c##_hv); \ + DIR_FUNCS(uni, C, c); \ + +static void FUNC(ff_vvc_inter_dsp_init)(VVCInterDSPContext *const inter) +{ + FUNCS(LUMA, luma); + FUNCS(CHROMA, chroma); + + inter->avg = FUNC(avg); + inter->w_avg = FUNC(w_avg); + + inter->dmvr[0][0] = FUNC(dmvr); + inter->dmvr[0][1] = FUNC(dmvr_h); + inter->dmvr[1][0] = FUNC(dmvr_v); + inter->dmvr[1][1] = FUNC(dmvr_hv); + + inter->put_ciip = FUNC(put_ciip); + inter->put_gpm = FUNC(put_gpm); + + inter->fetch_samples = FUNC(fetch_samples); + inter->bdof_fetch_samples = FUNC(bdof_fetch_samples); + inter->apply_prof = FUNC(apply_prof); + inter->apply_prof_uni = FUNC(apply_prof_uni); + inter->apply_prof_uni_w = FUNC(apply_prof_uni_w); + inter->apply_bdof = FUNC(apply_bdof); + inter->prof_grad_filter = FUNC(prof_grad_filter); + inter->sad = vvc_sad; +} + +#undef FUNCS +#undef PEL_FUNC +#undef DMVR_FUNCS diff --git a/libavcodec/vvc/vvcdec.h b/libavcodec/vvc/vvcdec.h index 746ee41661..89e11e9380 100644 --- a/libavcodec/vvc/vvcdec.h +++ b/libavcodec/vvc/vvcdec.h @@ -24,9 +24,11 @@ #ifndef AVCODEC_VVCDEC_H #define AVCODEC_VVCDEC_H +#include "libavcodec/videodsp.h" #include "libavcodec/vvc.h" #include "vvc_ps.h" +#include "vvcdsp.h" #define LUMA 0 #define CHROMA 1 @@ -103,6 +105,9 @@ typedef struct VVCFrameContext { VVCFrame *ref; + VVCDSPContext vvcdsp; + VideoDSPContext vdsp; + struct VVCFrameThread *ft; uint64_t decode_order; diff --git a/libavcodec/vvc/vvcdsp.h b/libavcodec/vvc/vvcdsp.h new file mode 100644 index 0000000000..3b35a25959 --- /dev/null +++ b/libavcodec/vvc/vvcdsp.h @@ -0,0 +1,170 @@ +/* + * VVC DSP + * + * Copyright (C) 2021 Nuo Mi + * + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VVCDSP_H +#define AVCODEC_VVCDSP_H + +#include +#include + +enum TxType { + DCT2, + DST7, + DCT8, + N_TX_TYPE, +}; + +enum TxSize { + TX_SIZE_2, + TX_SIZE_4, + TX_SIZE_8, + TX_SIZE_16, + TX_SIZE_32, + TX_SIZE_64, + N_TX_SIZE, +}; + +typedef struct VVCInterDSPContext { + void (*put[2 /* luma, chroma */][7 /* log2(width) - 1 */][2 /* int, frac */][2 /* int, frac */])( + int16_t *dst, const uint8_t *src, ptrdiff_t src_stride, int height, + const int8_t *hf, const int8_t *vf, int width); + + void (*put_uni[2 /* luma, chroma */][7 /* log2(width) - 1 */][2 /* int, frac */][2 /* int, frac */])( + uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, int height, + const int8_t *hf, const int8_t *vf, int width); + + void (*put_uni_w[2 /* luma, chroma */][7 /* log2(width) - 1 */][2 /* int, frac */][2 /* int, frac */])( + uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, int height, + int denom, int wx, int ox, const int8_t *hf, const int8_t *vf, int width); + + void (*avg)(uint8_t *dst, ptrdiff_t dst_stride, + const int16_t *src0, const int16_t *src1, int width, int height); + + void (*w_avg)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const int16_t *src0, const int16_t *src1, int width, int height, + int denom, int w0, int w1, int o0, int o1); + + void (*put_ciip)(uint8_t *dst, ptrdiff_t dst_stride, int width, int height, + const uint8_t *inter, ptrdiff_t inter_stride, int inter_weight); + + void (*put_gpm)(uint8_t *dst, ptrdiff_t dst_stride, int width, int height, + const int16_t *src0, const int16_t *src1, + const uint8_t *weights, int step_x, int step_y); + + void (*fetch_samples)(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride, int x_frac, int y_frac); + void (*bdof_fetch_samples)(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride, int x_frac, int y_frac, + int width, int height); + + void (*prof_grad_filter)(int16_t *gradient_h, int16_t *gradient_v, const ptrdiff_t gradient_stride, + const int16_t *src, const ptrdiff_t src_stride, int width, int height, const int pad); + void (*apply_prof)(int16_t *dst, const int16_t *src, const int16_t *diff_mv_x, const int16_t *diff_mv_y); + + void (*apply_prof_uni)(uint8_t *dst, ptrdiff_t dst_stride, const int16_t *src, + const int16_t *diff_mv_x, const int16_t *diff_mv_y); + void (*apply_prof_uni_w)(uint8_t *dst, const ptrdiff_t dst_stride, const int16_t *src, + const int16_t *diff_mv_x, const int16_t *diff_mv_y, int denom, int wx, int ox); + + void (*apply_bdof)(uint8_t *dst, ptrdiff_t dst_stride, int16_t *src0, int16_t *src1, int block_w, int block_h); + + int (*sad)(const int16_t *src0, const int16_t *src1, int dx, int dy, int block_w, int block_h); + void (*dmvr[2][2])(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride, int height, + intptr_t mx, intptr_t my, int width); +} VVCInterDSPContext; + +struct VVCLocalContext; + +typedef struct VVCIntraDSPContext { + void (*intra_cclm_pred)(const struct VVCLocalContext *lc, int x0, int y0, int w, int h); + void (*lmcs_scale_chroma)(struct VVCLocalContext *lc, int *dst, const int *coeff, int w, int h, int x0_cu, int y0_cu); + void (*intra_pred)(const struct VVCLocalContext *lc, int x0, int y0, int w, int h, int c_idx); + void (*pred_planar)(uint8_t *src, const uint8_t *top, const uint8_t *left, int w, int h, ptrdiff_t stride); + void (*pred_mip)(uint8_t *src, const uint8_t *top, const uint8_t *left, int w, int h, ptrdiff_t stride, + int mode_id, int is_transpose); + void (*pred_dc)(uint8_t *src, const uint8_t *top, const uint8_t *left, int w, int h, ptrdiff_t stride); + void (*pred_v)(uint8_t *src, const uint8_t *_top, int w, int h, ptrdiff_t stride); + void (*pred_h)(uint8_t *src, const uint8_t *_left, int w, int h, ptrdiff_t stride); + void (*pred_angular_v)(uint8_t *src, const uint8_t *_top, const uint8_t *_left, + int w, int h, ptrdiff_t stride, int c_idx, int mode, int ref_idx, int filter_flag, int need_pdpc); + void (*pred_angular_h)(uint8_t *src, const uint8_t *_top, const uint8_t *_left, int w, int h, ptrdiff_t stride, + int c_idx, int mode, int ref_idx, int filter_flag, int need_pdpc); +} VVCIntraDSPContext; + +typedef struct VVCItxDSPContext { + void (*add_residual)(uint8_t *dst, const int *res, int width, int height, ptrdiff_t stride); + void (*add_residual_joint)(uint8_t *dst, const int *res, int width, int height, ptrdiff_t stride, int c_sign, int shift); + void (*pred_residual_joint)(int *buf, int width, int height, int c_sign, int shift); + + void (*itx[N_TX_TYPE][N_TX_SIZE])(int *out, ptrdiff_t out_step, const int *in, ptrdiff_t in_step); + void (*transform_bdpcm)(int *coeffs, int width, int height, int vertical, int log2_transform_range); +} VVCItxDSPContext; + +typedef struct VVCLMCSDSPContext { + void (*filter)(uint8_t *dst, ptrdiff_t dst_stride, int width, int height, const uint8_t *lut); +} VVCLMCSDSPContext; + +typedef struct VVCLFDSPContext { + int (*ladf_level[2 /* h, v */])(const uint8_t *pix, ptrdiff_t stride); + + void (*filter_luma[2 /* h, v */])(uint8_t *pix, ptrdiff_t stride, const int32_t *beta, const int32_t *tc, + const uint8_t *no_p, const uint8_t *no_q, const uint8_t *max_len_p, const uint8_t *max_len_q, int hor_ctu_edge); + void (*filter_chroma[2 /* h, v */])(uint8_t *pix, ptrdiff_t stride, const int32_t *beta, const int32_t *tc, + const uint8_t *no_p, const uint8_t *no_q, const uint8_t *max_len_p, const uint8_t *max_len_q, int shift); +} VVCLFDSPContext; + +struct SAOParams; +typedef struct VVCSAODSPContext { + void (*band_filter[9])(uint8_t *dst, const uint8_t *src, ptrdiff_t dst_stride, ptrdiff_t src_stride, + const int16_t *sao_offset_val, int sao_left_class, int width, int height); + /* implicit src_stride parameter has value of 2 * MAX_PB_SIZE + AV_INPUT_BUFFER_PADDING_SIZE */ + void (*edge_filter[9])(uint8_t *dst /* align 16 */, const uint8_t *src /* align 32 */, ptrdiff_t dst_stride, + const int16_t *sao_offset_val, int sao_eo_class, int width, int height); + void (*edge_restore[2])(uint8_t *dst, const uint8_t *src, ptrdiff_t dst_stride, ptrdiff_t src_stride, + const struct SAOParams *sao, const int *borders, int width, int height, int c_idx, + const uint8_t *vert_edge, const uint8_t *horiz_edge, const uint8_t *diag_edge); +} VVCSAODSPContext; + +typedef struct VVCALFDSPContext { + void (*filter[2 /* luma, chroma */])(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, + int width, int height, const int16_t *filter, const int16_t *clip, int vb_pos); + void (*filter_cc)(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *luma, ptrdiff_t luma_stride, + int width, int height, int hs, int vs, const int16_t *filter, int vb_pos); + + void (*classify)(int *class_idx, int *transpose_idx, const uint8_t *src, ptrdiff_t src_stride, int width, int height, + int vb_pos, int *gradient_tmp); + void (*recon_coeff_and_clip)(int16_t *coeff, int16_t *clip, const int *class_idx, const int *transpose_idx, int size, + const int16_t *coeff_set, const uint8_t *clip_idx_set, const uint8_t *class_to_filt); +} VVCALFDSPContext; + +typedef struct VVCDSPContext { + VVCInterDSPContext inter; + VVCIntraDSPContext intra; + VVCItxDSPContext itx; + VVCLMCSDSPContext lmcs; + VVCLFDSPContext lf; + VVCSAODSPContext sao; + VVCALFDSPContext alf; +} VVCDSPContext; + +void ff_vvc_dsp_init(VVCDSPContext *hpc, int bit_depth); + +#endif /* AVCODEC_VVCDSP_H */ From patchwork Sun Nov 12 10:35:20 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 44626 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:92a5:b0:181:818d:5e7f with SMTP id q37csp728374pzg; Sun, 12 Nov 2023 02:37:24 -0800 (PST) X-Google-Smtp-Source: AGHT+IGgJAAy5/B2ae+hCD0MwpBkcoiIuwdoY+bVvPfy4h2IqTPTv+w4WvHAnTO83Nrv8Skip0YX X-Received: by 2002:a05:6402:268d:b0:541:1e0d:b56f with SMTP id w13-20020a056402268d00b005411e0db56fmr8512087edd.12.1699785443691; Sun, 12 Nov 2023 02:37:23 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id p23-20020a50cd97000000b00544f56a46f3si1551711edi.604.2023.11.12.02.37.23; Sun, 12 Nov 2023 02:37:23 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@outlook.com header.s=selector1 header.b=eYQwW2fL; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 53EBE68C851; Sun, 12 Nov 2023 12:36:23 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from APC01-SG2-obe.outbound.protection.outlook.com (mail-sgaapc01olkn2035.outbound.protection.outlook.com [40.92.53.35]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 3228F68CC5D for ; Sun, 12 Nov 2023 12:36:19 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=IS11lWTgTJuXiMECLl0kV1UOc6XPhSVEnz/DivDzG9nsCMXIOX39QhasoYOfAK9vZBGBf+S0OEDWiuuBpKfSOu7Lr0em89oOizSh5QN/dET3Sw785eC79jkMPjhdaRB6REOhoLLOxSm4cgrIxfoDm/W1rF+exEtSmzystcX7fjlM8wq5fbMWCccCTBhTfa6oU+eHpahCeInb92ltowmFk9UuqedPGf5wb/YNDCRw7vvvAVckaEfPGbiLjGc4vGUfZbzaYM7AZfJ/VIg0xNwZO3fVQvh9iqXQDbo9+sdx8+l0/q0GW4/ZvEgw9wzrTtstcBPU5/4szDbDEh5PSDDJeA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=YkmaRXaMgkRHmu8EGLuVs8+eazGM3fkmtMC7hepFMN8=; b=nPriVL3NbYPMnb5WPQWuKe77Ndx4uyYFoqmOg+zizY8JgpN7tlCtU+tRU0ZE8ZyvOH79jMikaN0knqsnlMe9IJTxiEGnIrNB9TJjIKWlp50IQ4zH8fUczmQY8f9HOIWqnZn755GE46I4tD6zQyD/ZxRda9wwYfw3UYBXeMF1mzyBvA3bOd5WS/1nFQy+ohDTGVvSuIywOQKQxzlyIioSHo2Juk5nPtxuDWQt5howN+jE31cC+67laxFazu1QuWTVov+SRvtB83FEJXoygNd0OVh3e5I9FZzNAQwjSWx4RPKoaVEQ8v/0IEj4UtrpdmA2vntohhCFwPmiesZu2hRPWg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=outlook.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=YkmaRXaMgkRHmu8EGLuVs8+eazGM3fkmtMC7hepFMN8=; b=eYQwW2fLpc4W12qrUYDT17Zy0YW+H4sAxviVFkmgeZazDDopkTPbd41cp7gZSdjQWHLiw1gA73oZp4DjfSqTRlZK6Jy5kDyZ33YVZxACUy8byVHLEy6mqal5AVINuMrrulqqZPggE3/zuAk999QnvQ1mZTIw+Pw7gxG2WjSeJykeQNPe9bLuj6Pr3DzGZe2mCD9E98Q3UOo0ClzVaKfEgYQDOvNCspV5EuKqzKqAiw2nWP+aGZ8l3WybLecJTPHlV/cGd3ac+w6WKn5BpgbFCwBkNEW51s3DAO+nOpp9fl3hwFjZ8XnjpnFMKiLJT7mg6wgvHNb/UxsLGQO2033gCQ== Received: from TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) by TYSPR06MB6768.apcprd06.prod.outlook.com (2603:1096:400:475::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6954.29; Sun, 12 Nov 2023 10:35:59 +0000 Received: from TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f]) by TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f%4]) with mapi id 15.20.6954.028; Sun, 12 Nov 2023 10:35:59 +0000 From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Nov 2023 18:35:20 +0800 Message-ID: X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231112103526.11245-1-nuomi2021@gmail.com> References: <20231112103526.11245-1-nuomi2021@gmail.com> X-TMN: [3Fjvett+OjMgwpBLcOHSsH8AHMt/CYIL] X-ClientProxiedBy: TYWP286CA0030.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:262::20) To TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) X-Microsoft-Original-Message-ID: <20231112103526.11245-8-nuomi2021@gmail.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 2 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: TYSPR06MB6433:EE_|TYSPR06MB6768:EE_ X-MS-Office365-Filtering-Correlation-Id: 0548e8b4-8509-4db2-288a-08dbe36b2926 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 8XrgxzlUDGShfO/W+VnmkvTrGPmwD91tiGZnEBtmJd7gpvI3uuIqVQ8/qi+hNaatEN0Fdb3EpP/L4Y/tH1Gr9rllE3PBwKRt/KJfWnHi2zyXZ+0K561HF0nfrj4J4TFZjh/jMN074OYoA7zCuzpMSD2gftP2X392L1h4VZTypqnEjUJ5IBarrT8HKjf26FazaMp/a5AhfDH6DIOPre82ItNI13F+9PCgphNizyrCbgZh2eU/uQunWkL9Mm4A6VtfrTYpnNx8Wav3CWS2IEiPmwahlYJlv5fTD10lV4FjmzejG4z9kqBlmnjhCLBh6E7Vt6RP46UimyA+C6LZikeLPhWBSUZnoVeGs9/XgcgOGF9LEdWWHG+cDWo+g7xMjMR+Aa0lzsEyQp+X8T7C5TfdI9t2tLdYyQ4IAEQo5oQTQrkSGv+dIHTgIWCUIu32oP0ohxGbD/8hsF5Z5Cec8PcwH+xQvnQ45qMxGzm07BfQNIMMFIM+v1W3vsIbkal8fPMAuBo8HYLIMqgx4794loKNuihtl1omamSwFW+LhNkdSgmEGcxjcAqXtyAO9aXqn9PwQIwgul8ISVphNxuQ1aPU/fHbYZ5DAy5ktsJHP1xpWIvhCmleCO5hZx0HRNOn9Yyh X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: FqgSZwTzmOel01LqKJbI0IMG97XejI4fkBMbszIVoeBOXGZJNlsAEmZyz9IL1RvbDzbzJ90sRv1s0BuC6P0aklZaxwp5ku1Hms4E15+tqMIOpDPrzD8iL+z0qrkqzgaJvALm96fxcXh10c7UbYhSjNkK/XR2gv7EEfCjVa3jbz1edOyoMyj9UK5n866y5OoaUGwLbe7yYCWBZeKVU33giO3zw/sHlEhy72OUVihUpnhFDr+BbVVNkG/VIjisbvvM07DHSQcRy35zemXjAsKRS4gqxhjJ+g6bUB84kdJ55/HMoIi6j2T5+oL7ULKaJWjjaff0s/5aQ5RYoJJVqUXmoGIdiXqEhCQ2O/czQQ76ouN9AbiRSs2tE+WtPUwWzqus7qRiISKiWBwQ0DKDELAn2HKZXL83D4qZlGrt14eXGdjXL+xSR+mlQDSaeboovmi8JWRVSI3fs1GfvKKd3PCIvXA3M3P0thjeQ5Hf02hnwtt89cnEs/hn8/sAL6OsRq7OIt3f5anhe4KvhDOAyXvOlDCfe/E8xW8XDwet8u0zQiQ+3yAoas+thh4DtQbQ2Rw4sSMlho/6d7sQ4PceJPg0UalutDkKGvlacdgeRpY6VJYFn5n51dN7HnHdI0IP25SSMKBjp6ejLWQaqjZVOmwB0q9Wq6lEYny5V6oXZFg7Kq4iQEo0il77S3oKs7C7zF5yPQDweE6IXAZgAHJ/cSYzEs9tRabQM5wtReJU3UOWm6va0v/3NRqzeirKqh4PKoLxqt2KFC1YJfv3OB657K8HPOEmFbmIYFtJsScz/6M6Bc08pHfRL6NTpZxrKFAnT2Udjru7GPRFiXvoK4GKYb/21UVuneGHhoNssr4p+QG8rit+TWO+8ZnYbmNEiO3V+6xL8ByCo1BlxQtjvFsrH/o3+ZfdMi2jhMYB3CgbZYOQ0qi1pRGoF0ebKeetQGP2Q2ai3tvTv7D2qBvrOR3CBpD9+X1qvVZRJxGq5JILevoB0/JTklYSzBBC3IlpC9sZVL0FfsA2D7H/tddw18YsQK1ENpMZqdr8dBQRqbCmEvp+cYQlWz4ePSf0/CNGq1HBaPc5XCUwoNmXCEvtZeaAHKuyOdDPHAiG+weEQI55MRJNq6/Xeo7Zvqyj+UGPUlVNOK0JYCF9fKJUUg5TOK1KUHXBmu24sRRn07a2wCYeK7PUPOklqbkEOPAci8VP2vZpfZe1JuncXoTG+mhLyE5QfEdS75kHdhHlLMjAJwOjpPtlhUN7wC86SPDa/QBfolL4m8VA X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 0548e8b4-8509-4db2-288a-08dbe36b2926 X-MS-Exchange-CrossTenant-AuthSource: TYSPR06MB6433.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Nov 2023 10:35:59.7073 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYSPR06MB6768 Subject: [FFmpeg-devel] [PATCH v5 08/14] vvcdec: add inv transform 1d X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Nuo Mi Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: gvp+/g/+EI9d --- libavcodec/vvc/Makefile | 1 + libavcodec/vvc/vvc_itx_1d.c | 713 ++++++++++++++++++++++++++++++++++++ libavcodec/vvc/vvc_itx_1d.h | 52 +++ 3 files changed, 766 insertions(+) create mode 100644 libavcodec/vvc/vvc_itx_1d.c create mode 100644 libavcodec/vvc/vvc_itx_1d.h diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile index 61b1ab39de..5796f9ad42 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -6,6 +6,7 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ vvc/vvc_ctu.o \ vvc/vvc_data.o \ vvc/vvc_inter.o \ + vvc/vvc_itx_1d.o \ vvc/vvc_mvs.o \ vvc/vvc_ps.o \ vvc/vvc_refs.o \ diff --git a/libavcodec/vvc/vvc_itx_1d.c b/libavcodec/vvc/vvc_itx_1d.c new file mode 100644 index 0000000000..e96919f41d --- /dev/null +++ b/libavcodec/vvc/vvc_itx_1d.c @@ -0,0 +1,713 @@ +/* + * VVC 1D transform + * + * Copyright (C) 2023 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* 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-2021, 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. + */ + +/* optimizaed with partial butterfly, see Hung C-Y, Landman P (1997) + Compact inverse discrete cosine transform circuit for MPEG video decoding. + */ + +#include "vvc_data.h" +#include "vvc_itx_1d.h" +#include "libavutil/avutil.h" + +/* +transmatrix[2][2] = { + { a, a }, + { a, -a }, +} + */ +void ff_vvc_inv_dct2_2(int *out, const ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) +{ + const int a = 64; + const int x0 = in[0 * in_stride], x1 = in[1 * in_stride]; + + out[0 * out_stride] = a * (x0 + x1); + out[1 * out_stride] = a * (x0 - x1); +} + +/* +transmatrix[4][4] = { + { a, a, a, a}, + { b, c, -c, -b}, + { a, -a, -a, a}, + { c, -b, b, -c}, +} + */ +void ff_vvc_inv_dct2_4(int *out, const ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) +{ + const int a = 64, b = 83, c = 36; + const int x0 = in[0 * in_stride], x1 = in[1 * in_stride]; + const int x2 = in[2 * in_stride], x3 = in[3 * in_stride]; + const int E[2] = { + a * (x0 + x2), + a * (x0 - x2), + }; + const int O[2] = { + b * x1 + c * x3, + c * x1 - b * x3, + }; + + out[0 * out_stride] = E[0] + O[0]; + out[1 * out_stride] = E[1] + O[1]; + out[2 * out_stride] = E[1] - O[1]; + out[3 * out_stride] = E[0] - O[0]; +} + +/* +transmatrix[8][8] = { + { a, a, a, a, a, a, a, a}, + { d, e, f, g, -g, -f, -e, -d}, + { b, c, -c, -b, -b, -c, c, b}, + { e, -g, -d, -f, f, d, g, -e}, + { a, -a, -a, a, a, -a, -a, a}, + { f, -d, g, e, -e, -g, d, -f}, + { c, -b, b, -c, -c, b, -b, c}, + { g, -f, e, -d, d, -e, f, -g}, +} + */ +void ff_vvc_inv_dct2_8(int *out, const ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) +{ + const int a = 64, b = 83, c = 36, d = 89, e = 75, f = 50, g = 18; + const int x0 = in[0 * in_stride], x1 = in[1 * in_stride]; + const int x2 = in[2 * in_stride], x3 = in[3 * in_stride]; + const int x4 = in[4 * in_stride], x5 = in[5 * in_stride]; + const int x6 = in[6 * in_stride], x7 = in[7 * in_stride]; + const int EE[2] = { + a * (x0 + x4), + a * (x0 - x4), + }; + const int EO[2] = { + b * x2 + c * x6, + c * x2 - b * x6, + }; + const int E[4] = { + EE[0] + EO[0], EE[1] + EO[1], + EE[1] - EO[1], EE[0] - EO[0], + }; + const int O[4] = { + d * x1 + e * x3 + f * x5 + g * x7, + e * x1 - g * x3 - d * x5 - f * x7, + f * x1 - d * x3 + g * x5 + e * x7, + g * x1 - f * x3 + e * x5 - d * x7, + }; + + out[0 * out_stride] = E[0] + O[0]; + out[1 * out_stride] = E[1] + O[1]; + out[2 * out_stride] = E[2] + O[2]; + out[3 * out_stride] = E[3] + O[3]; + out[4 * out_stride] = E[3] - O[3]; + out[5 * out_stride] = E[2] - O[2]; + out[6 * out_stride] = E[1] - O[1]; + out[7 * out_stride] = E[0] - O[0]; +} + +/* +transmatrix[16][16] = { + { a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a}, + { h, i, j, k, l, m, n, o, -o, -n, -m, -l, -k, -j, -i, -h}, + { d, e, f, g, -g, -f, -e, -d, -d, -e, -f, -g, g, f, e, d}, + { i, l, o, -m, -j, -h, -k, -n, n, k, h, j, m, -o, -l, -i}, + { b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b}, + { j, o, -k, -i, -n, l, h, m, -m, -h, -l, n, i, k, -o, -j}, + { e, -g, -d, -f, f, d, g, -e, -e, g, d, f, -f, -d, -g, e}, + { k, -m, -i, o, h, n, -j, -l, l, j, -n, -h, -o, i, m, -k}, + { a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a}, + { l, -j, -n, h, -o, -i, m, k, -k, -m, i, o, -h, n, j, -l}, + { f, -d, g, e, -e, -g, d, -f, -f, d, -g, -e, e, g, -d, f}, + { m, -h, l, n, -i, k, o, -j, j, -o, -k, i, -n, -l, h, -m}, + { c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c}, + { n, -k, h, -j, m, o, -l, i, -i, l, -o, -m, j, -h, k, -n}, + { g, -f, e, -d, d, -e, f, -g, -g, f, -e, d, -d, e, -f, g}, + { o, -n, m, -l, k, -j, i, -h, h, -i, j, -k, l, -m, n, -o}, +} + */ +void ff_vvc_inv_dct2_16(int *out, const ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) +{ + const int a = 64, b = 83, c = 36, d = 89, e = 75, f = 50, g = 18, h = 90; + const int i = 87, j = 80, k = 70, l = 57, m = 43, n = 25, o = 9; + const int x0 = in[0 * in_stride], x1 = in[1 * in_stride]; + const int x2 = in[2 * in_stride], x3 = in[3 * in_stride]; + const int x4 = in[4 * in_stride], x5 = in[5 * in_stride]; + const int x6 = in[6 * in_stride], x7 = in[7 * in_stride]; + const int x8 = in[8 * in_stride], x9 = in[9 * in_stride]; + const int x10 = in[10 * in_stride], x11 = in[11 * in_stride]; + const int x12 = in[12 * in_stride], x13 = in[13 * in_stride]; + const int x14 = in[14 * in_stride], x15 = in[15 * in_stride]; + const int EEE[2] = { + a * (x0 + x8), + a * (x0 - x8), + }; + const int EEO[2] = { + b * x4 + c * x12, + c * x4 - b * x12, + }; + const int EE[4] = { + EEE[0] + EEO[0], EEE[1] + EEO[1], + EEE[1] - EEO[1], EEE[0] - EEO[0], + }; + const int EO[4] = { + d * x2 + e * x6 + f * x10 + g * x14, + e * x2 - g * x6 - d * x10 - f * x14, + f * x2 - d * x6 + g * x10 + e * x14, + g * x2 - f * x6 + e * x10 - d * x14, + }; + const int E[8] = { + EE[0] + EO[0], EE[1] + EO[1], EE[2] + EO[2], EE[3] + EO[3], + EE[3] - EO[3], EE[2] - EO[2], EE[1] - EO[1], EE[0] - EO[0], + }; + const int O[8] = { + h * x1 + i * x3 + j * x5 + k * x7 + l * x9 + m * x11 + n * x13 + o * x15, + i * x1 + l * x3 + o * x5 - m * x7 - j * x9 - h * x11 - k * x13 - n * x15, + j * x1 + o * x3 - k * x5 - i * x7 - n * x9 + l * x11 + h * x13 + m * x15, + k * x1 - m * x3 - i * x5 + o * x7 + h * x9 + n * x11 - j * x13 - l * x15, + l * x1 - j * x3 - n * x5 + h * x7 - o * x9 - i * x11 + m * x13 + k * x15, + m * x1 - h * x3 + l * x5 + n * x7 - i * x9 + k * x11 + o * x13 - j * x15, + n * x1 - k * x3 + h * x5 - j * x7 + m * x9 + o * x11 - l * x13 + i * x15, + o * x1 - n * x3 + m * x5 - l * x7 + k * x9 - j * x11 + i * x13 - h * x15, + }; + + out[0 * out_stride] = E[0] + O[0]; + out[1 * out_stride] = E[1] + O[1]; + out[2 * out_stride] = E[2] + O[2]; + out[3 * out_stride] = E[3] + O[3]; + out[4 * out_stride] = E[4] + O[4]; + out[5 * out_stride] = E[5] + O[5]; + out[6 * out_stride] = E[6] + O[6]; + out[7 * out_stride] = E[7] + O[7]; + out[8 * out_stride] = E[7] - O[7]; + out[9 * out_stride] = E[6] - O[6]; + out[10 * out_stride] = E[5] - O[5]; + out[11 * out_stride] = E[4] - O[4]; + out[12 * out_stride] = E[3] - O[3]; + out[13 * out_stride] = E[2] - O[2]; + out[14 * out_stride] = E[1] - O[1]; + out[15 * out_stride] = E[0] - O[0]; +} + +/* +transMatrix[32][32] = { + { a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a}, + { p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, -E, -D, -C, -B, -A, -z, -y, -x, -w, -v, -u, -t, -s, -r, -q, -p}, + { h, i, j, k, l, m, n, o, -o, -n, -m, -l, -k, -j, -i, -h, -h, -i, -j, -k, -l, -m, -n, -o, o, n, m, l, k, j, i, h}, + { q, t, w, z, C, -E, -B, -y, -v, -s, -p, -r, -u, -x, -A, -D, D, A, x, u, r, p, s, v, y, B, E, -C, -z, -w, -t, -q}, + { d, e, f, g, -g, -f, -e, -d, -d, -e, -f, -g, g, f, e, d, d, e, f, g, -g, -f, -e, -d, -d, -e, -f, -g, g, f, e, d}, + { r, w, B, -D, -y, -t, -p, -u, -z, -E, A, v, q, s, x, C, -C, -x, -s, -q, -v, -A, E, z, u, p, t, y, D, -B, -w, -r}, + { i, l, o, -m, -j, -h, -k, -n, n, k, h, j, m, -o, -l, -i, -i, -l, -o, m, j, h, k, n, -n, -k, -h, -j, -m, o, l, i}, + { s, z, -D, -w, -p, -v, -C, A, t, r, y, -E, -x, -q, -u, -B, B, u, q, x, E, -y, -r, -t, -A, C, v, p, w, D, -z, -s}, + { b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b}, + { t, C, -y, -p, -x, D, u, s, B, -z, -q, -w, E, v, r, A, -A, -r, -v, -E, w, q, z, -B, -s, -u, -D, x, p, y, -C, -t}, + { j, o, -k, -i, -n, l, h, m, -m, -h, -l, n, i, k, -o, -j, -j, -o, k, i, n, -l, -h, -m, m, h, l, -n, -i, -k, o, j}, + { u, -E, -t, -v, D, s, w, -C, -r, -x, B, q, y, -A, -p, -z, z, p, A, -y, -q, -B, x, r, C, -w, -s, -D, v, t, E, -u}, + { e, -g, -d, -f, f, d, g, -e, -e, g, d, f, -f, -d, -g, e, e, -g, -d, -f, f, d, g, -e, -e, g, d, f, -f, -d, -g, e}, + { v, -B, -p, -C, u, w, -A, -q, -D, t, x, -z, -r, -E, s, y, -y, -s, E, r, z, -x, -t, D, q, A, -w, -u, C, p, B, -v}, + { k, -m, -i, o, h, n, -j, -l, l, j, -n, -h, -o, i, m, -k, -k, m, i, -o, -h, -n, j, l, -l, -j, n, h, o, -i, -m, k}, + { w, -y, -u, A, s, -C, -q, E, p, D, -r, -B, t, z, -v, -x, x, v, -z, -t, B, r, -D, -p, -E, q, C, -s, -A, u, y, -w}, + { a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a}, + { x, -v, -z, t, B, -r, -D, p, -E, -q, C, s, -A, -u, y, w, -w, -y, u, A, -s, -C, q, E, -p, D, r, -B, -t, z, v, -x}, + { l, -j, -n, h, -o, -i, m, k, -k, -m, i, o, -h, n, j, -l, -l, j, n, -h, o, i, -m, -k, k, m, -i, -o, h, -n, -j, l}, + { y, -s, -E, r, -z, -x, t, D, -q, A, w, -u, -C, p, -B, -v, v, B, -p, C, u, -w, -A, q, -D, -t, x, z, -r, E, s, -y}, + { f, -d, g, e, -e, -g, d, -f, -f, d, -g, -e, e, g, -d, f, f, -d, g, e, -e, -g, d, -f, -f, d, -g, -e, e, g, -d, f}, + { z, -p, A, y, -q, B, x, -r, C, w, -s, D, v, -t, E, u, -u, -E, t, -v, -D, s, -w, -C, r, -x, -B, q, -y, -A, p, -z}, + { m, -h, l, n, -i, k, o, -j, j, -o, -k, i, -n, -l, h, -m, -m, h, -l, -n, i, -k, -o, j, -j, o, k, -i, n, l, -h, m}, + { A, -r, v, -E, -w, q, -z, -B, s, -u, D, x, -p, y, C, -t, t, -C, -y, p, -x, -D, u, -s, B, z, -q, w, E, -v, r, -A}, + { c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c}, + { B, -u, q, -x, E, y, -r, t, -A, -C, v, -p, w, -D, -z, s, -s, z, D, -w, p, -v, C, A, -t, r, -y, -E, x, -q, u, -B}, + { n, -k, h, -j, m, o, -l, i, -i, l, -o, -m, j, -h, k, -n, -n, k, -h, j, -m, -o, l, -i, i, -l, o, m, -j, h, -k, n}, + { C, -x, s, -q, v, -A, -E, z, -u, p, -t, y, -D, -B, w, -r, r, -w, B, D, -y, t, -p, u, -z, E, A, -v, q, -s, x, -C}, + { g, -f, e, -d, d, -e, f, -g, -g, f, -e, d, -d, e, -f, g, g, -f, e, -d, d, -e, f, -g, -g, f, -e, d, -d, e, -f, g}, + { D, -A, x, -u, r, -p, s, -v, y, -B, E, C, -z, w, -t, q, -q, t, -w, z, -C, -E, B, -y, v, -s, p, -r, u, -x, A, -D}, + { o, -n, m, -l, k, -j, i, -h, h, -i, j, -k, l, -m, n, -o, -o, n, -m, l, -k, j, -i, h, -h, i, -j, k, -l, m, -n, o}, + { E, -D, C, -B, A, -z, y, -x, w, -v, u, -t, s, -r, q, -p, p, -q, r, -s, t, -u, v, -w, x, -y, z, -A, B, -C, D, -E}, +} + */ +void ff_vvc_inv_dct2_32(int *out, const ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) +{ + const int a = 64, b = 83, c = 36, d = 89, e = 75, f = 50, g = 18, h = 90; + const int i = 87, j = 80, k = 70, l = 57, m = 43, n = 25, o = 9, p = 90; + const int q = 90, r = 88, s = 85, t = 82, u = 78, v = 73, w = 67, x = 61; + const int y = 54, z = 46, A = 38, B = 31, C = 22, D = 13, E_= 4; + const int x0 = in[0 * in_stride], x1 = in[1 * in_stride]; + const int x2 = in[2 * in_stride], x3 = in[3 * in_stride]; + const int x4 = in[4 * in_stride], x5 = in[5 * in_stride]; + const int x6 = in[6 * in_stride], x7 = in[7 * in_stride]; + const int x8 = in[8 * in_stride], x9 = in[9 * in_stride]; + const int x10 = in[10 * in_stride], x11 = in[11 * in_stride]; + const int x12 = in[12 * in_stride], x13 = in[13 * in_stride]; + const int x14 = in[14 * in_stride], x15 = in[15 * in_stride]; + const int x16 = in[16 * in_stride], x17 = in[17 * in_stride]; + const int x18 = in[18 * in_stride], x19 = in[19 * in_stride]; + const int x20 = in[20 * in_stride], x21 = in[21 * in_stride]; + const int x22 = in[22 * in_stride], x23 = in[23 * in_stride]; + const int x24 = in[24 * in_stride], x25 = in[25 * in_stride]; + const int x26 = in[26 * in_stride], x27 = in[27 * in_stride]; + const int x28 = in[28 * in_stride], x29 = in[29 * in_stride]; + const int x30 = in[30 * in_stride], x31 = in[31 * in_stride]; + const int EEEE[2] = { + a * (x0 + x16), + a * (x0 - x16), + }; + const int EEEO[2] = { + b * x8 + c * x24, + c * x8 - b * x24, + }; + const int EEE[4] = { + EEEE[0] + EEEO[0], EEEE[1] + EEEO[1], + EEEE[1] - EEEO[1], EEEE[0] - EEEO[0], + }; + const int EEO[4] = { + d * x4 + e * x12 + f * x20 + g * x28, + e * x4 - g * x12 - d * x20 - f * x28, + f * x4 - d * x12 + g * x20 + e * x28, + g * x4 - f * x12 + e * x20 - d * x28, + }; + const int EE[8] = { + EEE[0] + EEO[0], EEE[1] + EEO[1], EEE[2] + EEO[2], EEE[3] + EEO[3], + EEE[3] - EEO[3], EEE[2] - EEO[2], EEE[1] - EEO[1], EEE[0] - EEO[0], + }; + const int EO[8] = { + h * x2 + i * x6 + j * x10 + k * x14 + l * x18 + m * x22 + n * x26 + o * x30, + i * x2 + l * x6 + o * x10 - m * x14 - j * x18 - h * x22 - k * x26 - n * x30, + j * x2 + o * x6 - k * x10 - i * x14 - n * x18 + l * x22 + h * x26 + m * x30, + k * x2 - m * x6 - i * x10 + o * x14 + h * x18 + n * x22 - j * x26 - l * x30, + l * x2 - j * x6 - n * x10 + h * x14 - o * x18 - i * x22 + m * x26 + k * x30, + m * x2 - h * x6 + l * x10 + n * x14 - i * x18 + k * x22 + o * x26 - j * x30, + n * x2 - k * x6 + h * x10 - j * x14 + m * x18 + o * x22 - l * x26 + i * x30, + o * x2 - n * x6 + m * x10 - l * x14 + k * x18 - j * x22 + i * x26 - h * x30, + }; + const int E[16] = { + EE[0] + EO[0], EE[1] + EO[1], EE[2] + EO[2], EE[3] + EO[3], EE[4] + EO[4], EE[5] + EO[5], EE[6] + EO[6], EE[7] + EO[7], + EE[7] - EO[7], EE[6] - EO[6], EE[5] - EO[5], EE[4] - EO[4], EE[3] - EO[3], EE[2] - EO[2], EE[1] - EO[1], EE[0] - EO[0], + }; + const int O[16] = { + p * x1 + q * x3 + r * x5 + s * x7 + t * x9 + u * x11 + v * x13 + w * x15 + x * x17 + y * x19 + z * x21 + A * x23 + B * x25 + C * x27 + D * x29 + E_* x31, + q * x1 + t * x3 + w * x5 + z * x7 + C * x9 - E_* x11 - B * x13 - y * x15 - v * x17 - s * x19 - p * x21 - r * x23 - u * x25 - x * x27 - A * x29 - D * x31, + r * x1 + w * x3 + B * x5 - D * x7 - y * x9 - t * x11 - p * x13 - u * x15 - z * x17 - E_* x19 + A * x21 + v * x23 + q * x25 + s * x27 + x * x29 + C * x31, + s * x1 + z * x3 - D * x5 - w * x7 - p * x9 - v * x11 - C * x13 + A * x15 + t * x17 + r * x19 + y * x21 - E_* x23 - x * x25 - q * x27 - u * x29 - B * x31, + t * x1 + C * x3 - y * x5 - p * x7 - x * x9 + D * x11 + u * x13 + s * x15 + B * x17 - z * x19 - q * x21 - w * x23 + E_* x25 + v * x27 + r * x29 + A * x31, + u * x1 - E_* x3 - t * x5 - v * x7 + D * x9 + s * x11 + w * x13 - C * x15 - r * x17 - x * x19 + B * x21 + q * x23 + y * x25 - A * x27 - p * x29 - z * x31, + v * x1 - B * x3 - p * x5 - C * x7 + u * x9 + w * x11 - A * x13 - q * x15 - D * x17 + t * x19 + x * x21 - z * x23 - r * x25 - E_* x27 + s * x29 + y * x31, + w * x1 - y * x3 - u * x5 + A * x7 + s * x9 - C * x11 - q * x13 + E_* x15 + p * x17 + D * x19 - r * x21 - B * x23 + t * x25 + z * x27 - v * x29 - x * x31, + x * x1 - v * x3 - z * x5 + t * x7 + B * x9 - r * x11 - D * x13 + p * x15 - E_* x17 - q * x19 + C * x21 + s * x23 - A * x25 - u * x27 + y * x29 + w * x31, + y * x1 - s * x3 - E_* x5 + r * x7 - z * x9 - x * x11 + t * x13 + D * x15 - q * x17 + A * x19 + w * x21 - u * x23 - C * x25 + p * x27 - B * x29 - v * x31, + z * x1 - p * x3 + A * x5 + y * x7 - q * x9 + B * x11 + x * x13 - r * x15 + C * x17 + w * x19 - s * x21 + D * x23 + v * x25 - t * x27 + E_* x29 + u * x31, + A * x1 - r * x3 + v * x5 - E_* x7 - w * x9 + q * x11 - z * x13 - B * x15 + s * x17 - u * x19 + D * x21 + x * x23 - p * x25 + y * x27 + C * x29 - t * x31, + B * x1 - u * x3 + q * x5 - x * x7 + E_* x9 + y * x11 - r * x13 + t * x15 - A * x17 - C * x19 + v * x21 - p * x23 + w * x25 - D * x27 - z * x29 + s * x31, + C * x1 - x * x3 + s * x5 - q * x7 + v * x9 - A * x11 - E_* x13 + z * x15 - u * x17 + p * x19 - t * x21 + y * x23 - D * x25 - B * x27 + w * x29 - r * x31, + D * x1 - A * x3 + x * x5 - u * x7 + r * x9 - p * x11 + s * x13 - v * x15 + y * x17 - B * x19 + E_* x21 + C * x23 - z * x25 + w * x27 - t * x29 + q * x31, + E_* x1 - D * x3 + C * x5 - B * x7 + A * x9 - z * x11 + y * x13 - x * x15 + w * x17 - v * x19 + u * x21 - t * x23 + s * x25 - r * x27 + q * x29 - p * x31, + }; + + out[0 * out_stride] = E[0] + O[0]; + out[1 * out_stride] = E[1] + O[1]; + out[2 * out_stride] = E[2] + O[2]; + out[3 * out_stride] = E[3] + O[3]; + out[4 * out_stride] = E[4] + O[4]; + out[5 * out_stride] = E[5] + O[5]; + out[6 * out_stride] = E[6] + O[6]; + out[7 * out_stride] = E[7] + O[7]; + out[8 * out_stride] = E[8] + O[8]; + out[9 * out_stride] = E[9] + O[9]; + out[10 * out_stride] = E[10] + O[10]; + out[11 * out_stride] = E[11] + O[11]; + out[12 * out_stride] = E[12] + O[12]; + out[13 * out_stride] = E[13] + O[13]; + out[14 * out_stride] = E[14] + O[14]; + out[15 * out_stride] = E[15] + O[15]; + out[16 * out_stride] = E[15] - O[15]; + out[17 * out_stride] = E[14] - O[14]; + out[18 * out_stride] = E[13] - O[13]; + out[19 * out_stride] = E[12] - O[12]; + out[20 * out_stride] = E[11] - O[11]; + out[21 * out_stride] = E[10] - O[10]; + out[22 * out_stride] = E[9] - O[9]; + out[23 * out_stride] = E[8] - O[8]; + out[24 * out_stride] = E[7] - O[7]; + out[25 * out_stride] = E[6] - O[6]; + out[26 * out_stride] = E[5] - O[5]; + out[27 * out_stride] = E[4] - O[4]; + out[28 * out_stride] = E[3] - O[3]; + out[29 * out_stride] = E[2] - O[2]; + out[30 * out_stride] = E[1] - O[1]; + out[31 * out_stride] = E[0] - O[0]; +} + +/* +transMatrix[64][64] = { + { aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa }, + { bf, bg, bh, bi, bj, bk, bl, bm, bn, bo, bp, bq, br, bs, bt, bu, bv, bw, bx, by, bz, ca, cb, cc, cd, ce, cf, cg, ch, ci, cj, ck, -ck, -cj, -ci, -ch, -cg, -cf, -ce, -cd, -cc, -cb, -ca, -bz, -by, -bx, -bw, -bv, -bu, -bt, -bs, -br, -bq, -bp, -bo, -bn, -bm, -bl, -bk, -bj, -bi, -bh, -bg, -bf }, + { ap, aq, ar, as, at, au, av, aw, ax, ay, az, ba, bb, bc, bd, be, -be, -bd, -bc, -bb, -ba, -az, -ay, -ax, -aw, -av, -au, -at, -as, -ar, -aq, -ap, -ap, -aq, -ar, -as, -at, -au, -av, -aw, -ax, -ay, -az, -ba, -bb, -bc, -bd, -be, be, bd, bc, bb, ba, az, ay, ax, aw, av, au, at, as, ar, aq, ap }, + { bg, bj, bm, bp, bs, bv, by, cb, ce, ch, ck, -ci, -cf, -cc, -bz, -bw, -bt, -bq, -bn, -bk, -bh, -bf, -bi, -bl, -bo, -br, -bu, -bx, -ca, -cd, -cg, -cj, cj, cg, cd, ca, bx, bu, br, bo, bl, bi, bf, bh, bk, bn, bq, bt, bw, bz, cc, cf, ci, -ck, -ch, -ce, -cb, -by, -bv, -bs, -bp, -bm, -bj, -bg }, + { ah, ai, aj, ak, al, am, an, ao, -ao, -an, -am, -al, -ak, -aj, -ai, -ah, -ah, -ai, -aj, -ak, -al, -am, -an, -ao, ao, an, am, al, ak, aj, ai, ah, ah, ai, aj, ak, al, am, an, ao, -ao, -an, -am, -al, -ak, -aj, -ai, -ah, -ah, -ai, -aj, -ak, -al, -am, -an, -ao, ao, an, am, al, ak, aj, ai, ah }, + { bh, bm, br, bw, cb, cg, -ck, -cf, -ca, -bv, -bq, -bl, -bg, -bi, -bn, -bs, -bx, -cc, -ch, cj, ce, bz, bu, bp, bk, bf, bj, bo, bt, by, cd, ci, -ci, -cd, -by, -bt, -bo, -bj, -bf, -bk, -bp, -bu, -bz, -ce, -cj, ch, cc, bx, bs, bn, bi, bg, bl, bq, bv, ca, cf, ck, -cg, -cb, -bw, -br, -bm, -bh }, + { aq, at, aw, az, bc, -be, -bb, -ay, -av, -as, -ap, -ar, -au, -ax, -ba, -bd, bd, ba, ax, au, ar, ap, as, av, ay, bb, be, -bc, -az, -aw, -at, -aq, -aq, -at, -aw, -az, -bc, be, bb, ay, av, as, ap, ar, au, ax, ba, bd, -bd, -ba, -ax, -au, -ar, -ap, -as, -av, -ay, -bb, -be, bc, az, aw, at, aq }, + { bi, bp, bw, cd, ck, -ce, -bx, -bq, -bj, -bh, -bo, -bv, -cc, -cj, cf, by, br, bk, bg, bn, bu, cb, ci, -cg, -bz, -bs, -bl, -bf, -bm, -bt, -ca, -ch, ch, ca, bt, bm, bf, bl, bs, bz, cg, -ci, -cb, -bu, -bn, -bg, -bk, -br, -by, -cf, cj, cc, bv, bo, bh, bj, bq, bx, ce, -ck, -cd, -bw, -bp, -bi }, + { ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad, ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad, ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad, ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad }, + { bj, bs, cb, ck, -cc, -bt, -bk, -bi, -br, -ca, -cj, cd, bu, bl, bh, bq, bz, ci, -ce, -bv, -bm, -bg, -bp, -by, -ch, cf, bw, bn, bf, bo, bx, cg, -cg, -bx, -bo, -bf, -bn, -bw, -cf, ch, by, bp, bg, bm, bv, ce, -ci, -bz, -bq, -bh, -bl, -bu, -cd, cj, ca, br, bi, bk, bt, cc, -ck, -cb, -bs, -bj }, + { ar, aw, bb, -bd, -ay, -at, -ap, -au, -az, -be, ba, av, aq, as, ax, bc, -bc, -ax, -as, -aq, -av, -ba, be, az, au, ap, at, ay, bd, -bb, -aw, -ar, -ar, -aw, -bb, bd, ay, at, ap, au, az, be, -ba, -av, -aq, -as, -ax, -bc, bc, ax, as, aq, av, ba, -be, -az, -au, -ap, -at, -ay, -bd, bb, aw, ar }, + { bk, bv, cg, -ce, -bt, -bi, -bm, -bx, -ci, cc, br, bg, bo, bz, ck, -ca, -bp, -bf, -bq, -cb, cj, by, bn, bh, bs, cd, -ch, -bw, -bl, -bj, -bu, -cf, cf, bu, bj, bl, bw, ch, -cd, -bs, -bh, -bn, -by, -cj, cb, bq, bf, bp, ca, -ck, -bz, -bo, -bg, -br, -cc, ci, bx, bm, bi, bt, ce, -cg, -bv, -bk }, + { ai, al, ao, -am, -aj, -ah, -ak, -an, an, ak, ah, aj, am, -ao, -al, -ai, -ai, -al, -ao, am, aj, ah, ak, an, -an, -ak, -ah, -aj, -am, ao, al, ai, ai, al, ao, -am, -aj, -ah, -ak, -an, an, ak, ah, aj, am, -ao, -al, -ai, -ai, -al, -ao, am, aj, ah, ak, an, -an, -ak, -ah, -aj, -am, ao, al, ai }, + { bl, by, -ck, -bx, -bk, -bm, -bz, cj, bw, bj, bn, ca, -ci, -bv, -bi, -bo, -cb, ch, bu, bh, bp, cc, -cg, -bt, -bg, -bq, -cd, cf, bs, bf, br, ce, -ce, -br, -bf, -bs, -cf, cd, bq, bg, bt, cg, -cc, -bp, -bh, -bu, -ch, cb, bo, bi, bv, ci, -ca, -bn, -bj, -bw, -cj, bz, bm, bk, bx, ck, -by, -bl }, + { as, az, -bd, -aw, -ap, -av, -bc, ba, at, ar, ay, -be, -ax, -aq, -au, -bb, bb, au, aq, ax, be, -ay, -ar, -at, -ba, bc, av, ap, aw, bd, -az, -as, -as, -az, bd, aw, ap, av, bc, -ba, -at, -ar, -ay, be, ax, aq, au, bb, -bb, -au, -aq, -ax, -be, ay, ar, at, ba, -bc, -av, -ap, -aw, -bd, az, as }, + { bm, cb, -cf, -bq, -bi, -bx, cj, bu, bf, bt, ci, -by, -bj, -bp, -ce, cc, bn, bl, ca, -cg, -br, -bh, -bw, ck, bv, bg, bs, ch, -bz, -bk, -bo, -cd, cd, bo, bk, bz, -ch, -bs, -bg, -bv, -ck, bw, bh, br, cg, -ca, -bl, -bn, -cc, ce, bp, bj, by, -ci, -bt, -bf, -bu, -cj, bx, bi, bq, cf, -cb, -bm }, + { ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab }, + { bn, ce, -ca, -bj, -br, -ci, bw, bf, bv, -cj, -bs, -bi, -bz, cf, bo, bm, cd, -cb, -bk, -bq, -ch, bx, bg, bu, -ck, -bt, -bh, -by, cg, bp, bl, cc, -cc, -bl, -bp, -cg, by, bh, bt, ck, -bu, -bg, -bx, ch, bq, bk, cb, -cd, -bm, -bo, -cf, bz, bi, bs, cj, -bv, -bf, -bw, ci, br, bj, ca, -ce, -bn }, + { at, bc, -ay, -ap, -ax, bd, au, as, bb, -az, -aq, -aw, be, av, ar, ba, -ba, -ar, -av, -be, aw, aq, az, -bb, -as, -au, -bd, ax, ap, ay, -bc, -at, -at, -bc, ay, ap, ax, -bd, -au, -as, -bb, az, aq, aw, -be, -av, -ar, -ba, ba, ar, av, be, -aw, -aq, -az, bb, as, au, bd, -ax, -ap, -ay, bc, at }, + { bo, ch, -bv, -bh, -ca, cc, bj, bt, -cj, -bq, -bm, -cf, bx, bf, by, -ce, -bl, -br, -ck, bs, bk, cd, -bz, -bg, -bw, cg, bn, bp, ci, -bu, -bi, -cb, cb, bi, bu, -ci, -bp, -bn, -cg, bw, bg, bz, -cd, -bk, -bs, ck, br, bl, ce, -by, -bf, -bx, cf, bm, bq, cj, -bt, -bj, -cc, ca, bh, bv, -ch, -bo }, + { aj, ao, -ak, -ai, -an, al, ah, am, -am, -ah, -al, an, ai, ak, -ao, -aj, -aj, -ao, ak, ai, an, -al, -ah, -am, am, ah, al, -an, -ai, -ak, ao, aj, aj, ao, -ak, -ai, -an, al, ah, am, -am, -ah, -al, an, ai, ak, -ao, -aj, -aj, -ao, ak, ai, an, -al, -ah, -am, am, ah, al, -an, -ai, -ak, ao, aj }, + { bp, ck, -bq, -bo, -cj, br, bn, ci, -bs, -bm, -ch, bt, bl, cg, -bu, -bk, -cf, bv, bj, ce, -bw, -bi, -cd, bx, bh, cc, -by, -bg, -cb, bz, bf, ca, -ca, -bf, -bz, cb, bg, by, -cc, -bh, -bx, cd, bi, bw, -ce, -bj, -bv, cf, bk, bu, -cg, -bl, -bt, ch, bm, bs, -ci, -bn, -br, cj, bo, bq, -ck, -bp }, + { au, -be, -at, -av, bd, as, aw, -bc, -ar, -ax, bb, aq, ay, -ba, -ap, -az, az, ap, ba, -ay, -aq, -bb, ax, ar, bc, -aw, -as, -bd, av, at, be, -au, -au, be, at, av, -bd, -as, -aw, bc, ar, ax, -bb, -aq, -ay, ba, ap, az, -az, -ap, -ba, ay, aq, bb, -ax, -ar, -bc, aw, as, bd, -av, -at, -be, au }, + { bq, -ci, -bl, -bv, cd, bg, ca, -by, -bi, -cf, bt, bn, ck, -bo, -bs, cg, bj, bx, -cb, -bf, -cc, bw, bk, ch, -br, -bp, cj, bm, bu, -ce, -bh, -bz, bz, bh, ce, -bu, -bm, -cj, bp, br, -ch, -bk, -bw, cc, bf, cb, -bx, -bj, -cg, bs, bo, -ck, -bn, -bt, cf, bi, by, -ca, -bg, -cd, bv, bl, ci, -bq }, + { ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae, ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae, ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae, ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae }, + { br, -cf, -bg, -cc, bu, bo, -ci, -bj, -bz, bx, bl, ck, -bm, -bw, ca, bi, ch, -bp, -bt, cd, bf, ce, -bs, -bq, cg, bh, cb, -bv, -bn, cj, bk, by, -by, -bk, -cj, bn, bv, -cb, -bh, -cg, bq, bs, -ce, -bf, -cd, bt, bp, -ch, -bi, -ca, bw, bm, -ck, -bl, -bx, bz, bj, ci, -bo, -bu, cc, bg, cf, -br }, + { av, -bb, -ap, -bc, au, aw, -ba, -aq, -bd, at, ax, -az, -ar, -be, as, ay, -ay, -as, be, ar, az, -ax, -at, bd, aq, ba, -aw, -au, bc, ap, bb, -av, -av, bb, ap, bc, -au, -aw, ba, aq, bd, -at, -ax, az, ar, be, -as, -ay, ay, as, -be, -ar, -az, ax, at, -bd, -aq, -ba, aw, au, -bc, -ap, -bb, av }, + { bs, -cc, -bi, -cj, bl, bz, -bv, -bp, cf, bf, cg, -bo, -bw, by, bm, -ci, -bh, -cd, br, bt, -cb, -bj, -ck, bk, ca, -bu, -bq, ce, bg, ch, -bn, -bx, bx, bn, -ch, -bg, -ce, bq, bu, -ca, -bk, ck, bj, cb, -bt, -br, cd, bh, ci, -bm, -by, bw, bo, -cg, -bf, -cf, bp, bv, -bz, -bl, cj, bi, cc, -bs }, + { ak, -am, -ai, ao, ah, an, -aj, -al, al, aj, -an, -ah, -ao, ai, am, -ak, -ak, am, ai, -ao, -ah, -an, aj, al, -al, -aj, an, ah, ao, -ai, -am, ak, ak, -am, -ai, ao, ah, an, -aj, -al, al, aj, -an, -ah, -ao, ai, am, -ak, -ak, am, ai, -ao, -ah, -an, aj, al, -al, -aj, an, ah, ao, -ai, -am, ak }, + { bt, -bz, -bn, cf, bh, ck, -bi, -ce, bo, by, -bu, -bs, ca, bm, -cg, -bg, -cj, bj, cd, -bp, -bx, bv, br, -cb, -bl, ch, bf, ci, -bk, -cc, bq, bw, -bw, -bq, cc, bk, -ci, -bf, -ch, bl, cb, -br, -bv, bx, bp, -cd, -bj, cj, bg, cg, -bm, -ca, bs, bu, -by, -bo, ce, bi, -ck, -bh, -cf, bn, bz, -bt }, + { aw, -ay, -au, ba, as, -bc, -aq, be, ap, bd, -ar, -bb, at, az, -av, -ax, ax, av, -az, -at, bb, ar, -bd, -ap, -be, aq, bc, -as, -ba, au, ay, -aw, -aw, ay, au, -ba, -as, bc, aq, -be, -ap, -bd, ar, bb, -at, -az, av, ax, -ax, -av, az, at, -bb, -ar, bd, ap, be, -aq, -bc, as, ba, -au, -ay, aw }, + { bu, -bw, -bs, by, bq, -ca, -bo, cc, bm, -ce, -bk, cg, bi, -ci, -bg, ck, bf, cj, -bh, -ch, bj, cf, -bl, -cd, bn, cb, -bp, -bz, br, bx, -bt, -bv, bv, bt, -bx, -br, bz, bp, -cb, -bn, cd, bl, -cf, -bj, ch, bh, -cj, -bf, -ck, bg, ci, -bi, -cg, bk, ce, -bm, -cc, bo, ca, -bq, -by, bs, bw, -bu }, + { aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa }, + { bv, -bt, -bx, br, bz, -bp, -cb, bn, cd, -bl, -cf, bj, ch, -bh, -cj, bf, -ck, -bg, ci, bi, -cg, -bk, ce, bm, -cc, -bo, ca, bq, -by, -bs, bw, bu, -bu, -bw, bs, by, -bq, -ca, bo, cc, -bm, -ce, bk, cg, -bi, -ci, bg, ck, -bf, cj, bh, -ch, -bj, cf, bl, -cd, -bn, cb, bp, -bz, -br, bx, bt, -bv }, + { ax, -av, -az, at, bb, -ar, -bd, ap, -be, -aq, bc, as, -ba, -au, ay, aw, -aw, -ay, au, ba, -as, -bc, aq, be, -ap, bd, ar, -bb, -at, az, av, -ax, -ax, av, az, -at, -bb, ar, bd, -ap, be, aq, -bc, -as, ba, au, -ay, -aw, aw, ay, -au, -ba, as, bc, -aq, -be, ap, -bd, -ar, bb, at, -az, -av, ax }, + { bw, -bq, -cc, bk, ci, -bf, ch, bl, -cb, -br, bv, bx, -bp, -cd, bj, cj, -bg, cg, bm, -ca, -bs, bu, by, -bo, -ce, bi, ck, -bh, cf, bn, -bz, -bt, bt, bz, -bn, -cf, bh, -ck, -bi, ce, bo, -by, -bu, bs, ca, -bm, -cg, bg, -cj, -bj, cd, bp, -bx, -bv, br, cb, -bl, -ch, bf, -ci, -bk, cc, bq, -bw }, + { al, -aj, -an, ah, -ao, -ai, am, ak, -ak, -am, ai, ao, -ah, an, aj, -al, -al, aj, an, -ah, ao, ai, -am, -ak, ak, am, -ai, -ao, ah, -an, -aj, al, al, -aj, -an, ah, -ao, -ai, am, ak, -ak, -am, ai, ao, -ah, an, aj, -al, -al, aj, an, -ah, ao, ai, -am, -ak, ak, am, -ai, -ao, ah, -an, -aj, al }, + { bx, -bn, -ch, bg, -ce, -bq, bu, ca, -bk, -ck, bj, -cb, -bt, br, cd, -bh, ci, bm, -by, -bw, bo, cg, -bf, cf, bp, -bv, -bz, bl, cj, -bi, cc, bs, -bs, -cc, bi, -cj, -bl, bz, bv, -bp, -cf, bf, -cg, -bo, bw, by, -bm, -ci, bh, -cd, -br, bt, cb, -bj, ck, bk, -ca, -bu, bq, ce, -bg, ch, bn, -bx }, + { ay, -as, -be, ar, -az, -ax, at, bd, -aq, ba, aw, -au, -bc, ap, -bb, -av, av, bb, -ap, bc, au, -aw, -ba, aq, -bd, -at, ax, az, -ar, be, as, -ay, -ay, as, be, -ar, az, ax, -at, -bd, aq, -ba, -aw, au, bc, -ap, bb, av, -av, -bb, ap, -bc, -au, aw, ba, -aq, bd, at, -ax, -az, ar, -be, -as, ay }, + { by, -bk, cj, bn, -bv, -cb, bh, -cg, -bq, bs, ce, -bf, cd, bt, -bp, -ch, bi, -ca, -bw, bm, ck, -bl, bx, bz, -bj, ci, bo, -bu, -cc, bg, -cf, -br, br, cf, -bg, cc, bu, -bo, -ci, bj, -bz, -bx, bl, -ck, -bm, bw, ca, -bi, ch, bp, -bt, -cd, bf, -ce, -bs, bq, cg, -bh, cb, bv, -bn, -cj, bk, -by }, + { af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af, af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af, af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af, af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af }, + { bz, -bh, ce, bu, -bm, cj, bp, -br, -ch, bk, -bw, -cc, bf, -cb, -bx, bj, -cg, -bs, bo, ck, -bn, bt, cf, -bi, by, ca, -bg, cd, bv, -bl, ci, bq, -bq, -ci, bl, -bv, -cd, bg, -ca, -by, bi, -cf, -bt, bn, -ck, -bo, bs, cg, -bj, bx, cb, -bf, cc, bw, -bk, ch, br, -bp, -cj, bm, -bu, -ce, bh, -bz }, + { az, -ap, ba, ay, -aq, bb, ax, -ar, bc, aw, -as, bd, av, -at, be, au, -au, -be, at, -av, -bd, as, -aw, -bc, ar, -ax, -bb, aq, -ay, -ba, ap, -az, -az, ap, -ba, -ay, aq, -bb, -ax, ar, -bc, -aw, as, -bd, -av, at, -be, -au, au, be, -at, av, bd, -as, aw, bc, -ar, ax, bb, -aq, ay, ba, -ap, az }, + { ca, -bf, bz, cb, -bg, by, cc, -bh, bx, cd, -bi, bw, ce, -bj, bv, cf, -bk, bu, cg, -bl, bt, ch, -bm, bs, ci, -bn, br, cj, -bo, bq, ck, -bp, bp, -ck, -bq, bo, -cj, -br, bn, -ci, -bs, bm, -ch, -bt, bl, -cg, -bu, bk, -cf, -bv, bj, -ce, -bw, bi, -cd, -bx, bh, -cc, -by, bg, -cb, -bz, bf, -ca }, + { am, -ah, al, an, -ai, ak, ao, -aj, aj, -ao, -ak, ai, -an, -al, ah, -am, -am, ah, -al, -an, ai, -ak, -ao, aj, -aj, ao, ak, -ai, an, al, -ah, am, am, -ah, al, an, -ai, ak, ao, -aj, aj, -ao, -ak, ai, -an, -al, ah, -am, -am, ah, -al, -an, ai, -ak, -ao, aj, -aj, ao, ak, -ai, an, al, -ah, am }, + { cb, -bi, bu, ci, -bp, bn, -cg, -bw, bg, -bz, -cd, bk, -bs, -ck, br, -bl, ce, by, -bf, bx, cf, -bm, bq, -cj, -bt, bj, -cc, -ca, bh, -bv, -ch, bo, -bo, ch, bv, -bh, ca, cc, -bj, bt, cj, -bq, bm, -cf, -bx, bf, -by, -ce, bl, -br, ck, bs, -bk, cd, bz, -bg, bw, cg, -bn, bp, -ci, -bu, bi, -cb }, + { ba, -ar, av, -be, -aw, aq, -az, -bb, as, -au, bd, ax, -ap, ay, bc, -at, at, -bc, -ay, ap, -ax, -bd, au, -as, bb, az, -aq, aw, be, -av, ar, -ba, -ba, ar, -av, be, aw, -aq, az, bb, -as, au, -bd, -ax, ap, -ay, -bc, at, -at, bc, ay, -ap, ax, bd, -au, as, -bb, -az, aq, -aw, -be, av, -ar, ba }, + { cc, -bl, bp, -cg, -by, bh, -bt, ck, bu, -bg, bx, ch, -bq, bk, -cb, -cd, bm, -bo, cf, bz, -bi, bs, -cj, -bv, bf, -bw, -ci, br, -bj, ca, ce, -bn, bn, -ce, -ca, bj, -br, ci, bw, -bf, bv, cj, -bs, bi, -bz, -cf, bo, -bm, cd, cb, -bk, bq, -ch, -bx, bg, -bu, -ck, bt, -bh, by, cg, -bp, bl, -cc }, + { ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac }, + { cd, -bo, bk, -bz, -ch, bs, -bg, bv, -ck, -bw, bh, -br, cg, ca, -bl, bn, -cc, -ce, bp, -bj, by, ci, -bt, bf, -bu, cj, bx, -bi, bq, -cf, -cb, bm, -bm, cb, cf, -bq, bi, -bx, -cj, bu, -bf, bt, -ci, -by, bj, -bp, ce, cc, -bn, bl, -ca, -cg, br, -bh, bw, ck, -bv, bg, -bs, ch, bz, -bk, bo, -cd }, + { bb, -au, aq, -ax, be, ay, -ar, at, -ba, -bc, av, -ap, aw, -bd, -az, as, -as, az, bd, -aw, ap, -av, bc, ba, -at, ar, -ay, -be, ax, -aq, au, -bb, -bb, au, -aq, ax, -be, -ay, ar, -at, ba, bc, -av, ap, -aw, bd, az, -as, as, -az, -bd, aw, -ap, av, -bc, -ba, at, -ar, ay, be, -ax, aq, -au, bb }, + { ce, -br, bf, -bs, cf, cd, -bq, bg, -bt, cg, cc, -bp, bh, -bu, ch, cb, -bo, bi, -bv, ci, ca, -bn, bj, -bw, cj, bz, -bm, bk, -bx, ck, by, -bl, bl, -by, -ck, bx, -bk, bm, -bz, -cj, bw, -bj, bn, -ca, -ci, bv, -bi, bo, -cb, -ch, bu, -bh, bp, -cc, -cg, bt, -bg, bq, -cd, -cf, bs, -bf, br, -ce }, + { an, -ak, ah, -aj, am, ao, -al, ai, -ai, al, -ao, -am, aj, -ah, ak, -an, -an, ak, -ah, aj, -am, -ao, al, -ai, ai, -al, ao, am, -aj, ah, -ak, an, an, -ak, ah, -aj, am, ao, -al, ai, -ai, al, -ao, -am, aj, -ah, ak, -an, -an, ak, -ah, aj, -am, -ao, al, -ai, ai, -al, ao, am, -aj, ah, -ak, an }, + { cf, -bu, bj, -bl, bw, -ch, -cd, bs, -bh, bn, -by, cj, cb, -bq, bf, -bp, ca, ck, -bz, bo, -bg, br, -cc, -ci, bx, -bm, bi, -bt, ce, cg, -bv, bk, -bk, bv, -cg, -ce, bt, -bi, bm, -bx, ci, cc, -br, bg, -bo, bz, -ck, -ca, bp, -bf, bq, -cb, -cj, by, -bn, bh, -bs, cd, ch, -bw, bl, -bj, bu, -cf }, + { bc, -ax, as, -aq, av, -ba, -be, az, -au, ap, -at, ay, -bd, -bb, aw, -ar, ar, -aw, bb, bd, -ay, at, -ap, au, -az, be, ba, -av, aq, -as, ax, -bc, -bc, ax, -as, aq, -av, ba, be, -az, au, -ap, at, -ay, bd, bb, -aw, ar, -ar, aw, -bb, -bd, ay, -at, ap, -au, az, -be, -ba, av, -aq, as, -ax, bc }, + { cg, -bx, bo, -bf, bn, -bw, cf, ch, -by, bp, -bg, bm, -bv, ce, ci, -bz, bq, -bh, bl, -bu, cd, cj, -ca, br, -bi, bk, -bt, cc, ck, -cb, bs, -bj, bj, -bs, cb, -ck, -cc, bt, -bk, bi, -br, ca, -cj, -cd, bu, -bl, bh, -bq, bz, -ci, -ce, bv, -bm, bg, -bp, by, -ch, -cf, bw, -bn, bf, -bo, bx, -cg }, + { ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag, ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag, ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag, ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag }, + { ch, -ca, bt, -bm, bf, -bl, bs, -bz, cg, ci, -cb, bu, -bn, bg, -bk, br, -by, cf, cj, -cc, bv, -bo, bh, -bj, bq, -bx, ce, ck, -cd, bw, -bp, bi, -bi, bp, -bw, cd, -ck, -ce, bx, -bq, bj, -bh, bo, -bv, cc, -cj, -cf, by, -br, bk, -bg, bn, -bu, cb, -ci, -cg, bz, -bs, bl, -bf, bm, -bt, ca, -ch }, + { bd, -ba, ax, -au, ar, -ap, as, -av, ay, -bb, be, bc, -az, aw, -at, aq, -aq, at, -aw, az, -bc, -be, bb, -ay, av, -as, ap, -ar, au, -ax, ba, -bd, -bd, ba, -ax, au, -ar, ap, -as, av, -ay, bb, -be, -bc, az, -aw, at, -aq, aq, -at, aw, -az, bc, be, -bb, ay, -av, as, -ap, ar, -au, ax, -ba, bd }, + { ci, -cd, by, -bt, bo, -bj, bf, -bk, bp, -bu, bz, -ce, cj, ch, -cc, bx, -bs, bn, -bi, bg, -bl, bq, -bv, ca, -cf, ck, cg, -cb, bw, -br, bm, -bh, bh, -bm, br, -bw, cb, -cg, -ck, cf, -ca, bv, -bq, bl, -bg, bi, -bn, bs, -bx, cc, -ch, -cj, ce, -bz, bu, -bp, bk, -bf, bj, -bo, bt, -by, cd, -ci }, + { ao, -an, am, -al, ak, -aj, ai, -ah, ah, -ai, aj, -ak, al, -am, an, -ao, -ao, an, -am, al, -ak, aj, -ai, ah, -ah, ai, -aj, ak, -al, am, -an, ao, ao, -an, am, -al, ak, -aj, ai, -ah, ah, -ai, aj, -ak, al, -am, an, -ao, -ao, an, -am, al, -ak, aj, -ai, ah, -ah, ai, -aj, ak, -al, am, -an, ao }, + { cj, -cg, cd, -ca, bx, -bu, br, -bo, bl, -bi, bf, -bh, bk, -bn, bq, -bt, bw, -bz, cc, -cf, ci, ck, -ch, ce, -cb, by, -bv, bs, -bp, bm, -bj, bg, -bg, bj, -bm, bp, -bs, bv, -by, cb, -ce, ch, -ck, -ci, cf, -cc, bz, -bw, bt, -bq, bn, -bk, bh, -bf, bi, -bl, bo, -br, bu, -bx, ca, -cd, cg, -cj }, + { be, -bd, bc, -bb, ba, -az, ay, -ax, aw, -av, au, -at, as, -ar, aq, -ap, ap, -aq, ar, -as, at, -au, av, -aw, ax, -ay, az, -ba, bb, -bc, bd, -be, -be, bd, -bc, bb, -ba, az, -ay, ax, -aw, av, -au, at, -as, ar, -aq, ap, -ap, aq, -ar, as, -at, au, -av, aw, -ax, ay, -az, ba, -bb, bc, -bd, be }, + { ck, -cj, ci, -ch, cg, -cf, ce, -cd, cc, -cb, ca, -bz, by, -bx, bw, -bv, bu, -bt, bs, -br, bq, -bp, bo, -bn, bm, -bl, bk, -bj, bi, -bh, bg, -bf, bf, -bg, bh, -bi, bj, -bk, bl, -bm, bn, -bo, bp, -bq, br, -bs, bt, -bu, bv, -bw, bx, -by, bz, -ca, cb, -cc, cd, -ce, cf, -cg, ch, -ci, cj, -ck }, +} + */ + +void ff_vvc_inv_dct2_64(int *out, const ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) +{ + const int aa = 64, ab = 83, ac = 36, ad = 89, ae = 75, af = 50, ag = 18, ah = 90; + const int ai = 87, aj = 80, ak = 70, al = 57, am = 43, an = 25, ao = 9, ap = 90; + const int aq = 90, ar = 88, as = 85, at = 82, au = 78, av = 73, aw = 67, ax = 61; + const int ay = 54, az = 46, ba = 38, bb = 31, bc = 22, bd = 13, be = 4, bf = 91; + const int bg = 90, bh = 90, bi = 90, bj = 88, bk = 87, bl = 86, bm = 84, bn = 83; + const int bo = 81, bp = 79, bq = 77, br = 73, bs = 71, bt = 69, bu = 65, bv = 62; + const int bw = 59, bx = 56, by = 52, bz = 48, ca = 44, cb = 41, cc = 37, cd = 33; + const int ce = 28, cf = 24, cg = 20, ch = 15, ci = 11, cj = 7, ck = 2; + const int x0 = in[0 * in_stride], x1 = in[1 * in_stride]; + const int x2 = in[2 * in_stride], x3 = in[3 * in_stride]; + const int x4 = in[4 * in_stride], x5 = in[5 * in_stride]; + const int x6 = in[6 * in_stride], x7 = in[7 * in_stride]; + const int x8 = in[8 * in_stride], x9 = in[9 * in_stride]; + const int x10 = in[10 * in_stride], x11 = in[11 * in_stride]; + const int x12 = in[12 * in_stride], x13 = in[13 * in_stride]; + const int x14 = in[14 * in_stride], x15 = in[15 * in_stride]; + const int x16 = in[16 * in_stride], x17 = in[17 * in_stride]; + const int x18 = in[18 * in_stride], x19 = in[19 * in_stride]; + const int x20 = in[20 * in_stride], x21 = in[21 * in_stride]; + const int x22 = in[22 * in_stride], x23 = in[23 * in_stride]; + const int x24 = in[24 * in_stride], x25 = in[25 * in_stride]; + const int x26 = in[26 * in_stride], x27 = in[27 * in_stride]; + const int x28 = in[28 * in_stride], x29 = in[29 * in_stride]; + const int x30 = in[30 * in_stride], x31 = in[31 * in_stride]; + const int x32 = in[32 * in_stride], x33 = in[33 * in_stride]; + const int x34 = in[34 * in_stride], x35 = in[35 * in_stride]; + const int x36 = in[36 * in_stride], x37 = in[37 * in_stride]; + const int x38 = in[38 * in_stride], x39 = in[39 * in_stride]; + const int x40 = in[40 * in_stride], x41 = in[41 * in_stride]; + const int x42 = in[42 * in_stride], x43 = in[43 * in_stride]; + const int x44 = in[44 * in_stride], x45 = in[45 * in_stride]; + const int x46 = in[46 * in_stride], x47 = in[47 * in_stride]; + const int x48 = in[48 * in_stride], x49 = in[49 * in_stride]; + const int x50 = in[50 * in_stride], x51 = in[51 * in_stride]; + const int x52 = in[52 * in_stride], x53 = in[53 * in_stride]; + const int x54 = in[54 * in_stride], x55 = in[55 * in_stride]; + const int x56 = in[56 * in_stride], x57 = in[57 * in_stride]; + const int x58 = in[58 * in_stride], x59 = in[59 * in_stride]; + const int x60 = in[60 * in_stride], x61 = in[61 * in_stride]; + const int x62 = in[62 * in_stride], x63 = in[63 * in_stride]; + const int EEEEE[2] = { + aa * (x0 + x32), + aa * (x0 - x32), + }; + const int EEEEO[2] = { + ab * x16 + ac * x48, + ac * x16 - ab * x48, + }; + const int EEEE[4] = { + EEEEE[0] + EEEEO[0], EEEEE[1] + EEEEO[1], + EEEEE[1] - EEEEO[1], EEEEE[0] - EEEEO[0], + }; + const int EEEO[4] = { + ad * x8 + ae * x24 + af * x40 + ag * x56, + ae * x8 - ag * x24 - ad * x40 - af * x56, + af * x8 - ad * x24 + ag * x40 + ae * x56, + ag * x8 - af * x24 + ae * x40 - ad * x56, + }; + const int EEE[8] = { + EEEE[0] + EEEO[0], EEEE[1] + EEEO[1], EEEE[2] + EEEO[2], EEEE[3] + EEEO[3], + EEEE[3] - EEEO[3], EEEE[2] - EEEO[2], EEEE[1] - EEEO[1], EEEE[0] - EEEO[0], + }; + const int EEO[8] = { + ah * x4 + ai * x12 + aj * x20 + ak * x28 + al * x36 + am * x44 + an * x52 + ao * x60, + ai * x4 + al * x12 + ao * x20 - am * x28 - aj * x36 - ah * x44 - ak * x52 - an * x60, + aj * x4 + ao * x12 - ak * x20 - ai * x28 - an * x36 + al * x44 + ah * x52 + am * x60, + ak * x4 - am * x12 - ai * x20 + ao * x28 + ah * x36 + an * x44 - aj * x52 - al * x60, + al * x4 - aj * x12 - an * x20 + ah * x28 - ao * x36 - ai * x44 + am * x52 + ak * x60, + am * x4 - ah * x12 + al * x20 + an * x28 - ai * x36 + ak * x44 + ao * x52 - aj * x60, + an * x4 - ak * x12 + ah * x20 - aj * x28 + am * x36 + ao * x44 - al * x52 + ai * x60, + ao * x4 - an * x12 + am * x20 - al * x28 + ak * x36 - aj * x44 + ai * x52 - ah * x60, + }; + const int EE[16] = { + EEE[0] + EEO[0], EEE[1] + EEO[1], EEE[2] + EEO[2], EEE[3] + EEO[3], EEE[4] + EEO[4], EEE[5] + EEO[5], EEE[6] + EEO[6], EEE[7] + EEO[7], + EEE[7] - EEO[7], EEE[6] - EEO[6], EEE[5] - EEO[5], EEE[4] - EEO[4], EEE[3] - EEO[3], EEE[2] - EEO[2], EEE[1] - EEO[1], EEE[0] - EEO[0], + }; + const int EO[16] = { + ap * x2 + aq * x6 + ar * x10 + as * x14 + at * x18 + au * x22 + av * x26 + aw * x30 + ax * x34 + ay * x38 + az * x42 + ba * x46 + bb * x50 + bc * x54 + bd * x58 + be * x62, + aq * x2 + at * x6 + aw * x10 + az * x14 + bc * x18 - be * x22 - bb * x26 - ay * x30 - av * x34 - as * x38 - ap * x42 - ar * x46 - au * x50 - ax * x54 - ba * x58 - bd * x62, + ar * x2 + aw * x6 + bb * x10 - bd * x14 - ay * x18 - at * x22 - ap * x26 - au * x30 - az * x34 - be * x38 + ba * x42 + av * x46 + aq * x50 + as * x54 + ax * x58 + bc * x62, + as * x2 + az * x6 - bd * x10 - aw * x14 - ap * x18 - av * x22 - bc * x26 + ba * x30 + at * x34 + ar * x38 + ay * x42 - be * x46 - ax * x50 - aq * x54 - au * x58 - bb * x62, + at * x2 + bc * x6 - ay * x10 - ap * x14 - ax * x18 + bd * x22 + au * x26 + as * x30 + bb * x34 - az * x38 - aq * x42 - aw * x46 + be * x50 + av * x54 + ar * x58 + ba * x62, + au * x2 - be * x6 - at * x10 - av * x14 + bd * x18 + as * x22 + aw * x26 - bc * x30 - ar * x34 - ax * x38 + bb * x42 + aq * x46 + ay * x50 - ba * x54 - ap * x58 - az * x62, + av * x2 - bb * x6 - ap * x10 - bc * x14 + au * x18 + aw * x22 - ba * x26 - aq * x30 - bd * x34 + at * x38 + ax * x42 - az * x46 - ar * x50 - be * x54 + as * x58 + ay * x62, + aw * x2 - ay * x6 - au * x10 + ba * x14 + as * x18 - bc * x22 - aq * x26 + be * x30 + ap * x34 + bd * x38 - ar * x42 - bb * x46 + at * x50 + az * x54 - av * x58 - ax * x62, + ax * x2 - av * x6 - az * x10 + at * x14 + bb * x18 - ar * x22 - bd * x26 + ap * x30 - be * x34 - aq * x38 + bc * x42 + as * x46 - ba * x50 - au * x54 + ay * x58 + aw * x62, + ay * x2 - as * x6 - be * x10 + ar * x14 - az * x18 - ax * x22 + at * x26 + bd * x30 - aq * x34 + ba * x38 + aw * x42 - au * x46 - bc * x50 + ap * x54 - bb * x58 - av * x62, + az * x2 - ap * x6 + ba * x10 + ay * x14 - aq * x18 + bb * x22 + ax * x26 - ar * x30 + bc * x34 + aw * x38 - as * x42 + bd * x46 + av * x50 - at * x54 + be * x58 + au * x62, + ba * x2 - ar * x6 + av * x10 - be * x14 - aw * x18 + aq * x22 - az * x26 - bb * x30 + as * x34 - au * x38 + bd * x42 + ax * x46 - ap * x50 + ay * x54 + bc * x58 - at * x62, + bb * x2 - au * x6 + aq * x10 - ax * x14 + be * x18 + ay * x22 - ar * x26 + at * x30 - ba * x34 - bc * x38 + av * x42 - ap * x46 + aw * x50 - bd * x54 - az * x58 + as * x62, + bc * x2 - ax * x6 + as * x10 - aq * x14 + av * x18 - ba * x22 - be * x26 + az * x30 - au * x34 + ap * x38 - at * x42 + ay * x46 - bd * x50 - bb * x54 + aw * x58 - ar * x62, + bd * x2 - ba * x6 + ax * x10 - au * x14 + ar * x18 - ap * x22 + as * x26 - av * x30 + ay * x34 - bb * x38 + be * x42 + bc * x46 - az * x50 + aw * x54 - at * x58 + aq * x62, + be * x2 - bd * x6 + bc * x10 - bb * x14 + ba * x18 - az * x22 + ay * x26 - ax * x30 + aw * x34 - av * x38 + au * x42 - at * x46 + as * x50 - ar * x54 + aq * x58 - ap * x62, + }; + const int E[32] = { + EE[0] + EO[0], EE[1] + EO[1], EE[2] + EO[2], EE[3] + EO[3], EE[4] + EO[4], EE[5] + EO[5], EE[6] + EO[6], EE[7] + EO[7], EE[8] + EO[8], EE[9] + EO[9], EE[10] + EO[10], EE[11] + EO[11], EE[12] + EO[12], EE[13] + EO[13], EE[14] + EO[14], EE[15] + EO[15], + EE[15] - EO[15], EE[14] - EO[14], EE[13] - EO[13], EE[12] - EO[12], EE[11] - EO[11], EE[10] - EO[10], EE[9] - EO[9], EE[8] - EO[8], EE[7] - EO[7], EE[6] - EO[6], EE[5] - EO[5], EE[4] - EO[4], EE[3] - EO[3], EE[2] - EO[2], EE[1] - EO[1], EE[0] - EO[0], + }; + const int O[32] = { + bf * x1 + bg * x3 + bh * x5 + bi * x7 + bj * x9 + bk * x11 + bl * x13 + bm * x15 + bn * x17 + bo * x19 + bp * x21 + bq * x23 + br * x25 + bs * x27 + bt * x29 + bu * x31 + bv * x33 + bw * x35 + bx * x37 + by * x39 + bz * x41 + ca * x43 + cb * x45 + cc * x47 + cd * x49 + ce * x51 + cf * x53 + cg * x55 + ch * x57 + ci * x59 + cj * x61 + ck * x63, + bg * x1 + bj * x3 + bm * x5 + bp * x7 + bs * x9 + bv * x11 + by * x13 + cb * x15 + ce * x17 + ch * x19 + ck * x21 - ci * x23 + -cf * x25 - cc * x27 - bz * x29 - bw * x31 - bt * x33 - bq * x35 - bn * x37 - bk * x39 - bh * x41 - bf * x43 - bi * x45 - bl * x47 - bo * x49 - br * x51 - bu * x53 - bx * x55 - ca * x57 - cd * x59 - cg * x61 - cj * x63, + bh * x1 + bm * x3 + br * x5 + bw * x7 + cb * x9 + cg * x11 - ck * x13 - cf * x15 - ca * x17 - bv * x19 - bq * x21 - bl * x23 + -bg * x25 - bi * x27 - bn * x29 - bs * x31 - bx * x33 - cc * x35 - ch * x37 + cj * x39 + ce * x41 + bz * x43 + bu * x45 + bp * x47 + bk * x49 + bf * x51 + bj * x53 + bo * x55 + bt * x57 + by * x59 + cd * x61 + ci * x63, + bi * x1 + bp * x3 + bw * x5 + cd * x7 + ck * x9 - ce * x11 - bx * x13 - bq * x15 - bj * x17 - bh * x19 - bo * x21 - bv * x23 + -cc * x25 - cj * x27 + cf * x29 + by * x31 + br * x33 + bk * x35 + bg * x37 + bn * x39 + bu * x41 + cb * x43 + ci * x45 - cg * x47 - bz * x49 - bs * x51 - bl * x53 - bf * x55 - bm * x57 - bt * x59 - ca * x61 - ch * x63, + bj * x1 + bs * x3 + cb * x5 + ck * x7 - cc * x9 - bt * x11 - bk * x13 - bi * x15 - br * x17 - ca * x19 - cj * x21 + cd * x23 + bu * x25 + bl * x27 + bh * x29 + bq * x31 + bz * x33 + ci * x35 - ce * x37 - bv * x39 - bm * x41 - bg * x43 - bp * x45 - by * x47 - ch * x49 + cf * x51 + bw * x53 + bn * x55 + bf * x57 + bo * x59 + bx * x61 + cg * x63, + bk * x1 + bv * x3 + cg * x5 - ce * x7 - bt * x9 - bi * x11 - bm * x13 - bx * x15 - ci * x17 + cc * x19 + br * x21 + bg * x23 + bo * x25 + bz * x27 + ck * x29 - ca * x31 - bp * x33 - bf * x35 - bq * x37 - cb * x39 + cj * x41 + by * x43 + bn * x45 + bh * x47 + bs * x49 + cd * x51 - ch * x53 - bw * x55 - bl * x57 - bj * x59 - bu * x61 - cf * x63, + bl * x1 + by * x3 - ck * x5 - bx * x7 - bk * x9 - bm * x11 - bz * x13 + cj * x15 + bw * x17 + bj * x19 + bn * x21 + ca * x23 + -ci * x25 - bv * x27 - bi * x29 - bo * x31 - cb * x33 + ch * x35 + bu * x37 + bh * x39 + bp * x41 + cc * x43 - cg * x45 - bt * x47 - bg * x49 - bq * x51 - cd * x53 + cf * x55 + bs * x57 + bf * x59 + br * x61 + ce * x63, + bm * x1 + cb * x3 - cf * x5 - bq * x7 - bi * x9 - bx * x11 + cj * x13 + bu * x15 + bf * x17 + bt * x19 + ci * x21 - by * x23 + -bj * x25 - bp * x27 - ce * x29 + cc * x31 + bn * x33 + bl * x35 + ca * x37 - cg * x39 - br * x41 - bh * x43 - bw * x45 + ck * x47 + bv * x49 + bg * x51 + bs * x53 + ch * x55 - bz * x57 - bk * x59 - bo * x61 - cd * x63, + bn * x1 + ce * x3 - ca * x5 - bj * x7 - br * x9 - ci * x11 + bw * x13 + bf * x15 + bv * x17 - cj * x19 - bs * x21 - bi * x23 + -bz * x25 + cf * x27 + bo * x29 + bm * x31 + cd * x33 - cb * x35 - bk * x37 - bq * x39 - ch * x41 + bx * x43 + bg * x45 + bu * x47 - ck * x49 - bt * x51 - bh * x53 - by * x55 + cg * x57 + bp * x59 + bl * x61 + cc * x63, + bo * x1 + ch * x3 - bv * x5 - bh * x7 - ca * x9 + cc * x11 + bj * x13 + bt * x15 - cj * x17 - bq * x19 - bm * x21 - cf * x23 + bx * x25 + bf * x27 + by * x29 - ce * x31 - bl * x33 - br * x35 - ck * x37 + bs * x39 + bk * x41 + cd * x43 - bz * x45 - bg * x47 - bw * x49 + cg * x51 + bn * x53 + bp * x55 + ci * x57 - bu * x59 - bi * x61 - cb * x63, + bp * x1 + ck * x3 - bq * x5 - bo * x7 - cj * x9 + br * x11 + bn * x13 + ci * x15 - bs * x17 - bm * x19 - ch * x21 + bt * x23 + bl * x25 + cg * x27 - bu * x29 - bk * x31 - cf * x33 + bv * x35 + bj * x37 + ce * x39 - bw * x41 - bi * x43 - cd * x45 + bx * x47 + bh * x49 + cc * x51 - by * x53 - bg * x55 - cb * x57 + bz * x59 + bf * x61 + ca * x63, + bq * x1 - ci * x3 - bl * x5 - bv * x7 + cd * x9 + bg * x11 + ca * x13 - by * x15 - bi * x17 - cf * x19 + bt * x21 + bn * x23 + ck * x25 - bo * x27 - bs * x29 + cg * x31 + bj * x33 + bx * x35 - cb * x37 - bf * x39 - cc * x41 + bw * x43 + bk * x45 + ch * x47 - br * x49 - bp * x51 + cj * x53 + bm * x55 + bu * x57 - ce * x59 - bh * x61 - bz * x63, + br * x1 - cf * x3 - bg * x5 - cc * x7 + bu * x9 + bo * x11 - ci * x13 - bj * x15 - bz * x17 + bx * x19 + bl * x21 + ck * x23 + -bm * x25 - bw * x27 + ca * x29 + bi * x31 + ch * x33 - bp * x35 - bt * x37 + cd * x39 + bf * x41 + ce * x43 - bs * x45 - bq * x47 + cg * x49 + bh * x51 + cb * x53 - bv * x55 - bn * x57 + cj * x59 + bk * x61 + by * x63, + bs * x1 - cc * x3 - bi * x5 - cj * x7 + bl * x9 + bz * x11 - bv * x13 - bp * x15 + cf * x17 + bf * x19 + cg * x21 - bo * x23 + -bw * x25 + by * x27 + bm * x29 - ci * x31 - bh * x33 - cd * x35 + br * x37 + bt * x39 - cb * x41 - bj * x43 - ck * x45 + bk * x47 + ca * x49 - bu * x51 - bq * x53 + ce * x55 + bg * x57 + ch * x59 - bn * x61 - bx * x63, + bt * x1 - bz * x3 - bn * x5 + cf * x7 + bh * x9 + ck * x11 - bi * x13 - ce * x15 + bo * x17 + by * x19 - bu * x21 - bs * x23 + ca * x25 + bm * x27 - cg * x29 - bg * x31 - cj * x33 + bj * x35 + cd * x37 - bp * x39 - bx * x41 + bv * x43 + br * x45 - cb * x47 - bl * x49 + ch * x51 + bf * x53 + ci * x55 - bk * x57 - cc * x59 + bq * x61 + bw * x63, + bu * x1 - bw * x3 - bs * x5 + by * x7 + bq * x9 - ca * x11 - bo * x13 + cc * x15 + bm * x17 - ce * x19 - bk * x21 + cg * x23 + bi * x25 - ci * x27 - bg * x29 + ck * x31 + bf * x33 + cj * x35 - bh * x37 - ch * x39 + bj * x41 + cf * x43 - bl * x45 - cd * x47 + bn * x49 + cb * x51 - bp * x53 - bz * x55 + br * x57 + bx * x59 - bt * x61 - bv * x63, + bv * x1 - bt * x3 - bx * x5 + br * x7 + bz * x9 - bp * x11 - cb * x13 + bn * x15 + cd * x17 - bl * x19 - cf * x21 + bj * x23 + ch * x25 - bh * x27 - cj * x29 + bf * x31 - ck * x33 - bg * x35 + ci * x37 + bi * x39 - cg * x41 - bk * x43 + ce * x45 + bm * x47 - cc * x49 - bo * x51 + ca * x53 + bq * x55 - by * x57 - bs * x59 + bw * x61 + bu * x63, + bw * x1 - bq * x3 - cc * x5 + bk * x7 + ci * x9 - bf * x11 + ch * x13 + bl * x15 - cb * x17 - br * x19 + bv * x21 + bx * x23 + -bp * x25 - cd * x27 + bj * x29 + cj * x31 - bg * x33 + cg * x35 + bm * x37 - ca * x39 - bs * x41 + bu * x43 + by * x45 - bo * x47 - ce * x49 + bi * x51 + ck * x53 - bh * x55 + cf * x57 + bn * x59 - bz * x61 - bt * x63, + bx * x1 - bn * x3 - ch * x5 + bg * x7 - ce * x9 - bq * x11 + bu * x13 + ca * x15 - bk * x17 - ck * x19 + bj * x21 - cb * x23 + -bt * x25 + br * x27 + cd * x29 - bh * x31 + ci * x33 + bm * x35 - by * x37 - bw * x39 + bo * x41 + cg * x43 - bf * x45 + cf * x47 + bp * x49 - bv * x51 - bz * x53 + bl * x55 + cj * x57 - bi * x59 + cc * x61 + bs * x63, + by * x1 - bk * x3 + cj * x5 + bn * x7 - bv * x9 - cb * x11 + bh * x13 - cg * x15 - bq * x17 + bs * x19 + ce * x21 - bf * x23 + cd * x25 + bt * x27 - bp * x29 - ch * x31 + bi * x33 - ca * x35 - bw * x37 + bm * x39 + ck * x41 - bl * x43 + bx * x45 + bz * x47 - bj * x49 + ci * x51 + bo * x53 - bu * x55 - cc * x57 + bg * x59 - cf * x61 - br * x63, + bz * x1 - bh * x3 + ce * x5 + bu * x7 - bm * x9 + cj * x11 + bp * x13 - br * x15 - ch * x17 + bk * x19 - bw * x21 - cc * x23 + bf * x25 - cb * x27 - bx * x29 + bj * x31 - cg * x33 - bs * x35 + bo * x37 + ck * x39 - bn * x41 + bt * x43 + cf * x45 - bi * x47 + by * x49 + ca * x51 - bg * x53 + cd * x55 + bv * x57 - bl * x59 + ci * x61 + bq * x63, + ca * x1 - bf * x3 + bz * x5 + cb * x7 - bg * x9 + by * x11 + cc * x13 - bh * x15 + bx * x17 + cd * x19 - bi * x21 + bw * x23 + ce * x25 - bj * x27 + bv * x29 + cf * x31 - bk * x33 + bu * x35 + cg * x37 - bl * x39 + bt * x41 + ch * x43 - bm * x45 + bs * x47 + ci * x49 - bn * x51 + br * x53 + cj * x55 - bo * x57 + bq * x59 + ck * x61 - bp * x63, + cb * x1 - bi * x3 + bu * x5 + ci * x7 - bp * x9 + bn * x11 - cg * x13 - bw * x15 + bg * x17 - bz * x19 - cd * x21 + bk * x23 + -bs * x25 - ck * x27 + br * x29 - bl * x31 + ce * x33 + by * x35 - bf * x37 + bx * x39 + cf * x41 - bm * x43 + bq * x45 - cj * x47 - bt * x49 + bj * x51 - cc * x53 - ca * x55 + bh * x57 - bv * x59 - ch * x61 + bo * x63, + cc * x1 - bl * x3 + bp * x5 - cg * x7 - by * x9 + bh * x11 - bt * x13 + ck * x15 + bu * x17 - bg * x19 + bx * x21 + ch * x23 + -bq * x25 + bk * x27 - cb * x29 - cd * x31 + bm * x33 - bo * x35 + cf * x37 + bz * x39 - bi * x41 + bs * x43 - cj * x45 - bv * x47 + bf * x49 - bw * x51 - ci * x53 + br * x55 - bj * x57 + ca * x59 + ce * x61 - bn * x63, + cd * x1 - bo * x3 + bk * x5 - bz * x7 - ch * x9 + bs * x11 - bg * x13 + bv * x15 - ck * x17 - bw * x19 + bh * x21 - br * x23 + cg * x25 + ca * x27 - bl * x29 + bn * x31 - cc * x33 - ce * x35 + bp * x37 - bj * x39 + by * x41 + ci * x43 - bt * x45 + bf * x47 - bu * x49 + cj * x51 + bx * x53 - bi * x55 + bq * x57 - cf * x59 - cb * x61 + bm * x63, + ce * x1 - br * x3 + bf * x5 - bs * x7 + cf * x9 + cd * x11 - bq * x13 + bg * x15 - bt * x17 + cg * x19 + cc * x21 - bp * x23 + bh * x25 - bu * x27 + ch * x29 + cb * x31 - bo * x33 + bi * x35 - bv * x37 + ci * x39 + ca * x41 - bn * x43 + bj * x45 - bw * x47 + cj * x49 + bz * x51 - bm * x53 + bk * x55 - bx * x57 + ck * x59 + by * x61 - bl * x63, + cf * x1 - bu * x3 + bj * x5 - bl * x7 + bw * x9 - ch * x11 - cd * x13 + bs * x15 - bh * x17 + bn * x19 - by * x21 + cj * x23 + cb * x25 - bq * x27 + bf * x29 - bp * x31 + ca * x33 + ck * x35 - bz * x37 + bo * x39 - bg * x41 + br * x43 - cc * x45 - ci * x47 + bx * x49 - bm * x51 + bi * x53 - bt * x55 + ce * x57 + cg * x59 - bv * x61 + bk * x63, + cg * x1 - bx * x3 + bo * x5 - bf * x7 + bn * x9 - bw * x11 + cf * x13 + ch * x15 - by * x17 + bp * x19 - bg * x21 + bm * x23 + -bv * x25 + ce * x27 + ci * x29 - bz * x31 + bq * x33 - bh * x35 + bl * x37 - bu * x39 + cd * x41 + cj * x43 - ca * x45 + br * x47 - bi * x49 + bk * x51 - bt * x53 + cc * x55 + ck * x57 - cb * x59 + bs * x61 - bj * x63, + ch * x1 - ca * x3 + bt * x5 - bm * x7 + bf * x9 - bl * x11 + bs * x13 - bz * x15 + cg * x17 + ci * x19 - cb * x21 + bu * x23 + -bn * x25 + bg * x27 - bk * x29 + br * x31 - by * x33 + cf * x35 + cj * x37 - cc * x39 + bv * x41 - bo * x43 + bh * x45 - bj * x47 + bq * x49 - bx * x51 + ce * x53 + ck * x55 - cd * x57 + bw * x59 - bp * x61 + bi * x63, + ci * x1 - cd * x3 + by * x5 - bt * x7 + bo * x9 - bj * x11 + bf * x13 - bk * x15 + bp * x17 - bu * x19 + bz * x21 - ce * x23 + cj * x25 + ch * x27 - cc * x29 + bx * x31 - bs * x33 + bn * x35 - bi * x37 + bg * x39 - bl * x41 + bq * x43 - bv * x45 + ca * x47 - cf * x49 + ck * x51 + cg * x53 - cb * x55 + bw * x57 - br * x59 + bm * x61 - bh * x63, + cj * x1 - cg * x3 + cd * x5 - ca * x7 + bx * x9 - bu * x11 + br * x13 - bo * x15 + bl * x17 - bi * x19 + bf * x21 - bh * x23 + bk * x25 - bn * x27 + bq * x29 - bt * x31 + bw * x33 - bz * x35 + cc * x37 - cf * x39 + ci * x41 + ck * x43 - ch * x45 + ce * x47 - cb * x49 + by * x51 - bv * x53 + bs * x55 - bp * x57 + bm * x59 - bj * x61 + bg * x63, + ck * x1 - cj * x3 + ci * x5 - ch * x7 + cg * x9 - cf * x11 + ce * x13 - cd * x15 + cc * x17 - cb * x19 + ca * x21 - bz * x23 + by * x25 - bx * x27 + bw * x29 - bv * x31 + bu * x33 - bt * x35 + bs * x37 - br * x39 + bq * x41 - bp * x43 + bo * x45 - bn * x47 + bm * x49 - bl * x51 + bk * x53 - bj * x55 + bi * x57 - bh * x59 + bg * x61 - bf * x63, + }; + + out[0 * out_stride] = E[0 ] + O[0 ]; + out[1 * out_stride] = E[1 ] + O[1 ]; + out[2 * out_stride] = E[2 ] + O[2 ]; + out[3 * out_stride] = E[3 ] + O[3 ]; + out[4 * out_stride] = E[4 ] + O[4 ]; + out[5 * out_stride] = E[5 ] + O[5 ]; + out[6 * out_stride] = E[6 ] + O[6 ]; + out[7 * out_stride] = E[7 ] + O[7 ]; + out[8 * out_stride] = E[8 ] + O[8 ]; + out[9 * out_stride] = E[9 ] + O[9 ]; + out[10 * out_stride] = E[10] + O[10]; + out[11 * out_stride] = E[11] + O[11]; + out[12 * out_stride] = E[12] + O[12]; + out[13 * out_stride] = E[13] + O[13]; + out[14 * out_stride] = E[14] + O[14]; + out[15 * out_stride] = E[15] + O[15]; + out[16 * out_stride] = E[16] + O[16]; + out[17 * out_stride] = E[17] + O[17]; + out[18 * out_stride] = E[18] + O[18]; + out[19 * out_stride] = E[19] + O[19]; + out[20 * out_stride] = E[20] + O[20]; + out[21 * out_stride] = E[21] + O[21]; + out[22 * out_stride] = E[22] + O[22]; + out[23 * out_stride] = E[23] + O[23]; + out[24 * out_stride] = E[24] + O[24]; + out[25 * out_stride] = E[25] + O[25]; + out[26 * out_stride] = E[26] + O[26]; + out[27 * out_stride] = E[27] + O[27]; + out[28 * out_stride] = E[28] + O[28]; + out[29 * out_stride] = E[29] + O[29]; + out[30 * out_stride] = E[30] + O[30]; + out[31 * out_stride] = E[31] + O[31]; + out[32 * out_stride] = E[31] - O[31]; + out[33 * out_stride] = E[30] - O[30]; + out[34 * out_stride] = E[29] - O[29]; + out[35 * out_stride] = E[28] - O[28]; + out[36 * out_stride] = E[27] - O[27]; + out[37 * out_stride] = E[26] - O[26]; + out[38 * out_stride] = E[25] - O[25]; + out[39 * out_stride] = E[24] - O[24]; + out[40 * out_stride] = E[23] - O[23]; + out[41 * out_stride] = E[22] - O[22]; + out[42 * out_stride] = E[21] - O[21]; + out[43 * out_stride] = E[20] - O[20]; + out[44 * out_stride] = E[19] - O[19]; + out[45 * out_stride] = E[18] - O[18]; + out[46 * out_stride] = E[17] - O[17]; + out[47 * out_stride] = E[16] - O[16]; + out[48 * out_stride] = E[15] - O[15]; + out[49 * out_stride] = E[14] - O[14]; + out[50 * out_stride] = E[13] - O[13]; + out[51 * out_stride] = E[12] - O[12]; + out[52 * out_stride] = E[11] - O[11]; + out[53 * out_stride] = E[10] - O[10]; + out[54 * out_stride] = E[9] - O[9]; + out[55 * out_stride] = E[8] - O[8]; + out[56 * out_stride] = E[7] - O[7]; + out[57 * out_stride] = E[6] - O[6]; + out[58 * out_stride] = E[5] - O[5]; + out[59 * out_stride] = E[4] - O[4]; + out[60 * out_stride] = E[3] - O[3]; + out[61 * out_stride] = E[2] - O[2]; + out[62 * out_stride] = E[1] - O[1]; + out[63 * out_stride] = E[0] - O[0]; +}; + +static void matrix_mul(int *out, const ptrdiff_t out_stride, const int *in, const ptrdiff_t in_stride, const int8_t* matrix, const int size) +{ + for (int i = 0; i < size; i++) { + int o = 0; + + for (int j = 0; j < size; j++) + o += in[j * in_stride] * matrix[j * size]; + *out = o; + out += out_stride; + matrix++; + } +} + +static void inv_dct8(int *out, const ptrdiff_t out_stride, const int *in, const ptrdiff_t in_stride, const int8_t *matrix, const int size) +{ + matrix_mul(out, out_stride, in, in_stride, matrix, size); +} + +#define DEFINE_INV_DCT8_1D(S) \ +void ff_vvc_inv_dct8_ ## S(int *out, ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) \ +{ \ + inv_dct8(out, out_stride, in, in_stride, &ff_vvc_dct8_##S##x##S[0][0], S); \ +} + +DEFINE_INV_DCT8_1D( 4) +DEFINE_INV_DCT8_1D( 8) +DEFINE_INV_DCT8_1D(16) +DEFINE_INV_DCT8_1D(32) + +static void inv_dst7(int *out, const ptrdiff_t out_stride, const int *in, const ptrdiff_t in_stride, const int8_t* matrix, const int size) +{ + matrix_mul(out, out_stride, in, in_stride, matrix, size); +} + +#define DEFINE_INV_DST7_1D(S) \ +void ff_vvc_inv_dst7_ ## S(int *out, ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) \ +{ \ + inv_dst7(out, out_stride, in, in_stride, &ff_vvc_dst7_##S##x##S[0][0], S); \ +} + +DEFINE_INV_DST7_1D( 4) +DEFINE_INV_DST7_1D( 8) +DEFINE_INV_DST7_1D(16) +DEFINE_INV_DST7_1D(32) + +void ff_vvc_inv_lfnst_1d(int *v, const int *u, int no_zero_size, int n_tr_s, + int pred_mode_intra, int lfnst_idx, int log2_transform_range) +{ + int lfnst_tr_set_idx = pred_mode_intra < 0 ? 1 : ff_vvc_lfnst_tr_set_index[pred_mode_intra]; + const int8_t *tr_mat = n_tr_s > 16 ? ff_vvc_lfnst_8x8[lfnst_tr_set_idx][lfnst_idx-1][0] : ff_vvc_lfnst_4x4[lfnst_tr_set_idx][lfnst_idx - 1][0]; + + for (int j = 0; j < n_tr_s; j++, tr_mat++) { + int t = 0; + + for (int i = 0; i < no_zero_size; i++) + t += u[i] * tr_mat[i * n_tr_s]; + v[j] = av_clip_intp2((t + 64) >> 7 , log2_transform_range); + } +} diff --git a/libavcodec/vvc/vvc_itx_1d.h b/libavcodec/vvc/vvc_itx_1d.h new file mode 100644 index 0000000000..313bc7fc5d --- /dev/null +++ b/libavcodec/vvc/vvc_itx_1d.h @@ -0,0 +1,52 @@ +/* + * VVC 1D transform + * + * Copyright (C) 2023 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VVC_ITX_1D_H +#define AVCODEC_VVC_ITX_1D_H + +#include +#include + +#define vvc_itx_1d_fn(name) \ + void (name)(int *out, ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) +typedef vvc_itx_1d_fn(*vvc_itx_1d_fn); + +vvc_itx_1d_fn(ff_vvc_inv_dct2_2); +vvc_itx_1d_fn(ff_vvc_inv_dct2_4); +vvc_itx_1d_fn(ff_vvc_inv_dct2_8); +vvc_itx_1d_fn(ff_vvc_inv_dct2_16); +vvc_itx_1d_fn(ff_vvc_inv_dct2_32); +vvc_itx_1d_fn(ff_vvc_inv_dct2_64); +vvc_itx_1d_fn(ff_vvc_inv_dst7_4); +vvc_itx_1d_fn(ff_vvc_inv_dst7_8); +vvc_itx_1d_fn(ff_vvc_inv_dst7_16); +vvc_itx_1d_fn(ff_vvc_inv_dst7_32); +vvc_itx_1d_fn(ff_vvc_inv_dct8_4); +vvc_itx_1d_fn(ff_vvc_inv_dct8_8); +vvc_itx_1d_fn(ff_vvc_inv_dct8_16); +vvc_itx_1d_fn(ff_vvc_inv_dct8_32); + + +void ff_vvc_inv_lfnst_1d(int *v, const int *u, int no_zero_size, int n_tr_s, + int pred_mode_intra, int lfnst_idx, int log2_transform_range); + +#endif // AVCODEC_VVC_ITX_1D_H From patchwork Sun Nov 12 10:35:21 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 44627 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:92a5:b0:181:818d:5e7f with SMTP id q37csp728437pzg; Sun, 12 Nov 2023 02:37:35 -0800 (PST) X-Google-Smtp-Source: AGHT+IE8baeTPNpdKsRcdP48fJGZ3Qurx+TBuC45795MGzS0I+InMxoYVixtnbGM/s5ddLMD/r+b X-Received: by 2002:a17:906:27d8:b0:9e8:2820:eec8 with SMTP id k24-20020a17090627d800b009e82820eec8mr1737372ejc.35.1699785455379; Sun, 12 Nov 2023 02:37:35 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id sa12-20020a1709076d0c00b00992a0f83dfcsi1653018ejc.471.2023.11.12.02.37.34; Sun, 12 Nov 2023 02:37:35 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@outlook.com header.s=selector1 header.b=a+XqEkXL; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 8874F68CC59; Sun, 12 Nov 2023 12:36:24 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from APC01-PSA-obe.outbound.protection.outlook.com (mail-psaapc01olkn2037.outbound.protection.outlook.com [40.92.52.37]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 91EC168CC50 for ; Sun, 12 Nov 2023 12:36:19 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=MXvdLu7QDhlSctG7wKZDWXfdsZzELmALBf4FuKHVO/wCgqOqzmQP5YAM/qbi0H8e2YwqFCcoIj/mzs8YIG68fPHKio6hCLUdILdiYSM0U0Ky/CK7ugrk9nK++djOyKfVxqqwtznEJvvRbHA0ZY+RuvDIfWkNjZPboC/ALZbaa6j4s45yQ3hLp0YpB1MQvFdaaMyOEioKPCCYblvBKfAwjilydFy/oXw1C7pSJek/Wz6Au1XCwr1KsbM3bDN/NmVC2SXTx64sITRytXdtppv9sx8YwS8MJksfI5pygTAjpUQY5Y+VCQ9THquQs+jsF76Gh1l/d2brc1z1tuI24YW4Hw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=GFXSDEqKT235EXXj9u3LNBjBvjxKoLN94sGNgJ2PEWQ=; b=ZfRig6YkH4S8cWCZ1cr6j9SPM9xVYr9LEfZdb26kIRY9zFQOFoMrRol7AJSTe/PQdBp59yUQuT0nxjVi3/mkzqONXBBqL3kJYn7oWcUYQkJYIjGJUFVfEt4uj8KiB9T65k/bvI13kgLfF1s2a4P8UxgJ230jd2CeiQr0raNZ8vlcuZifrKPhsL1t+xDuIcMmrLmlwLAeJaFt3UYlpqXBtOxS51nannxuHks+mIhCzKVeTCEVDn0WCKelPXLp/k/CnUD3auJr7SLjps+YG4wLLIjR9/NAnySMSByStxS/B/XQuPjUPrBLK55FMTEY3lL+9vTVqDkSCSL1xbtU6nRCOg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=outlook.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=GFXSDEqKT235EXXj9u3LNBjBvjxKoLN94sGNgJ2PEWQ=; b=a+XqEkXLbxsDye1ek028t0fNGlqBvLJaAaP6hmCRNTW/+YP0vW40rcZIcBvahCrkcUuU2MDpuB4o3LTT7ffOWX/UDV0kO1ID+qivxWVY6RrrMziIbUpYfa6xIEyHVFzpaiMj0KoG+6vgDyhHoyrrDacBdc4IDviVlCX2em6eVqLL7BhEk2nWaczjOsU7zup9GxZQaQC1nYC4zLYisNAf13sZqEzfzMKer3dpLjXeB4/oGrklZyCTHXV2bDw370mg+u7di4Pe4vLdcPU7t21r4Rxvuw+kfDzeI16/QAW3RrBZrlFv95x8f05Iau0OiIjl+l/ztsBsZS6kINJmiGl12A== Received: from TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) by TYSPR06MB6768.apcprd06.prod.outlook.com (2603:1096:400:475::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6954.29; Sun, 12 Nov 2023 10:36:00 +0000 Received: from TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f]) by TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f%4]) with mapi id 15.20.6954.028; Sun, 12 Nov 2023 10:36:00 +0000 From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Nov 2023 18:35:21 +0800 Message-ID: X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231112103526.11245-1-nuomi2021@gmail.com> References: <20231112103526.11245-1-nuomi2021@gmail.com> X-TMN: [m4K2jnhcWM26fc28EbPXp1q+JXTm5iO1] X-ClientProxiedBy: TYWP286CA0030.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:262::20) To TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) X-Microsoft-Original-Message-ID: <20231112103526.11245-9-nuomi2021@gmail.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 2 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: TYSPR06MB6433:EE_|TYSPR06MB6768:EE_ X-MS-Office365-Filtering-Correlation-Id: 1f913a7e-edc2-44ec-d7ee-08dbe36b2993 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: MPlKKQWWKYZhCRtf0fzpHZeduXjzoM/MBFv3kzXteICvLN74RmEQAgMfl/KvUm/cx2jarlxIfU+tXHkYMCxmeW9r+TKgF0Jm2CAdJ76tzLlJwuENpRl4of9eiaT+sgQxdVriRzm43DKJMmBbX2TCWfkyqbv01Y3C2ZWzvUAb2V00nxdZjLk44FbUDxtv+CGIMlexTTYEWnrWD1OlV73mSMK1XQNftRDo8G9wJN84R6Rx6FCtSmVlUXlbKQ4xtZEo1yFiIZmwdui+8Qu4efSwUfQf2eITN3mx7TR2qAKZBB9k3ayT6FPcpYvqx6Epz9QGbvvxK9SuQX/HtmG3XiX2qoCS5EmdP8y9p8JF67ffpLoy2lS0FyRPrXeQ+jyHbFIyeE3VoEgNpLJQ8np8NUR8WL7wmcEVUjurs270/Epps+3+xAdFpFTe4+Pvc9iun0YRiglLbOBuGZmUuw+5Lf+3rggtXDL6iZlKCZ3nJqbfYOefC3HmzZ30h3hPN+US5MDMY2Sa+Jg5wFxQDmQ+K6Ah+ncE0V5ZO/Gqrxp4wE6p1eICKMJ7dBFjngLL2kcajkZK4rjcRufxoT3otfyr6qjYoXu1VpjpT4Mt8DNtyvgML6JcwEG6GmExdlX0nXzmjN1n X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?q?o9aAZEwjp46ioIUhdVPMO/bcnf6d?= =?utf-8?q?tDZg+pnXcFYSLgza/9iqbwbeioXt7eJCiOo9NGWosT1GFhIXvipEVq3YE1lhZS/yl?= =?utf-8?q?YBuSyquAL6lnd8c1Kwb9p8zgu3c6H3K6kUljsZVkFX3/emMf+LI/i8DxR9N8WQI9a?= =?utf-8?q?P94So4AXDx1idN0gqSGrZ+Qe4U0Wy2IeiLgBUrg2BnC2VILmoV63NBIc4FJwjIzXL?= =?utf-8?q?lXiHJCkBR75TyAmXJG/s+S9KZ7o0/xP7glofT4GUW3rKpp6s/9RD/siEf+BqUkRyi?= =?utf-8?q?tdKuP4GLeRb6iZlNqqFXaEuhF+gCD62QMkpIAmZpO0FO4fa9pKKoKwK2GiYvfdspi?= =?utf-8?q?/a3+n3NEeT9woTqPMaeYS3JAtshSgV5F0ceyWK/dHQ2T8wl1UmRnJM0j3gzM6OfZF?= =?utf-8?q?3dFmnt/CqH9KPgpnEVnsMFAXiHoQ9qeSolDHciAzt5VWxGdwd8DgTJsiTcnWkW/F2?= =?utf-8?q?bkZVKVKyQrl47l6iCm61VTJtyOQ7yH5W1xNqh8KD9JBJbXBKOqfWYpdM0ON2FHqj8?= =?utf-8?q?60W7fPcPJmaKzaYD0/RRR76HPuOMlZXZtV6OZfLd1HzH1GJEOpTnlWvi1KERJpRKs?= =?utf-8?q?MfY5EVeNt9KJeinhip7spuvfv8BqggOhKsgkKeA3sAygSerHL66w5K46xJskKoHZc?= =?utf-8?q?zj8WeDBhsvXloMyoheto+KF7MjD0/JZNidOy0Is483G8QsF8mDSAHeuhDScCuEowq?= =?utf-8?q?J+mfYVfA5jVhKIkKf8KfGlgiXYrCVMFlXnr95IFmLmqumj+lB9KMrFuKwSOEJ2tdW?= =?utf-8?q?MvBdcGLYSdKMUopVUmEicRsCae7lUDiZmOB01AUre4klsFRJ0LbveRuOlRuIxWMt9?= =?utf-8?q?8I4PFe0GrUXpkJ3SLAQ0S+CpZeQcEWp1iHDBn2VAQAGJNZ3ExO9Q2gbMz0pEVkOFA?= =?utf-8?q?05BuEIYtW/L/RvZbauN3xRBTN/IXXnC7Qmzy7H5XedHJTtRtC9rvWkGlGEbKPmzy3?= =?utf-8?q?RVBgQUwz1XHUfxFzIK1BOsnv8IMkAWXX945QGrSO0Yyz8IB97jnTAA3DfFH6OEStk?= =?utf-8?q?ZMvtDDiFcn/gIlidnhRAQ4r4DS8gX7w453mraKApf1qXvfTxXRuaiUtuNHsnQB6Lo?= =?utf-8?q?EUcZcIdRT+KomuPKaujoMzHUZLazA5GSC7tRqvDfX+Z9rITeBqnfRHBAyZ86skuR0?= =?utf-8?q?qDmzQk0N9gx+FJYgdTusd5K8AIBm+xT2M13q3w8b8kLWQTt9ywIPf70bziyJuWITE?= =?utf-8?q?OdkyltwPpk+Wm47NZ?= X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 1f913a7e-edc2-44ec-d7ee-08dbe36b2993 X-MS-Exchange-CrossTenant-AuthSource: TYSPR06MB6433.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Nov 2023 10:36:00.5026 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYSPR06MB6768 Subject: [FFmpeg-devel] [PATCH v5 09/14] vvcdec: add intra prediction X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Nuo Mi Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: eidRA3NMpHSv --- libavcodec/vvc/Makefile | 1 + libavcodec/vvc/vvc_ctu.c | 50 ++ libavcodec/vvc/vvc_ctu.h | 2 + libavcodec/vvc/vvc_intra.c | 769 ++++++++++++++++++++ libavcodec/vvc/vvc_intra.h | 49 ++ libavcodec/vvc/vvc_intra_template.c | 1015 +++++++++++++++++++++++++++ 6 files changed, 1886 insertions(+) create mode 100644 libavcodec/vvc/vvc_intra.c create mode 100644 libavcodec/vvc/vvc_intra.h create mode 100644 libavcodec/vvc/vvc_intra_template.c diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile index 5796f9ad42..3b1ac72029 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -6,6 +6,7 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ vvc/vvc_ctu.o \ vvc/vvc_data.o \ vvc/vvc_inter.o \ + vvc/vvc_intra.o \ vvc/vvc_itx_1d.o \ vvc/vvc_mvs.o \ vvc/vvc_ps.o \ diff --git a/libavcodec/vvc/vvc_ctu.c b/libavcodec/vvc/vvc_ctu.c index 3d31b862ae..6138d2fc9f 100644 --- a/libavcodec/vvc/vvc_ctu.c +++ b/libavcodec/vvc/vvc_ctu.c @@ -20,10 +20,41 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavcodec/refstruct.h" + #include "vvc_cabac.h" #include "vvc_ctu.h" #include "vvc_mvs.h" +void ff_vvc_decode_neighbour(VVCLocalContext *lc, const int x_ctb, const int y_ctb, + const int rx, const int ry, const int rs) +{ + VVCFrameContext *fc = lc->fc; + const int ctb_size = fc->ps.sps->ctb_size_y; + + lc->end_of_tiles_x = fc->ps.sps->width; + lc->end_of_tiles_y = fc->ps.sps->height; + if (fc->ps.pps->ctb_to_col_bd[rx] != fc->ps.pps->ctb_to_col_bd[rx + 1]) + lc->end_of_tiles_x = FFMIN(x_ctb + ctb_size, lc->end_of_tiles_x); + if (fc->ps.pps->ctb_to_row_bd[ry] != fc->ps.pps->ctb_to_row_bd[ry + 1]) + lc->end_of_tiles_y = FFMIN(y_ctb + ctb_size, lc->end_of_tiles_y); + + lc->boundary_flags = 0; + if (rx > 0 && fc->ps.pps->ctb_to_col_bd[rx] != fc->ps.pps->ctb_to_col_bd[rx - 1]) + lc->boundary_flags |= BOUNDARY_LEFT_TILE; + if (rx > 0 && fc->tab.slice_idx[rs] != fc->tab.slice_idx[rs - 1]) + lc->boundary_flags |= BOUNDARY_LEFT_SLICE; + if (ry > 0 && fc->ps.pps->ctb_to_row_bd[ry] != fc->ps.pps->ctb_to_row_bd[ry - 1]) + lc->boundary_flags |= BOUNDARY_UPPER_TILE; + if (ry > 0 && fc->tab.slice_idx[rs] != fc->tab.slice_idx[rs - fc->ps.pps->ctb_width]) + lc->boundary_flags |= BOUNDARY_UPPER_SLICE; + lc->ctb_left_flag = rx > 0 && !(lc->boundary_flags & BOUNDARY_LEFT_TILE); + lc->ctb_up_flag = ry > 0 && !(lc->boundary_flags & BOUNDARY_UPPER_TILE) && !(lc->boundary_flags & BOUNDARY_UPPER_SLICE); + lc->ctb_up_right_flag = lc->ctb_up_flag && (fc->ps.pps->ctb_to_col_bd[rx] == fc->ps.pps->ctb_to_col_bd[rx + 1]) && + (fc->ps.pps->ctb_to_row_bd[ry] == fc->ps.pps->ctb_to_row_bd[ry - 1]); + lc->ctb_up_left_flag = lc->ctb_left_flag && lc->ctb_up_flag; +} + void ff_vvc_set_neighbour_available(VVCLocalContext *lc, const int x0, const int y0, const int w, const int h) { @@ -39,6 +70,25 @@ void ff_vvc_set_neighbour_available(VVCLocalContext *lc, lc->na.cand_up_right = lc->na.cand_up_right_sap && (x0 + w) < lc->end_of_tiles_x; } +void ff_vvc_ctu_free_cus(CTU *ctu) +{ + CodingUnit **cus = &ctu->cus; + while (*cus) { + CodingUnit *cu = *cus; + TransformUnit **head = &cu->tus.head; + + *cus = cu->next; + + while (*head) { + TransformUnit *tu = *head; + *head = tu->next; + ff_refstruct_unref(&tu); + } + cu->tus.tail = NULL; + + ff_refstruct_unref(&cu); + } +} void ff_vvc_ep_init_stat_coeff(EntryPoint *ep, const int bit_depth, const int persistent_rice_adaptation_enabled_flag) diff --git a/libavcodec/vvc/vvc_ctu.h b/libavcodec/vvc/vvc_ctu.h index 37da5f0df4..6925b621a6 100644 --- a/libavcodec/vvc/vvc_ctu.h +++ b/libavcodec/vvc/vvc_ctu.h @@ -460,6 +460,8 @@ typedef struct ALFParams { //utils void ff_vvc_set_neighbour_available(VVCLocalContext *lc, int x0, int y0, int w, int h); +void ff_vvc_decode_neighbour(VVCLocalContext *lc, int x_ctb, int y_ctb, int rx, int ry, int rs); +void ff_vvc_ctu_free_cus(CTU *ctu); void ff_vvc_ep_init_stat_coeff(EntryPoint *ep, int bit_depth, int persistent_rice_adaptation_enabled_flag); #endif // AVCODEC_VVC_CTU_H diff --git a/libavcodec/vvc/vvc_intra.c b/libavcodec/vvc/vvc_intra.c new file mode 100644 index 0000000000..781f04f419 --- /dev/null +++ b/libavcodec/vvc/vvc_intra.c @@ -0,0 +1,769 @@ +/* + * VVC intra prediction + * + * Copyright (C) 2021 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "libavutil/frame.h" + +#include "vvc_data.h" +#include "vvc_inter.h" +#include "vvc_intra.h" +#include "vvc_itx_1d.h" + +static int is_cclm(enum IntraPredMode mode) +{ + return mode == INTRA_LT_CCLM || mode == INTRA_L_CCLM || mode == INTRA_T_CCLM; +} + +static int derive_ilfnst_pred_mode_intra(const VVCLocalContext *lc, const TransformBlock *tb) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const CodingUnit *cu = lc->cu; + const int x_tb = tb->x0 >> fc->ps.sps->min_cb_log2_size_y; + const int y_tb = tb->y0 >> fc->ps.sps->min_cb_log2_size_y; + const int x_c = (tb->x0 + (tb->tb_width << sps->hshift[1] >> 1) ) >> fc->ps.sps->min_cb_log2_size_y; + const int y_c = (tb->y0 + (tb->tb_height << sps->vshift[1] >> 1)) >> fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int intra_mip_flag = SAMPLE_CTB(fc->tab.imf, x_tb, y_tb); + int pred_mode_intra = tb->c_idx == 0 ? cu->intra_pred_mode_y : cu->intra_pred_mode_c; + if (intra_mip_flag && !tb->c_idx) { + pred_mode_intra = INTRA_PLANAR; + } else if (is_cclm(pred_mode_intra)) { + int intra_mip_flag_c = SAMPLE_CTB(fc->tab.imf, x_c, y_c); + int cu_pred_mode = SAMPLE_CTB(fc->tab.cpm[0], x_c, y_c); + if (intra_mip_flag_c) { + pred_mode_intra = INTRA_PLANAR; + } else if (cu_pred_mode == MODE_IBC || cu_pred_mode == MODE_PLT) { + pred_mode_intra = INTRA_DC; + } else { + pred_mode_intra = SAMPLE_CTB(fc->tab.ipm, x_c, y_c); + } + } + pred_mode_intra = ff_vvc_wide_angle_mode_mapping(cu, tb->tb_width, tb->tb_height, tb->c_idx, pred_mode_intra); + + return pred_mode_intra; +} + +//8.7.4 Transformation process for scaled transform coefficients +static void ilfnst_transform(const VVCLocalContext *lc, TransformBlock *tb) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const CodingUnit *cu = lc->cu; + const int w = tb->tb_width; + const int h = tb->tb_height; + const int n_lfnst_out_size = (w >= 8 && h >= 8) ? 48 : 16; ///< nLfnstOutSize + const int log2_lfnst_size = (w >= 8 && h >= 8) ? 3 : 2; ///< log2LfnstSize + const int n_lfnst_size = 1 << log2_lfnst_size; ///< nLfnstSize + const int non_zero_size = ((w == 8 && h == 8) || (w == 4 && h == 4)) ? 8 : 16; ///< nonZeroSize + const int pred_mode_intra = derive_ilfnst_pred_mode_intra(lc, tb); + const int transpose = pred_mode_intra > 34; + int u[16], v[48]; + + for (int x = 0; x < non_zero_size; x++) { + int xc = ff_vvc_diag_scan_x[2][2][x]; + int yc = ff_vvc_diag_scan_y[2][2][x]; + u[x] = tb->coeffs[w * yc + xc]; + } + ff_vvc_inv_lfnst_1d(v, u, non_zero_size, n_lfnst_out_size, pred_mode_intra, + cu->lfnst_idx, sps->log2_transform_range); + if (transpose) { + int *dst = tb->coeffs; + const int *src = v; + if (n_lfnst_size == 4) { + for (int y = 0; y < 4; y++) { + dst[0] = src[0]; + dst[1] = src[4]; + dst[2] = src[8]; + dst[3] = src[12]; + src++; + dst += w; + } + } else { + for (int y = 0; y < 8; y++) { + dst[0] = src[0]; + dst[1] = src[8]; + dst[2] = src[16]; + dst[3] = src[24]; + if (y < 4) { + dst[4] = src[32]; + dst[5] = src[36]; + dst[6] = src[40]; + dst[7] = src[44]; + } + src++; + dst += w; + } + } + + } else { + int *dst = tb->coeffs; + const int *src = v; + for (int y = 0; y < n_lfnst_size; y++) { + int size = (y < 4) ? n_lfnst_size : 4; + memcpy(dst, src, size * sizeof(int)); + src += size; + dst += w; + } + } + tb->max_scan_x = n_lfnst_size - 1; + tb->max_scan_y = n_lfnst_size - 1; +} + +//part of 8.7.4 Transformation process for scaled transform coefficients +static void derive_transform_type(const VVCFrameContext *fc, const VVCLocalContext *lc, const TransformBlock *tb, enum TxType *trh, enum TxType *trv) +{ + const CodingUnit *cu = lc->cu; + static const enum TxType mts_to_trh[] = {DCT2, DST7, DCT8, DST7, DCT8}; + static const enum TxType mts_to_trv[] = {DCT2, DST7, DST7, DCT8, DCT8}; + const VVCSPS *sps = fc->ps.sps; + int implicit_mts_enabled = 0; + if (tb->c_idx || (cu->isp_split_type != ISP_NO_SPLIT && cu->lfnst_idx)) { + *trh = *trv = DCT2; + return; + } + + if (sps->r->sps_mts_enabled_flag) { + if (cu->isp_split_type != ISP_NO_SPLIT || + (cu->sbt_flag && FFMAX(tb->tb_width, tb->tb_height) <= 32) || + (!sps->r->sps_explicit_mts_intra_enabled_flag && cu->pred_mode == MODE_INTRA && + !cu->lfnst_idx && !cu->intra_mip_flag)) { + implicit_mts_enabled = 1; + } + } + if (implicit_mts_enabled) { + const int w = tb->tb_width; + const int h = tb->tb_height; + if (cu->sbt_flag) { + *trh = (cu->sbt_horizontal_flag || cu->sbt_pos_flag) ? DST7 : DCT8; + *trv = (!cu->sbt_horizontal_flag || cu->sbt_pos_flag) ? DST7 : DCT8; + } else { + *trh = (w >= 4 && w <= 16) ? DST7 : DCT2; + *trv = (h >= 4 && h <= 16) ? DST7 : DCT2; + } + return; + } + *trh = mts_to_trh[cu->mts_idx]; + *trv = mts_to_trv[cu->mts_idx]; +} + +static void add_residual_for_joint_coding_chroma(VVCLocalContext *lc, + const TransformUnit *tu, TransformBlock *tb, const int chroma_scale) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const int c_sign = 1 - 2 * fc->ps.ph.r->ph_joint_cbcr_sign_flag; + const int shift = tu->coded_flag[1] ^ tu->coded_flag[2]; + const int c_idx = 1 + tu->coded_flag[1]; + const ptrdiff_t stride = fc->frame->linesize[c_idx]; + const int hs = fc->ps.sps->hshift[c_idx]; + const int vs = fc->ps.sps->vshift[c_idx]; + uint8_t *dst = &fc->frame->data[c_idx][(tb->y0 >> vs) * stride + + ((tb->x0 >> hs) << fc->ps.sps->pixel_shift)]; + if (chroma_scale) { + fc->vvcdsp.itx.pred_residual_joint(tb->coeffs, tb->tb_width, tb->tb_height, c_sign, shift); + fc->vvcdsp.intra.lmcs_scale_chroma(lc, tb->coeffs, tb->coeffs, tb->tb_width, tb->tb_height, cu->x0, cu->y0); + fc->vvcdsp.itx.add_residual(dst, tb->coeffs, tb->tb_width, tb->tb_height, stride); + } else { + fc->vvcdsp.itx.add_residual_joint(dst, tb->coeffs, tb->tb_width, tb->tb_height, stride, c_sign, shift); + } +} + +static int add_reconstructed_area(VVCLocalContext *lc, const int ch_type, const int x0, const int y0, const int w, const int h) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const int hs = sps->hshift[ch_type]; + const int vs = sps->vshift[ch_type]; + ReconstructedArea *a; + + if (lc->num_ras[ch_type] >= FF_ARRAY_ELEMS(lc->ras[ch_type])) + return AVERROR_INVALIDDATA; + + a = &lc->ras[ch_type][lc->num_ras[ch_type]]; + a->x = x0 >> hs; + a->y = y0 >> vs; + a->w = w >> hs; + a->h = h >> vs; + lc->num_ras[ch_type]++; + + return 0; +} + +static void add_tu_area(const TransformUnit *tu, int *x0, int *y0, int *w, int *h) +{ + *x0 = tu->x0; + *y0 = tu->y0; + *w = tu->width; + *h = tu->height; +} + +#define MIN_ISP_PRED_WIDTH 4 +static int get_luma_predict_unit(const CodingUnit *cu, const TransformUnit *tu, const int idx, int *x0, int *y0, int *w, int *h) +{ + int has_luma = 1; + add_tu_area(tu, x0, y0, w, h); + if (cu->isp_split_type == ISP_VER_SPLIT && tu->width < MIN_ISP_PRED_WIDTH) { + *w = MIN_ISP_PRED_WIDTH; + has_luma = !(idx % (MIN_ISP_PRED_WIDTH / tu->width)); + } + return has_luma; +} + +static int get_chroma_predict_unit(const CodingUnit *cu, const TransformUnit *tu, const int idx, int *x0, int *y0, int *w, int *h) +{ + if (cu->isp_split_type == ISP_NO_SPLIT) { + add_tu_area(tu, x0, y0, w, h); + return 1; + } + if (idx == cu->num_intra_subpartitions - 1) { + *x0 = cu->x0; + *y0 = cu->y0; + *w = cu->cb_width; + *h = cu->cb_height; + return 1; + } + return 0; +} + +//8.4.5.1 General decoding process for intra blocks +static void predict_intra(VVCLocalContext *lc, const TransformUnit *tu, const int idx, const int target_ch_type) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const VVCTreeType tree_type = cu->tree_type; + int x0, y0, w, h; + if (cu->pred_mode != MODE_INTRA) { + add_reconstructed_area(lc, target_ch_type, tu->x0, tu->y0, tu->width, tu->height); + return; + } + if (!target_ch_type && tree_type != DUAL_TREE_CHROMA) { + if (get_luma_predict_unit(cu, tu, idx, &x0, &y0, &w, &h)) { + ff_vvc_set_neighbour_available(lc, x0, y0, w, h); + fc->vvcdsp.intra.intra_pred(lc, x0, y0, w, h, 0); + add_reconstructed_area(lc, 0, x0, y0, w, h); + } + } + if (target_ch_type && tree_type != DUAL_TREE_LUMA) { + if (get_chroma_predict_unit(cu, tu, idx, &x0, &y0, &w, &h)){ + ff_vvc_set_neighbour_available(lc, x0, y0, w, h); + if (is_cclm(cu->intra_pred_mode_c)) { + fc->vvcdsp.intra.intra_cclm_pred(lc, x0, y0, w, h); + } else { + fc->vvcdsp.intra.intra_pred(lc, x0, y0, w, h, 1); + fc->vvcdsp.intra.intra_pred(lc, x0, y0, w, h, 2); + } + add_reconstructed_area(lc, 1, x0, y0, w, h); + } + } +} + +static void scale_clip(int *coeff, const int nzw, const int w, const int h, + const int shift, const int log2_transform_range) +{ + const int add = 1 << (shift - 1); + for (int y = 0; y < h; y++) { + int *p = coeff + y * w; + for (int x = 0; x < nzw; x++) { + *p = av_clip_intp2((*p + add) >> shift, log2_transform_range); + p++; + } + memset(p, 0, sizeof(*p) * (w - nzw)); + } +} + +static void scale(int *out, const int *in, const int w, const int h, const int shift) +{ + const int add = 1 << (shift - 1); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int *o = out + y * w + x; + const int *i = in + y * w + x; + *o = (*i + add) >> shift; + } + } +} + +// part of 8.7.3 Scaling process for transform coefficients +static void derive_qp(const VVCLocalContext *lc, const TransformUnit *tu, TransformBlock *tb) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const CodingUnit *cu = lc->cu; + int qp, qp_act_offset; + + if (tb->c_idx == 0) { + //fix me + qp = cu->qp[LUMA] + sps->qp_bd_offset; + qp_act_offset = cu->act_enabled_flag ? -5 : 0; + } else { + const int is_jcbcr = tu->joint_cbcr_residual_flag && tu->coded_flag[CB] && tu->coded_flag[CR]; + const int idx = is_jcbcr ? JCBCR : tb->c_idx; + qp = cu->qp[idx]; + qp_act_offset = cu->act_enabled_flag ? 1 : 0; + } + if (tb->ts) { + const int qp_prime_ts_min = 4 + 6 * sps->r->sps_min_qp_prime_ts; + + tb->qp = av_clip(qp + qp_act_offset, qp_prime_ts_min, 63 + sps->qp_bd_offset); + tb->rect_non_ts_flag = 0; + tb->bd_shift = 10; + } else { + const int log_sum = tb->log2_tb_width + tb->log2_tb_height; + const int rect_non_ts_flag = log_sum & 1; + + tb->qp = av_clip(qp + qp_act_offset, 0, 63 + sps->qp_bd_offset); + tb->rect_non_ts_flag = rect_non_ts_flag; + tb->bd_shift = sps->bit_depth + rect_non_ts_flag + (log_sum / 2) + + 10 - sps->log2_transform_range + rsh->sh_dep_quant_used_flag; + } + tb->bd_offset = (1 << tb->bd_shift) >> 1; +} + +//8.7.3 Scaling process for transform coefficients +static av_always_inline int derive_scale(const TransformBlock *tb, const int sh_dep_quant_used_flag) +{ + static const uint8_t rem6[63 + 2 * 6 + 1] = { + 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, + 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, + 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, + 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3 + }; + + static const uint8_t div6[63 + 2 * 6 + 1] = { + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, + 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, + 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12 + }; + + const static int level_scale[2][6] = { + { 40, 45, 51, 57, 64, 72 }, + { 57, 64, 72, 80, 90, 102 } + }; + const int addin = sh_dep_quant_used_flag && !tb->ts; + const int qp = tb->qp + addin; + + return level_scale[tb->rect_non_ts_flag][rem6[qp]] << div6[qp]; +} + +//8.7.3 Scaling process for transform coefficients +static const uint8_t* derive_scale_m(const VVCLocalContext *lc, const TransformBlock *tb, uint8_t *scale_m) +{ + //Table 38 – Specification of the scaling matrix identifier variable id according to predMode, cIdx, nTbW, and nTbH + const int ids[2][3][6] = { + { + { 0, 2, 8, 14, 20, 26 }, + { 0, 3, 9, 15, 21, 21 }, + { 0, 4, 10, 16, 22, 22 } + }, + { + { 0, 5, 11, 17, 23, 27 }, + { 0, 6, 12, 18, 24, 24 }, + { 1, 7, 13, 19, 25, 25 }, + } + }; + const VVCFrameParamSets *ps = &lc->fc->ps; + const VVCSPS *sps = ps->sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const CodingUnit *cu = lc->cu; + const VVCScalingList *sl = ps->sl; + const int id = ids[cu->pred_mode != MODE_INTRA][tb->c_idx][FFMAX(tb->log2_tb_height, tb->log2_tb_width) - 1]; + const int log2_matrix_size = (id < 2) ? 1 : (id < 8) ? 2 : 3; + uint8_t *p = scale_m; + + av_assert0(!sps->r->sps_scaling_matrix_for_alternative_colour_space_disabled_flag); + + if (!rsh->sh_explicit_scaling_list_used_flag || tb->ts || + sps->r->sps_scaling_matrix_for_lfnst_disabled_flag && cu->apply_lfnst_flag[tb->c_idx]) + return ff_vvc_default_scale_m; + + if (!sl) { + av_log(lc->fc->avctx, AV_LOG_WARNING, "bug: no scaling list aps, id = %d", ps->ph.r->ph_scaling_list_aps_id); + return ff_vvc_default_scale_m; + } + + for (int y = tb->min_scan_y; y <= tb->max_scan_y; y++) { + const int off = y << log2_matrix_size >> tb->log2_tb_height << log2_matrix_size; + const uint8_t *m = &sl->scaling_matrix_rec[id][off]; + + for (int x = tb->min_scan_x; x <= tb->max_scan_x; x++) + *p++ = m[x << log2_matrix_size >> tb->log2_tb_width]; + } + if (id >= SL_START_16x16 && !tb->min_scan_x && !tb->min_scan_y) + *scale_m = sl->scaling_matrix_dc_rec[id - SL_START_16x16]; + + return scale_m; +} + +//8.7.3 Scaling process for transform coefficients +static av_always_inline int scale_coeff(const TransformBlock *tb, int coeff, + const int scale, const int scale_m, const int log2_transform_range) +{ + coeff = (coeff * scale * scale_m + tb->bd_offset) >> tb->bd_shift; + coeff = av_clip_intp2(coeff, log2_transform_range); + return coeff; +} + +static void dequant(const VVCLocalContext *lc, const TransformUnit *tu, TransformBlock *tb) +{ + uint8_t tmp[MAX_TB_SIZE * MAX_TB_SIZE]; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const VVCSPS *sps = lc->fc->ps.sps; + const uint8_t *scale_m = derive_scale_m(lc, tb, tmp); + int scale; + + derive_qp(lc, tu, tb); + scale = derive_scale(tb, rsh->sh_dep_quant_used_flag); + + for (int y = tb->min_scan_y; y <= tb->max_scan_y; y++) { + for (int x = tb->min_scan_x; x <= tb->max_scan_x; x++) { + int *coeff = tb->coeffs + y * tb->tb_width + x; + + if (*coeff) + *coeff = scale_coeff(tb, *coeff, scale, *scale_m, sps->log2_transform_range); + scale_m++; + } + } +} + +static void itx_2d(const VVCFrameContext *fc, TransformBlock *tb, const enum TxType trh, const enum TxType trv, int *temp) +{ + const VVCSPS *sps = fc->ps.sps; + const int w = tb->tb_width; + const int h = tb->tb_height; + const int nzw = tb->max_scan_x + 1; + + for (int x = 0; x < nzw; x++) + fc->vvcdsp.itx.itx[trv][tb->log2_tb_height - 1](temp + x, w, tb->coeffs + x, w); + scale_clip(temp, nzw, w, h, 7, sps->log2_transform_range); + + for (int y = 0; y < h; y++) + fc->vvcdsp.itx.itx[trh][tb->log2_tb_width - 1](tb->coeffs + y * w, 1, temp + y * w, 1); + scale(tb->coeffs, tb->coeffs, w, h, 5 + sps->log2_transform_range - sps->bit_depth); +} + +static void itx_1d(const VVCFrameContext *fc, TransformBlock *tb, const enum TxType trh, const enum TxType trv, int *temp) +{ + const VVCSPS *sps = fc->ps.sps; + const int w = tb->tb_width; + const int h = tb->tb_height; + + if (w > 1) + fc->vvcdsp.itx.itx[trh][tb->log2_tb_width - 1](temp, 1, tb->coeffs, 1); + else + fc->vvcdsp.itx.itx[trv][tb->log2_tb_height - 1](temp, 1, tb->coeffs, 1); + scale(tb->coeffs, temp, w, h, 6 + sps->log2_transform_range - sps->bit_depth); +} + +static void transform_bdpcm(TransformBlock *tb, const VVCLocalContext *lc, const CodingUnit *cu) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const IntraPredMode mode = tb->c_idx ? cu->intra_pred_mode_c : cu->intra_pred_mode_y; + const int vertical = mode == INTRA_VERT; + lc->fc->vvcdsp.itx.transform_bdpcm(tb->coeffs, tb->tb_width, tb->tb_height, + vertical, sps->log2_transform_range); + if (vertical) + tb->max_scan_y = tb->tb_height - 1; + else + tb->max_scan_x = tb->tb_width - 1; +} + +static void itransform(VVCLocalContext *lc, TransformUnit *tu, const int tu_idx, const int target_ch_type) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCSH *sh = &lc->sc->sh; + const CodingUnit *cu = lc->cu; + const int ps = fc->ps.sps->pixel_shift; + DECLARE_ALIGNED(32, int, temp)[MAX_TB_SIZE * MAX_TB_SIZE]; + + for (int i = 0; i < tu->nb_tbs; i++) { + TransformBlock *tb = &tu->tbs[i]; + const int c_idx = tb->c_idx; + const int ch_type = c_idx > 0; + + if (ch_type == target_ch_type && tb->has_coeffs) { + const int w = tb->tb_width; + const int h = tb->tb_height; + const int chroma_scale = ch_type && sh->r->sh_lmcs_used_flag && fc->ps.ph.r->ph_chroma_residual_scale_flag && (w * h > 4); + const ptrdiff_t stride = fc->frame->linesize[c_idx]; + const int hs = sps->hshift[c_idx]; + const int vs = sps->vshift[c_idx]; + uint8_t *dst = &fc->frame->data[c_idx][(tb->y0 >> vs) * stride + ((tb->x0 >> hs) << ps)]; + + if (cu->bdpcm_flag[tb->c_idx]) + transform_bdpcm(tb, lc, cu); + dequant(lc, tu, tb); + if (!tb->ts) { + enum TxType trh, trv; + + if (cu->apply_lfnst_flag[c_idx]) + ilfnst_transform(lc, tb); + derive_transform_type(fc, lc, tb, &trh, &trv); + if (w > 1 && h > 1) + itx_2d(fc, tb, trh, trv, temp); + else + itx_1d(fc, tb, trh, trv, temp); + } + + if (chroma_scale) + fc->vvcdsp.intra.lmcs_scale_chroma(lc, temp, tb->coeffs, w, h, cu->x0, cu->y0); + fc->vvcdsp.itx.add_residual(dst, chroma_scale ? temp : tb->coeffs, w, h, stride); + + if (tu->joint_cbcr_residual_flag && tb->c_idx) + add_residual_for_joint_coding_chroma(lc, tu, tb, chroma_scale); + } + } +} + +static int reconstruct(VVCLocalContext *lc) +{ + VVCFrameContext *fc = lc->fc; + CodingUnit *cu = lc->cu; + const int start = cu->tree_type == DUAL_TREE_CHROMA; + const int end = fc->ps.sps->r->sps_chroma_format_idc && (cu->tree_type != DUAL_TREE_LUMA); + + for (int ch_type = start; ch_type <= end; ch_type++) { + TransformUnit *tu = cu->tus.head; + for (int i = 0; tu; i++) { + predict_intra(lc, tu, i, ch_type); + itransform(lc, tu, i, ch_type); + tu = tu->next; + } + } + return 0; +} + +int ff_vvc_reconstruct(VVCLocalContext *lc, const int rs, const int rx, const int ry) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int x_ctb = rx << sps->ctb_log2_size_y; + const int y_ctb = ry << sps->ctb_log2_size_y; + CTU *ctu = fc->tab.ctus + rs; + CodingUnit *cu = ctu->cus; + int ret = 0; + + lc->num_ras[0] = lc->num_ras[1] = 0; + lc->lmcs.x_vpdu = -1; + lc->lmcs.y_vpdu = -1; + ff_vvc_decode_neighbour(lc, x_ctb, y_ctb, rx, ry, rs); + while (cu) { + lc->cu = cu; + + if (cu->ciip_flag) + ff_vvc_predict_ciip(lc); + if (cu->coded_flag) { + ret = reconstruct(lc); + } else { + add_reconstructed_area(lc, LUMA, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + add_reconstructed_area(lc, CHROMA, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + } + cu = cu->next; + } + ff_vvc_ctu_free_cus(ctu); + return ret; +} + +int ff_vvc_get_mip_size_id(const int w, const int h) +{ + if (w == 4 && h == 4) + return 0; + if ((w == 4 || h == 4) || (w == 8 && h == 8)) + return 1; + return 2; +} + +int ff_vvc_nscale_derive(const int w, const int h, const int mode) +{ + int side_size, nscale; + av_assert0(mode < INTRA_LT_CCLM && !(mode > INTRA_HORZ && mode < INTRA_VERT)); + if (mode == INTRA_PLANAR || mode == INTRA_DC || + mode == INTRA_HORZ || mode == INTRA_VERT) { + nscale = (av_log2(w) + av_log2(h) - 2) >> 2; + } else { + const int intra_pred_angle = ff_vvc_intra_pred_angle_derive(mode); + const int inv_angle = ff_vvc_intra_inv_angle_derive(intra_pred_angle); + if (mode >= INTRA_VERT) + side_size = h; + if (mode <= INTRA_HORZ) + side_size = w; + nscale = FFMIN(2, av_log2(side_size) - av_log2(3 * inv_angle - 2) + 8); + } + return nscale; +} + +int ff_vvc_need_pdpc(const int w, const int h, const uint8_t bdpcm_flag, const int mode, const int ref_idx) +{ + av_assert0(mode < INTRA_LT_CCLM); + if ((w >= 4 && h >= 4) && !ref_idx && !bdpcm_flag) { + int nscale; + if (mode == INTRA_PLANAR || mode == INTRA_DC || + mode == INTRA_HORZ || mode == INTRA_VERT) + return 1; + if (mode > INTRA_HORZ && mode < INTRA_VERT) + return 0; + nscale = ff_vvc_nscale_derive(w, h, mode); + return nscale >= 0; + + } + return 0; +} + +static const ReconstructedArea* get_reconstructed_area(const VVCLocalContext *lc, const int x, const int y, const int c_idx) +{ + const int ch_type = c_idx > 0; + for (int i = lc->num_ras[ch_type] - 1; i >= 0; i--) { + const ReconstructedArea* a = &lc->ras[ch_type][i]; + const int r = (a->x + a->w); + const int b = (a->y + a->h); + if (a->x <= x && x < r && a->y <= y && y < b) + return a; + + //it's too far away, no need check it; + if (x >= r && y >= b) + break; + } + return NULL; +} + +int ff_vvc_get_top_available(const VVCLocalContext *lc, const int x, const int y, int target_size, const int c_idx) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int hs = sps->hshift[c_idx]; + const int vs = sps->vshift[c_idx]; + const int log2_ctb_size_v = sps->ctb_log2_size_y - vs; + const int end_of_ctb_x = ((lc->cu->x0 >> sps->ctb_log2_size_y) + 1) << sps->ctb_log2_size_y; + const int y0b = av_mod_uintp2(y, log2_ctb_size_v); + const int max_x = FFMIN(fc->ps.pps->width, end_of_ctb_x) >> hs; + const ReconstructedArea *a; + int px = x; + + if (!y0b) { + if (!lc->ctb_up_flag) + return 0; + target_size = FFMIN(target_size, (lc->end_of_tiles_x >> hs) - x); + if (sps->r->sps_entropy_coding_sync_enabled_flag) + target_size = FFMIN(target_size, (end_of_ctb_x >> hs) - x); + return target_size; + } + + target_size = FFMAX(0, FFMIN(target_size, max_x - x)); + while (target_size > 0 && (a = get_reconstructed_area(lc, px, y - 1, c_idx))) { + const int sz = FFMIN(target_size, a->x + a->w - px); + px += sz; + target_size -= sz; + } + return px - x; +} + +int ff_vvc_get_left_available(const VVCLocalContext *lc, const int x, const int y, int target_size, const int c_idx) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int hs = sps->hshift[c_idx]; + const int vs = sps->vshift[c_idx]; + const int log2_ctb_size_h = sps->ctb_log2_size_y - hs; + const int x0b = av_mod_uintp2(x, log2_ctb_size_h); + const int end_of_ctb_y = ((lc->cu->y0 >> sps->ctb_log2_size_y) + 1) << sps->ctb_log2_size_y; + const int max_y = FFMIN(fc->ps.pps->height, end_of_ctb_y) >> vs; + const ReconstructedArea *a; + int py = y; + + if (!x0b && !lc->ctb_left_flag) + return 0; + + target_size = FFMAX(0, FFMIN(target_size, max_y - y)); + if (!x0b) + return target_size; + + while (target_size > 0 && (a = get_reconstructed_area(lc, x - 1, py, c_idx))) { + const int sz = FFMIN(target_size, a->y + a->h - py); + py += sz; + target_size -= sz; + } + return py - y; +} + +static int less(const void *a, const void *b) +{ + return *(const int*)a - *(const int*)b; +} + +int ff_vvc_ref_filter_flag_derive(const int mode) +{ + static const int modes[] = { -14, -12, -10, -6, INTRA_PLANAR, 2, 34, 66, 72, 76, 78, 80}; + return bsearch(&mode, modes, FF_ARRAY_ELEMS(modes), sizeof(int), less) != NULL; +} + +int ff_vvc_intra_pred_angle_derive(const int pred_mode) +{ + static const int angles[] = { + 0, 1, 2, 3, 4, 6, 8, 10, 12, 14, 16, 18, 20, 23, 26, 29, + 32, 35, 39, 45, 51, 57, 64, 73, 86, 102, 128, 171, 256, 341, 512 + }; + int sign = 1, idx, intra_pred_angle; + if (pred_mode > INTRA_DIAG) { + idx = pred_mode - INTRA_VERT; + } else if (pred_mode > 0) { + idx = INTRA_HORZ - pred_mode; + } else { + idx = INTRA_HORZ - 2 - pred_mode; + } + if (idx < 0) { + idx = -idx; + sign = -1; + } + intra_pred_angle = sign * angles[idx]; + return intra_pred_angle; +} + +#define ROUND(f) (int)(f < 0 ? -(-f + 0.5) : (f + 0.5)) +int ff_vvc_intra_inv_angle_derive(const int intra_pred_angle) +{ + float inv_angle; + av_assert0(intra_pred_angle); + inv_angle = 32 * 512.0 / intra_pred_angle; + return ROUND(inv_angle); +} + +//8.4.5.2.7 Wide angle intra prediction mode mapping proces +int ff_vvc_wide_angle_mode_mapping(const CodingUnit *cu, + const int tb_width, const int tb_height, const int c_idx, int pred_mode_intra) +{ + int nw, nh, wh_ratio, min, max; + + if (cu->isp_split_type == ISP_NO_SPLIT || c_idx) { + nw = tb_width; + nh = tb_height; + } else { + nw = cu->cb_width; + nh = cu->cb_height; + } + wh_ratio = FFABS(ff_log2(nw) - ff_log2(nh)); + max = (wh_ratio > 1) ? (8 + 2 * wh_ratio) : 8; + min = (wh_ratio > 1) ? (60 - 2 * wh_ratio) : 60; + + if (nw > nh && pred_mode_intra >=2 && pred_mode_intra < max) + pred_mode_intra += 65; + else if (nh > nw && pred_mode_intra <= 66 && pred_mode_intra > min) + pred_mode_intra -= 67; + return pred_mode_intra; +} diff --git a/libavcodec/vvc/vvc_intra.h b/libavcodec/vvc/vvc_intra.h new file mode 100644 index 0000000000..12d0dae801 --- /dev/null +++ b/libavcodec/vvc/vvc_intra.h @@ -0,0 +1,49 @@ +/* + * VVC intra prediction + * + * Copyright (C) 2021 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef AVCODEC_VVC_INTRA_H +#define AVCODEC_VVC_INTRA_H + +#include "vvc_ctu.h" + +/** + * reconstruct a CTU + * @param lc local context for CTU + * @param rs raster order for the CTU. + * @param rx raster order x for the CTU. + * @param ry raster order y for the CTU. + * @return AVERROR + */ +int ff_vvc_reconstruct(VVCLocalContext *lc, const int rs, const int rx, const int ry); + +//utils for vvc_intra_template +int ff_vvc_get_top_available(const VVCLocalContext *lc, int x0, int y0, int target_size, int c_idx); +int ff_vvc_get_left_available(const VVCLocalContext *lc, int x0, int y0, int target_size, int c_idx); +int ff_vvc_get_mip_size_id(int w, int h); +int ff_vvc_need_pdpc(int w, int h, uint8_t bdpcm_flag, int mode, int ref_idx); +int ff_vvc_nscale_derive(int w, int h, int mode); +int ff_vvc_ref_filter_flag_derive(int mode); +int ff_vvc_intra_pred_angle_derive(int pred_mode); +int ff_vvc_intra_inv_angle_derive(int pred_mode); +int ff_vvc_wide_angle_mode_mapping(const CodingUnit *cu, + int tb_width, int tb_height, int c_idx, int pred_mode_intra); + +#endif // AVCODEC_VVC_INTRA_H diff --git a/libavcodec/vvc/vvc_intra_template.c b/libavcodec/vvc/vvc_intra_template.c new file mode 100644 index 0000000000..9fb47549d5 --- /dev/null +++ b/libavcodec/vvc/vvc_intra_template.c @@ -0,0 +1,1015 @@ +/* + * VVC intra prediction DSP + * + * Copyright (C) 2021-2023 Nuomi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavcodec/bit_depth_template.c" + +#include "vvc_intra.h" + +#define POS(x, y) src[(x) + stride * (y)] + +static av_always_inline void FUNC(cclm_linear_pred)(VVCFrameContext *fc, const int x0, const int y0, + const int w, const int h, const pixel* pdsy, const int *a, const int *b, const int *k) +{ + const VVCSPS *sps = fc->ps.sps; + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS - 1; i++) { + const int c_idx = i + 1; + const int x = x0 >> sps->hshift[c_idx]; + const int y = y0 >> sps->vshift[c_idx]; + const ptrdiff_t stride = fc->frame->linesize[c_idx] / sizeof(pixel); + pixel *src = (pixel*)fc->frame->data[c_idx] + x + y * stride; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + const int dsy = pdsy[y * w + x]; + const int pred = ((dsy * a[i]) >> k[i]) + b[i]; + POS(x, y) = CLIP(pred); + } + } + } +} + +#define MAX_PICK_POS 4 +#define TOP 0 +#define LEFT 1 + +static av_always_inline void FUNC(cclm_get_params_default)(int *a, int *b, int *k) +{ + for (int i = 0; i < 2; i++) { + a[i] = k[i] = 0; + b[i] = 1 << (BIT_DEPTH - 1); + } +} + +static av_always_inline int FUNC(cclm_get_select_pos)(const VVCLocalContext *lc, + const int x, const int y, const int w, const int h, const int avail_t, const int avail_l, + int cnt[2], int pos[2][MAX_PICK_POS]) +{ + const enum IntraPredMode mode = lc->cu->intra_pred_mode_c; + const int num_is4 = !avail_t || !avail_l || mode != INTRA_LT_CCLM; + int num_samp[2]; + + if (mode == INTRA_LT_CCLM) { + num_samp[TOP] = avail_t ? w : 0; + num_samp[LEFT] = avail_l ? h : 0; + } else { + num_samp[TOP] = (avail_t && mode == INTRA_T_CCLM) ? ff_vvc_get_top_available(lc, x, y, w + FFMIN(w, h), 1) : 0; + num_samp[LEFT] = (avail_l && mode == INTRA_L_CCLM) ? ff_vvc_get_left_available(lc, x, y, h + FFMIN(w, h), 1) : 0; + } + if (!num_samp[TOP] && !num_samp[LEFT]) { + return 0; + } + for (int i = TOP; i <= LEFT; i++) { + const int start = num_samp[i] >> (2 + num_is4); + const int step = FFMAX(1, num_samp[i] >> (1 + num_is4)) ; + cnt[i] = FFMIN(num_samp[i], (1 + num_is4) << 1); + for (int c = 0; c < cnt[i]; c++) + pos[i][c] = start + c * step; + } + return 1; +} + +static av_always_inline void FUNC(cclm_select_luma_444)(const pixel *src, const int step, + const int cnt, const int pos[MAX_PICK_POS], pixel *sel_luma) +{ + for (int i = 0; i < cnt; i++) + sel_luma[i] = src[pos[i] * step]; +} + +static av_always_inline void FUNC(cclm_select_luma)(const VVCFrameContext *fc, + const int x0, const int y0, const int avail_t, const int avail_l, const int cnt[2], const int pos[2][MAX_PICK_POS], + pixel *sel_luma) +{ + const VVCSPS *sps = fc->ps.sps; + + const int b_ctu_boundary = !av_mod_uintp2(y0, sps->ctb_log2_size_y); + const int hs = sps->hshift[1]; + const int vs = sps->vshift[1]; + const ptrdiff_t stride = fc->frame->linesize[0] / sizeof(pixel); + + if (!hs && !vs) { + const pixel* src = (pixel*)fc->frame->data[0] + x0 + y0 * stride; + FUNC(cclm_select_luma_444)(src - avail_t * stride, 1, cnt[TOP], pos[TOP], sel_luma); + FUNC(cclm_select_luma_444)(src - avail_l, stride, cnt[LEFT], pos[LEFT], sel_luma + cnt[TOP]); + } else { + // top + if (vs && !b_ctu_boundary) { + const pixel *source = (pixel *)fc->frame->data[0] + x0 + (y0 - 2) * stride; + for (int i = 0; i < cnt[TOP]; i++) { + const int x = pos[TOP][i] << hs; + const pixel *src = source + x; + const int has_left = x || avail_l; + const pixel l = has_left ? POS(-1, 0) : POS(0, 0); + if (sps->r->sps_chroma_vertical_collocated_flag) { + sel_luma[i] = (POS(0, -1) + l + 4 * POS(0, 0) + POS(1, 0) + POS(0, 1) + 4) >> 3; + } else { + const pixel l1 = has_left ? POS(-1, 1) : POS(0, 1); + sel_luma[i] = (l + l1 + 2 * (POS(0, 0) + POS(0, 1)) + POS(1, 0) + POS(1, 1) + 4) >> 3; + } + } + } else { + const pixel *source = (pixel*)fc->frame->data[0] + x0 + (y0 - 1) * stride; + for (int i = 0; i < cnt[TOP]; i++) { + const int x = pos[TOP][i] << hs; + const pixel *src = source + x; + const int has_left = x || avail_l; + const pixel l = has_left ? POS(-1, 0) : POS(0, 0); + sel_luma[i] = (l + 2 * POS(0, 0) + POS(1, 0) + 2) >> 2; + } + } + + // left + { + const pixel *left; + const pixel *source = (pixel *)fc->frame->data[0] + x0 + y0 * stride - (1 + hs) * avail_l; + left = source - avail_l; + + for (int i = 0; i < cnt[LEFT]; i++) { + const int y = pos[LEFT][i] << vs; + const int offset = y * stride; + const pixel *l = left + offset; + const pixel *src = source + offset; + pixel pred; + if (!vs) { + pred = (*l + 2 * POS(0, 0) + POS(1, 0) + 2) >> 2; + } else { + if (sps->r->sps_chroma_vertical_collocated_flag) { + const int has_top = y || avail_t; + const pixel t = has_top ? POS(0, -1) : POS(0, 0); + pred = (*l + t + 4 * POS(0, 0) + POS(1, 0) + POS(0, 1) + 4) >> 3; + } else { + pred = (*l + *(l + stride) + 2 * POS(0, 0) + 2 * POS(0, 1) + POS(1, 0) + POS(1, 1) + 4) >> 3; + } + } + sel_luma[i + cnt[TOP]] = pred; + } + } + } +} + +static av_always_inline void FUNC(cclm_select_chroma)(const VVCFrameContext *fc, + const int x, const int y, const int cnt[2], const int pos[2][MAX_PICK_POS], + pixel sel[][MAX_PICK_POS * 2]) +{ + for (int c_idx = 1; c_idx < VVC_MAX_SAMPLE_ARRAYS; c_idx++) { + const ptrdiff_t stride = fc->frame->linesize[c_idx] / sizeof(pixel); + + //top + const pixel *src = (pixel*)fc->frame->data[c_idx] + x + (y - 1)* stride; + for (int i = 0; i < cnt[TOP]; i++) { + sel[c_idx][i] = src[pos[TOP][i]]; + } + + //left + src = (pixel*)fc->frame->data[c_idx] + x - 1 + y * stride; + for (int i = 0; i < cnt[LEFT]; i++) { + sel[c_idx][i + cnt[TOP]] = src[pos[LEFT][i] * stride]; + } + } +} + +static av_always_inline int FUNC(cclm_select_samples)(const VVCLocalContext *lc, + const int x0, const int y0, const int w, const int h, const int avail_t, const int avail_l, + pixel sel[][MAX_PICK_POS * 2]) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int x = x0 >> sps->hshift[1]; + const int y = y0 >> sps->vshift[1]; + int cnt[2], pos[2][MAX_PICK_POS]; + + if (!FUNC(cclm_get_select_pos)(lc, x, y, w, h, avail_t, avail_l, cnt, pos)) + return 0; + + FUNC(cclm_select_luma)(fc, x0, y0, avail_t, avail_l, cnt, pos, sel[LUMA]); + FUNC(cclm_select_chroma)(fc, x, y, cnt, pos, sel); + + if (cnt[TOP] + cnt[LEFT] == 2) { + for (int c_idx = 0; c_idx < VVC_MAX_SAMPLE_ARRAYS; c_idx++) { + sel[c_idx][3] = sel[c_idx][0]; + sel[c_idx][2] = sel[c_idx][1]; + sel[c_idx][0] = sel[c_idx][1]; + sel[c_idx][1] = sel[c_idx][3]; + } + } + return 1; +} + +static av_always_inline void FUNC(cclm_get_min_max)( + const pixel sel[][MAX_PICK_POS * 2], int *min, int *max) +{ + int min_grp_idx[] = { 0, 2 }; + int max_grp_idx[] = { 1, 3 }; + + if (sel[LUMA][min_grp_idx[0]] > sel[LUMA][min_grp_idx[1]]) + FFSWAP(int, min_grp_idx[0], min_grp_idx[1]); + if (sel[LUMA][max_grp_idx[0]] > sel[LUMA][max_grp_idx[1]]) + FFSWAP(int, max_grp_idx[0], max_grp_idx[1]); + if (sel[LUMA][min_grp_idx[0]] > sel[LUMA][max_grp_idx[1]]) { + FFSWAP(int, min_grp_idx[0], max_grp_idx[0]); + FFSWAP(int, min_grp_idx[1], max_grp_idx[1]); + } + if (sel[LUMA][min_grp_idx[1]] > sel[LUMA][max_grp_idx[0]]) + FFSWAP(int, min_grp_idx[1], max_grp_idx[0]); + for (int c_idx = 0; c_idx < VVC_MAX_SAMPLE_ARRAYS; c_idx++) { + max[c_idx] = (sel[c_idx][max_grp_idx[0]] + sel[c_idx][max_grp_idx[1]] + 1) >> 1; + min[c_idx] = (sel[c_idx][min_grp_idx[0]] + sel[c_idx][min_grp_idx[1]] + 1) >> 1; + } +} + +static av_always_inline void FUNC(cclm_get_params)(const VVCLocalContext *lc, + const int x0, const int y0, const int w, const int h, const int avail_t, const int avail_l, + int *a, int *b, int *k) +{ + pixel sel[VVC_MAX_SAMPLE_ARRAYS][MAX_PICK_POS * 2]; + int max[VVC_MAX_SAMPLE_ARRAYS], min[VVC_MAX_SAMPLE_ARRAYS]; + int diff; + + if (!FUNC(cclm_select_samples)(lc, x0, y0, w, h, avail_t, avail_l, sel)) { + FUNC(cclm_get_params_default)(a, b, k); + return; + } + + FUNC(cclm_get_min_max)(sel, min, max); + + diff = max[LUMA] - min[LUMA]; + if (diff == 0) { + for (int i = 0; i < 2; i++) { + a[i] = k[i] = 0; + b[i] = min[i + 1]; + } + return; + } + for (int i = 0; i < 2; i++) { + const static int div_sig_table[] = {0, 7, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 1, 1, 0}; + const int diffc = max[i + 1] - min[i + 1]; + int x = av_log2(diff); + int y, v, sign, add; + const int norm_diff = ((diff << 4) >> x) & 15; + x += (norm_diff) ? 1 : 0; + y = abs(diffc) > 0 ? av_log2(abs(diffc)) + 1 : 0; + v = div_sig_table[norm_diff] | 8; + add = (1 << y >> 1); + a[i] = (diffc * v + add) >> y; + k[i] = FFMAX(1, 3 + x -y); + sign = a[i] < 0 ? -1 : (a[i] > 0); + a[i] = ((3 + x - y) < 1) ? sign * 15 : a[i]; + b[i] = min[i + 1] - ((a[i] * min[0]) >> k[i]); + } + +} + +#undef TOP +#undef LEFT + +static av_always_inline void FUNC(cclm_get_luma_rec_pixels)(const VVCFrameContext *fc, + const int x0, const int y0, const int w, const int h, const int avail_t, const int avail_l, + pixel *pdsy) +{ + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + const ptrdiff_t stride = fc->frame->linesize[0] / sizeof(pixel); + const pixel *source = (pixel*)fc->frame->data[0] + x0 + y0 * stride; + const pixel *left = source - avail_l; + const pixel *top = source - avail_t * stride; + + const VVCSPS *sps = fc->ps.sps; + if (!hs && !vs) { + for (int i = 0; i < h; i++) + memcpy(pdsy + i * w, source + i * stride, w * sizeof(pixel)); + return; + } + for (int i = 0; i < h; i++) { + const pixel *src = source; + const pixel *l = left; + const pixel *t = top; + if (!vs) { + for (int j = 0; j < w; j++) { + pixel pred = (*l + 2 * POS(0, 0) + POS(1, 0) + 2) >> 2; + pdsy[i * w + j] = pred; + src += 2; + l = src - 1; + } + + } else { + if (sps->r->sps_chroma_vertical_collocated_flag) { + for (int j = 0; j < w; j++) { + pixel pred = (*l + *t + 4 * POS(0, 0) + POS(1, 0) + POS(0, 1) + 4) >> 3; + pdsy[i * w + j] = pred; + src += 2; + t += 2; + l = src - 1; + } + } else { + for (int j = 0; j < w; j++) { + pixel pred = (*l + *(l + stride) + 2 * POS(0, 0) + 2 * POS(0, 1) + POS(1, 0) + POS(1, 1) + 4) >> 3; + + pdsy[i * w + j] = pred; + src += 2; + l = src - 1; + } + } + } + source += (stride << vs); + left += (stride << vs); + top = source - stride; + } +} + +static av_always_inline void FUNC(cclm_pred_default)(VVCFrameContext *fc, + const int x, const int y, const int w, const int h, const int avail_t, const int avail_l) +{ + for (int c_idx = 1; c_idx < VVC_MAX_SAMPLE_ARRAYS; c_idx++) { + const ptrdiff_t stride = fc->frame->linesize[c_idx] / sizeof(pixel); + pixel *dst = (pixel*)fc->frame->data[c_idx] + x + y * stride; + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + dst[j] = 1 << (BIT_DEPTH - 1); + } + dst += stride; + } + } +} + +//8.4.5.2.14 Specification of INTRA_LT_CCLM, INTRA_L_CCLM and INTRA_T_CCLM intra prediction mode +static void FUNC(intra_cclm_pred)(const VVCLocalContext *lc, const int x0, const int y0, + const int width, const int height) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int avail_t = ff_vvc_get_top_available(lc, x0, y0, 1, 0); + const int avail_l = ff_vvc_get_left_available(lc, x0, y0, 1, 0); + const int hs = sps->hshift[1]; + const int vs = sps->vshift[1]; + const int x = x0 >> hs; + const int y = y0 >> vs; + const int w = width >> hs; + const int h = height >> vs; + int a[2], b[2], k[2]; + + pixel dsy[MAX_TB_SIZE * MAX_TB_SIZE]; + if (!avail_t && !avail_l) { + FUNC(cclm_pred_default)(fc, x, y, w, h, avail_t, avail_l); + return; + } + FUNC(cclm_get_luma_rec_pixels)(fc, x0, y0, w, h, avail_t, avail_l, dsy); + FUNC(cclm_get_params) (lc, x0, y0, w, h, avail_t, avail_l, a, b, k); + FUNC(cclm_linear_pred)(fc, x0, y0, w, h, dsy, a, b, k); +} + +static int FUNC(lmcs_sum_samples)(const pixel *start, ptrdiff_t stride, const int avail, const int target_size) +{ + const int size = FFMIN(avail, target_size); + int sum = 0; + for (int i = 0; i < size; i++) { + sum += *start; + start += stride; + } + sum += *(start - stride) * (target_size - size); + return sum; +} + +// 8.7.5.3 Picture reconstruction with luma dependent chroma residual scaling process for chroma samples +static int FUNC(lmcs_derive_chroma_scale)(VVCLocalContext *lc, const int x0, const int y0) +{ + VVCFrameContext *fc = lc->fc; + const VVCLMCS *lmcs = &fc->ps.lmcs; + const int size_y = FFMIN(fc->ps.sps->ctb_size_y, 64); + + const int x = x0 & ~(size_y - 1); + const int y = y0 & ~(size_y - 1); + if (lc->lmcs.x_vpdu != x || lc->lmcs.y_vpdu != y) { + int cnt = 0, luma = 0, i; + const pixel *src = (const pixel *)(fc->frame->data[LUMA] + y * fc->frame->linesize[LUMA] + (x << fc->ps.sps->pixel_shift)); + const ptrdiff_t stride = fc->frame->linesize[LUMA] / sizeof(pixel); + const int avail_t = ff_vvc_get_top_available (lc, x, y, 1, 0); + const int avail_l = ff_vvc_get_left_available(lc, x, y, 1, 0); + if (avail_l) { + luma += FUNC(lmcs_sum_samples)(src - 1, stride, fc->ps.pps->height - y, size_y); + cnt = size_y; + } + if (avail_t) { + luma += FUNC(lmcs_sum_samples)(src - stride, 1, fc->ps.pps->width - x, size_y); + cnt += size_y; + } + if (cnt) + luma = (luma + (cnt >> 1)) >> av_log2(cnt); + else + luma = 1 << (BIT_DEPTH - 1); + + for (i = lmcs->min_bin_idx; i <= lmcs->max_bin_idx; i++) { + if (luma < lmcs->pivot[i + 1]) + break; + } + i = FFMIN(i, LMCS_MAX_BIN_SIZE - 1); + + lc->lmcs.chroma_scale = lmcs->chroma_scale_coeff[i]; + lc->lmcs.x_vpdu = x; + lc->lmcs.y_vpdu = y; + } + return lc->lmcs.chroma_scale; +} + +// 8.7.5.3 Picture reconstruction with luma dependent chroma residual scaling process for chroma samples +static void FUNC(lmcs_scale_chroma)(VVCLocalContext *lc, int *dst, const int *coeff, + const int width, const int height, const int x0_cu, const int y0_cu) +{ + const int chroma_scale = FUNC(lmcs_derive_chroma_scale)(lc, x0_cu, y0_cu); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int c = av_clip_intp2(*coeff, BIT_DEPTH); + + if (c > 0) + *dst = (c * chroma_scale + (1 << 10)) >> 11; + else + *dst = -((-c * chroma_scale + (1 << 10)) >> 11); + coeff++; + dst++; + } + } +} + +static av_always_inline void FUNC(ref_filter)(const pixel *left, const pixel *top, + pixel *filtered_left, pixel *filtered_top, const int left_size, const int top_size, + const int unfilter_last_one) +{ + filtered_left[-1] = filtered_top[-1] = (left[0] + 2 * left[-1] + top[0] + 2 ) >> 2; + for (int i = 0; i < left_size - unfilter_last_one; i++) { + filtered_left[i] = (left[i- 1] + 2 * left[i] + left[i + 1] + 2) >> 2; + } + for (int i = 0; i < top_size - unfilter_last_one; i++) { + filtered_top[i] = (top[i-1] + 2 * top[i] + top[i + 1] + 2) >> 2; + } + if (unfilter_last_one) { + filtered_top[top_size - 1] = top[top_size - 1]; + filtered_left[left_size - 1] = left[left_size - 1]; + } +} + +static av_always_inline void FUNC(prepare_intra_edge_params)(const VVCLocalContext *lc, + IntraEdgeParams* edge, const pixel *src, const ptrdiff_t stride, + const int x, int y, int w, int h, int c_idx, const int is_intra_mip, + const int mode, const int ref_idx, const int need_pdpc) +{ +#define EXTEND(ptr, val, len) \ +do { \ + for (i = 0; i < (len); i++) \ + *(ptr + i) = val; \ +} while (0) + const CodingUnit *cu = lc->cu; + const int ref_filter_flag = is_intra_mip ? 0 : ff_vvc_ref_filter_flag_derive(mode); + const int filter_flag = !ref_idx && w * h > 32 && !c_idx && + cu->isp_split_type == ISP_NO_SPLIT && ref_filter_flag; + int cand_up_left = lc->na.cand_up_left; + pixel *left = (pixel*)edge->left_array + MAX_TB_SIZE + 3; + pixel *top = (pixel*)edge->top_array + MAX_TB_SIZE + 3; + pixel *filtered_left = (pixel*)edge->filtered_left_array + MAX_TB_SIZE + 3; + pixel *filtered_top = (pixel*)edge->filtered_top_array + MAX_TB_SIZE + 3; + const int ref_line = ref_idx == 3 ? -4 : (-1 - ref_idx); + int left_size, top_size, unfilter_left_size, unfilter_top_size; + int left_available, top_available; + int refw, refh; + int intra_pred_angle, inv_angle; + int i; + + if (is_intra_mip || mode == INTRA_PLANAR) { + left_size = h + 1; + top_size = w + 1; + unfilter_left_size = left_size + filter_flag; + unfilter_top_size = top_size + filter_flag; + } else if (mode == INTRA_DC) { + unfilter_left_size = left_size = h; + unfilter_top_size = top_size = w; + } else if (mode == INTRA_VERT) { + //we may need 1 pixel to predict the top left. + unfilter_left_size = left_size = need_pdpc ? h : 1; + unfilter_top_size = top_size = w; + } else if (mode == INTRA_HORZ) { + unfilter_left_size = left_size = h; + //even need_pdpc == 0, we may need 1 pixel to predict the top left. + unfilter_top_size = top_size = need_pdpc ? w : 1; + } else { + if (cu->isp_split_type == ISP_NO_SPLIT || c_idx) { + refw = w * 2; + refh = h * 2; + } else { + refw = cu->cb_width + w; + refh = cu->cb_height + h; + } + intra_pred_angle = ff_vvc_intra_pred_angle_derive(mode); + inv_angle = ff_vvc_intra_inv_angle_derive(intra_pred_angle); + unfilter_top_size = top_size = refw; + unfilter_left_size = left_size = refh; + } + + left_available = ff_vvc_get_left_available(lc, x, y, unfilter_left_size, c_idx); + for (i = 0; i < left_available; i++) + left[i] = POS(ref_line, i); + + top_available = ff_vvc_get_top_available(lc, x, y, unfilter_top_size, c_idx); + memcpy(top, src + ref_line * stride, top_available * sizeof(pixel)); + + for (int i = -1; i >= ref_line; i--) { + if (cand_up_left) { + left[i] = POS(ref_line, i); + top[i] = POS(i, ref_line); + } else if (left_available) { + left[i] = top[i] = left[0]; + } else if (top_available) { + left[i] = top[i] = top[0]; + } else { + left[i] = top[i] = 1 << (BIT_DEPTH - 1); + } + } + + EXTEND(top + top_available, top[top_available-1], unfilter_top_size - top_available); + EXTEND(left + left_available, left[left_available-1], unfilter_left_size - left_available); + + if (ref_filter_flag) { + if (!ref_idx && w * h > 32 && !c_idx && cu->isp_split_type == ISP_NO_SPLIT ) { + const int unfilter_last_one = left_size == unfilter_left_size; + FUNC(ref_filter)(left, top, filtered_left, filtered_top, unfilter_left_size, unfilter_top_size, unfilter_last_one); + left = filtered_left; + top = filtered_top; + } + } + if (!is_intra_mip && mode != INTRA_PLANAR && mode != INTRA_DC) { + if (ref_filter_flag || ref_idx || cu->isp_split_type != ISP_NO_SPLIT) { + edge->filter_flag = 0; + } else { + const int min_dist_ver_hor = FFMIN(abs(mode - 50), abs(mode - 18)); + const int intra_hor_ver_dist_thres[] = {24, 14, 2, 0, 0}; + const int ntbs = (av_log2(w) + av_log2(h)) >> 1; + edge->filter_flag = min_dist_ver_hor > intra_hor_ver_dist_thres[ntbs - 2]; + } + + if (mode != INTRA_VERT && mode != INTRA_HORZ) { + if (mode >= INTRA_DIAG) { + if (intra_pred_angle < 0) { + pixel *p = top - (ref_idx + 1); + for (int x = -h; x < 0; x++) { + const int idx = -1 - ref_idx + FFMIN((x*inv_angle + 256) >> 9, h); + p[x] = left[idx]; + } + } else { + for (int i = refw; i <= refw + FFMAX(1, w/h) * ref_idx + 1; i++) + top[i] = top[refw - 1]; + } + } else { + if (intra_pred_angle < 0) { + pixel *p = left - (ref_idx + 1); + for (int x = -w; x < 0; x++) { + const int idx = -1 - ref_idx + FFMIN((x*inv_angle + 256) >> 9, w); + p[x] = top[idx]; + } + } else { + for (int i = refh; i <= refh + FFMAX(1, h/w) * ref_idx + 1; i++) + left[i] = left[refh - 1]; + } + } + } + } + edge->left = (uint8_t*)left; + edge->top = (uint8_t*)top; +} + +//8.4.1 General decoding process for coding units coded in intra prediction mode +static void FUNC(intra_pred)(const VVCLocalContext *lc, int x0, int y0, + const int width, const int height, int c_idx) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const CodingUnit *cu = lc->cu; + const int log2_min_cb_size = sps->min_cb_log2_size_y; + const int min_cb_width = pps->min_cb_width; + const int x_cb = x0 >> log2_min_cb_size; + const int y_cb = y0 >> log2_min_cb_size; + + const int hshift = fc->ps.sps->hshift[c_idx]; + const int vshift = fc->ps.sps->vshift[c_idx]; + const int x = x0 >> hshift; + const int y = y0 >> vshift; + const int w = width >> hshift; + const int h = height >> vshift; + const ptrdiff_t stride = fc->frame->linesize[c_idx] / sizeof(pixel); + + const int pred_mode = c_idx ? cu->intra_pred_mode_c : cu->intra_pred_mode_y; + const int mode = ff_vvc_wide_angle_mode_mapping(cu, w, h, c_idx, pred_mode); + + const int intra_mip_flag = SAMPLE_CTB(fc->tab.imf, x_cb, y_cb); + const int is_intra_mip = intra_mip_flag && (!c_idx || cu->mip_chroma_direct_flag); + const int ref_idx = c_idx ? 0 : cu->intra_luma_ref_idx; + const int need_pdpc = ff_vvc_need_pdpc(w, h, cu->bdpcm_flag[c_idx], mode, ref_idx); + + + pixel *src = (pixel*)fc->frame->data[c_idx] + x + y * stride; + IntraEdgeParams edge; + + FUNC(prepare_intra_edge_params)(lc, &edge, src, stride, x, y, w, h, c_idx, is_intra_mip, mode, ref_idx, need_pdpc); + + if (is_intra_mip) { + int intra_mip_transposed_flag = SAMPLE_CTB(fc->tab.imtf, x_cb, y_cb); + int intra_mip_mode = SAMPLE_CTB(fc->tab.imm, x_cb, y_cb); + + fc->vvcdsp.intra.pred_mip((uint8_t *)src, edge.top, edge.left, + w, h, stride, intra_mip_mode, intra_mip_transposed_flag); + } else if (mode == INTRA_PLANAR) { + fc->vvcdsp.intra.pred_planar((uint8_t *)src, edge.top, edge.left, w, h, stride); + } else if (mode == INTRA_DC) { + fc->vvcdsp.intra.pred_dc((uint8_t *)src, edge.top, edge.left, w, h, stride); + } else if (mode == INTRA_VERT) { + fc->vvcdsp.intra.pred_v((uint8_t *)src, edge.top, w, h, stride); + } else if (mode == INTRA_HORZ) { + fc->vvcdsp.intra.pred_h((uint8_t *)src, edge.left, w, h, stride); + } else { + if (mode >= INTRA_DIAG) { + fc->vvcdsp.intra.pred_angular_v((uint8_t *)src, edge.top, edge.left, + w, h, stride, c_idx, mode, ref_idx, + edge.filter_flag, need_pdpc); + } else { + fc->vvcdsp.intra.pred_angular_h((uint8_t *)src, edge.top, edge.left, + w, h, stride, c_idx, mode, ref_idx, + edge.filter_flag, need_pdpc); + } + } + if (need_pdpc) { + //8.4.5.2.15 Position-dependent intra prediction sample filtering process + if (!is_intra_mip && (mode == INTRA_PLANAR || mode == INTRA_DC || + mode == INTRA_VERT || mode == INTRA_HORZ)) { + const int scale = (av_log2(w) + av_log2(h) - 2) >> 2; + const pixel *left = (pixel*)edge.left; + const pixel *top = (pixel*)edge.top; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int l, t, wl, wt, pred; + pixel val; + if (mode == INTRA_PLANAR || mode == INTRA_DC) { + l = left[y]; + t = top[x]; + wl = 32 >> FFMIN((x << 1) >> scale, 31); + wt = 32 >> FFMIN((y << 1) >> scale, 31); + } else { + l = left[y] - left[-1] + POS(x,y); + t = top[x] - top[-1] + POS(x,y); + wl = (mode == INTRA_VERT) ? (32 >> FFMIN((x << 1) >> scale, 31)) : 0; + wt = (mode == INTRA_HORZ) ? (32 >> FFMIN((y << 1) >> scale, 31)) : 0; + } + val = POS(x, y); + pred = val + ((wl * (l - val) + wt * (t - val) + 32) >> 6); + POS(x, y) = CLIP(pred); + } + } + } + } +} + +//8.4.5.2.11 Specification of INTRA_PLANAR intra prediction mode +static av_always_inline void FUNC(pred_planar)(uint8_t *_src, const uint8_t *_top, + const uint8_t *_left, const int w, const int h, const ptrdiff_t stride) +{ + int x, y; + pixel *src = (pixel *)_src; + const pixel *top = (const pixel *)_top; + const pixel *left = (const pixel *)_left; + const int logw = av_log2(w); + const int logh = av_log2(h); + const int size = w * h; + const int shift = (logw + logh + 1); + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + const int pred_v = ((h - 1 - y) * top[x] + (y + 1) * left[h]) << logw; + const int pred_h = ((w - 1 - x) * left[y] + (x + 1) * top[w]) << logh; + const int pred = (pred_v + pred_h + size) >> shift; + POS(x, y) = pred; + } + } +} + +//8.4.5.2.3 MIP boundary sample downsampling process +static av_always_inline void FUNC(mip_downsampling)(int *reduced, const int boundary_size, + const pixel *ref, const int n_tb_s) +{ + const int b_dwn = n_tb_s / boundary_size; + const int log2 = av_log2(b_dwn); + + if (boundary_size == n_tb_s) { + for (int i = 0; i < n_tb_s; i++) + reduced[i] = ref[i]; + return; + } + for (int i = 0; i < boundary_size; i++) { + int r; + r = *ref++; + for (int j = 1; j < b_dwn; j++) + r += *ref++; + reduced[i] = (r + (1 << (log2 - 1))) >> log2; + } +} + +static av_always_inline void FUNC(mip_reduced_pred)(pixel *src, const ptrdiff_t stride, + const int up_hor, const int up_ver, const int pred_size, const int *reduced, const int reduced_size, + const int ow, const int temp0, const uint8_t *matrix, int is_transposed) +{ + src = &POS(up_hor - 1, up_ver - 1); + for (int y = 0; y < pred_size; y++) { + for (int x = 0; x < pred_size; x++) { + int pred = 0; + for (int i = 0; i < reduced_size; i++) + pred += reduced[i] * matrix[i]; + matrix += reduced_size; + pred = ((pred + ow) >> 6) + temp0; + pred = av_clip(pred, 0, (1< 1 || up_ver > 1) { + if (up_hor > 1) + FUNC(mip_upsampling_1d)(&POS(0, up_ver - 1), 1, up_ver * stride, pred_size, up_hor, left + up_ver - 1, up_ver, pred_size); + if (up_ver > 1) + FUNC(mip_upsampling_1d)(src, stride, 1, w, up_ver, top, 1, pred_size); + } +} + +static av_always_inline pixel FUNC(pred_dc_val)(const pixel *top, const pixel *left, + const int w, const int h) +{ + pixel dc_val; + int sum = 0; + unsigned int offset = (w == h) ? (w << 1) : FFMAX(w, h); + const int shift = av_log2(offset); + offset >>= 1; + if (w >= h) { + for (int i = 0; i < w; i++) + sum += top[i]; + } + if (w <= h) { + for (int i = 0; i < h; i++) + sum += left[i]; + } + dc_val = (sum + offset) >> shift; + return dc_val; +} + +//8.4.5.2.12 Specification of INTRA_DC intra prediction mode +static av_always_inline void FUNC(pred_dc)(uint8_t *_src, const uint8_t *_top, + const uint8_t *_left, const int w, const int h, const ptrdiff_t stride) +{ + int x, y; + pixel *src = (pixel *)_src; + const pixel *top = (const pixel *)_top; + const pixel *left = (const pixel *)_left; + const pixel dc = FUNC(pred_dc_val)(top, left, w, h); + const pixel4 a = PIXEL_SPLAT_X4(dc); + for (y = 0; y < h; y++) { + pixel *s = src; + for (x = 0; x < w; x += 4) { + AV_WN4P(s, a); + s += 4; + } + src += stride; + } +} + +static av_always_inline void FUNC(pred_v)(uint8_t *_src, const uint8_t *_top, + const int w, const int h, const ptrdiff_t stride) +{ + pixel *src = (pixel *)_src; + const pixel *top = (const pixel *)_top; + for (int y = 0; y < h; y++) { + memcpy(src, top, sizeof(pixel) * w); + src += stride; + } +} + +static void FUNC(pred_h)(uint8_t *_src, const uint8_t *_left, const int w, const int h, + const ptrdiff_t stride) +{ + pixel *src = (pixel *)_src; + const pixel *left = (const pixel *)_left; + for (int y = 0; y < h; y++) { + const pixel4 a = PIXEL_SPLAT_X4(left[y]); + for (int x = 0; x < w; x += 4) { + AV_WN4P(&POS(x, y), a); + } + } +} + +#define INTRA_LUMA_FILTER(p) CLIP((p[0] * f[0] + p[1] * f[1] + p[2] * f[2] + p[3] * f[3] + 32) >> 6) +#define INTRA_CHROMA_FILTER(p) (((32 - fact) * p[1] + fact * p[2] + 16) >> 5) + +//8.4.5.2.13 Specification of INTRA_ANGULAR2..INTRA_ANGULAR66 intra prediction modes +static void FUNC(pred_angular_v)(uint8_t *_src, const uint8_t *_top, const uint8_t *_left, + const int w, const int h, const ptrdiff_t stride, const int c_idx, const int mode, + const int ref_idx, const int filter_flag, const int need_pdpc) +{ + pixel *src = (pixel *)_src; + const pixel *left = (const pixel *)_left; + const pixel *top = (const pixel *)_top - (1 + ref_idx); + const int intra_pred_angle = ff_vvc_intra_pred_angle_derive(mode); + int pos = (1 + ref_idx) * intra_pred_angle; + const int dp = intra_pred_angle; + const int is_luma = !c_idx; + int nscale, inv_angle; + + if (need_pdpc) { + inv_angle = ff_vvc_intra_inv_angle_derive(intra_pred_angle); + nscale = ff_vvc_nscale_derive(w, h, mode); + } + + for (int y = 0; y < h; y++) { + const int idx = (pos >> 5) + ref_idx; + const int fact = pos & 31; + if (!fact && (!is_luma || !filter_flag)) { + for (int x = 0; x < w; x++) { + const pixel *p = top + x + idx + 1; + POS(x, y) = *p; + } + } else { + if (!c_idx) { + const int8_t *f = ff_vvc_intra_luma_filter[filter_flag][fact]; + for (int x = 0; x < w; x++) { + const pixel *p = top + x + idx; + POS(x, y) = INTRA_LUMA_FILTER(p); + } + } else { + for (int x = 0; x < w; x++) { + const pixel *p = top + x + idx; + POS(x, y) = INTRA_CHROMA_FILTER(p); + } + } + } + if (need_pdpc) { + int inv_angle_sum = 256 + inv_angle; + for (int x = 0; x < FFMIN(w, 3 << nscale); x++) { + const pixel l = left[y + (inv_angle_sum >> 9)]; + const pixel val = POS(x, y); + const int wl = 32 >> ((x << 1) >> nscale); + const int pred = val + (((l - val) * wl + 32) >> 6); + POS(x, y) = CLIP(pred); + inv_angle_sum += inv_angle; + } + } + pos += dp; + } +} + +//8.4.5.2.13 Specification of INTRA_ANGULAR2..INTRA_ANGULAR66 intra prediction modes +static void FUNC(pred_angular_h)(uint8_t *_src, const uint8_t *_top, const uint8_t *_left, + const int w, const int h, const ptrdiff_t stride, const int c_idx, const int mode, + const int ref_idx, const int filter_flag, const int need_pdpc) +{ + pixel *src = (pixel *)_src; + const pixel *left = (const pixel *)_left - (1 + ref_idx); + const pixel *top = (const pixel *)_top; + const int is_luma = !c_idx; + const int intra_pred_angle = ff_vvc_intra_pred_angle_derive(mode); + const int dp = intra_pred_angle; + int nscale = 0, inv_angle, inv_angle_sum; + + if (need_pdpc) { + inv_angle = ff_vvc_intra_inv_angle_derive(intra_pred_angle); + inv_angle_sum = 256 + inv_angle; + nscale = ff_vvc_nscale_derive(w, h, mode); + } + + for (int y = 0; y < h; y++) { + int pos = (1 + ref_idx) * intra_pred_angle; + int wt; + if (need_pdpc) + wt = (32 >> ((y * 2) >> nscale)); + + for (int x = 0; x < w; x++) { + const int idx = (pos >> 5) + ref_idx; + const int fact = pos & 31; + const pixel *p = left + y + idx; + int pred; + if (!fact && (!is_luma || !filter_flag)) { + pred = p[1]; + } else { + if (!c_idx) { + const int8_t *f = ff_vvc_intra_luma_filter[filter_flag][fact]; + pred = INTRA_LUMA_FILTER(p); + } else { + pred = INTRA_CHROMA_FILTER(p); + } + } + if (need_pdpc) { + if (y < (3 << nscale)) { + const pixel t = top[x + (inv_angle_sum >> 9)]; + pred = CLIP(pred + (((t - pred) * wt + 32) >> 6)); + } + } + POS(x, y) = pred; + pos += dp; + } + if (need_pdpc) + inv_angle_sum += inv_angle; + } +} + +static void FUNC(ff_vvc_intra_dsp_init)(VVCIntraDSPContext *const intra) +{ + intra->lmcs_scale_chroma = FUNC(lmcs_scale_chroma); + intra->intra_cclm_pred = FUNC(intra_cclm_pred); + intra->intra_pred = FUNC(intra_pred); + intra->pred_planar = FUNC(pred_planar); + intra->pred_mip = FUNC(pred_mip); + intra->pred_dc = FUNC(pred_dc); + intra->pred_v = FUNC(pred_v); + intra->pred_h = FUNC(pred_h); + intra->pred_angular_v = FUNC(pred_angular_v); + intra->pred_angular_h = FUNC(pred_angular_h); +} From patchwork Sun Nov 12 10:35:22 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 44628 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:92a5:b0:181:818d:5e7f with SMTP id q37csp728490pzg; Sun, 12 Nov 2023 02:37:46 -0800 (PST) X-Google-Smtp-Source: AGHT+IEfDMf4V7Gz5qRnl9mDCw/EaolfaWKNz2sv9Jkntv6Vd5szYvaoLYijAwEVkhP18mDOHYBq X-Received: by 2002:aa7:c548:0:b0:543:5fc0:de22 with SMTP id s8-20020aa7c548000000b005435fc0de22mr2815519edr.24.1699785466474; Sun, 12 Nov 2023 02:37:46 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id fk25-20020a056402399900b00540c538f270si1649528edb.450.2023.11.12.02.37.45; Sun, 12 Nov 2023 02:37:46 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@outlook.com header.s=selector1 header.b=eT3OKPgh; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 9B8D968CAA2; Sun, 12 Nov 2023 12:36:25 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from APC01-SG2-obe.outbound.protection.outlook.com (mail-sgaapc01olkn2035.outbound.protection.outlook.com [40.92.53.35]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id AF80A68CC68 for ; Sun, 12 Nov 2023 12:36:19 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=DEhY/JEtU2nZmDLvrKYWiMa8yj/IRraJpFRS7VP3Y9VFUupRDpMeQ98Pu4/E7ZUCuMMn2ioQMD8xBtuIMXKPocg+DYE7949u6Kyz/AH8kAvGz55S0iKzR+yTz1UxpAToan6dt8hltCsouRJHh6IHTSIodr+fqwpjAEtLwjHFtBdT68kpro8Sb50niQ8lq4IDHDWZb96zLgAMpoV9RZ1WyXaD5na1OGKeEjgOYiOL5mX35ViGISOQlQVlIuAU/TxlIyWxiB6czEW3zByekqS6xRe/jqKLIPWfBYjv9f81T6npVYcHZwKcw7tbvyQ/ubfgmcOURo9ZW+a2ZSCcc4f75A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=0VGOrNi9cQ7Uw6k6rslfHJnCzvK8dzHYLBZibllGTtQ=; b=MPBAv8lFegLGsnaoBj89u8tepDAWEXEGyTA9R/WO9S0ZPv0NC7aFptuLmhGJssXpw7/hHtYRO1alIswsXAETdAhS6lEhxjpKGx8FJ7JMnmY3Y3MFau9BNnqZDK+GT3taoJSMZ4JAa0ddgO1W4zPu+XZcBQ4R6Us1kYUMlREQS32LYA9VJKyVaXUGOtOo+G+VX6gu8PZZuVYm1f4HEDz/id7u7XvDWbYeAF+WSgodc/uqhqTJsyZREMvAziyzJYRISvQS5ltuEl3vYu/2paq6CRjhInkHMtzoI9satkQgSgCo2bBIGN9QbYz3FXgo41KLbRHPaYVw09jq+vp+5x86/g== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=outlook.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=0VGOrNi9cQ7Uw6k6rslfHJnCzvK8dzHYLBZibllGTtQ=; b=eT3OKPghtiYwFpgdIq6Rhkzm5O7n5yKNRsx4SGpk2na0AhSnvv/8VZ2aveF+/rC9hvZHnKpdiDEg92wa3U5UdIEDAfnyMGWl12vSfVO5DuO1l1+iKlDTKU9Jsav9jnlA6BP6DvRR2SymcoVQlbBUKfK+rE2uoBzXZUYYecvOb/l7Tc6bVIYun2OLo+Nv4IZI4ktBMvn3Mt1SRAjwahLcdW6Wz+Szg3wTCsYbemkl9in6aDsce59ZCMziUPTK4aOqGtREfitnFog/heILkkXbPcbNndyEWRvNolVNPvScuB6OLqcKquk2hPlsFPhLfb3j4BymRae7s1cwYexvNiB/Zw== Received: from TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) by TYSPR06MB6768.apcprd06.prod.outlook.com (2603:1096:400:475::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6954.29; Sun, 12 Nov 2023 10:36:01 +0000 Received: from TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f]) by TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f%4]) with mapi id 15.20.6954.028; Sun, 12 Nov 2023 10:36:01 +0000 From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Nov 2023 18:35:22 +0800 Message-ID: X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231112103526.11245-1-nuomi2021@gmail.com> References: <20231112103526.11245-1-nuomi2021@gmail.com> X-TMN: [imd4DsjPqedeuBZHi7FQa0OuCRn36utA] X-ClientProxiedBy: TYWP286CA0030.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:262::20) To TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) X-Microsoft-Original-Message-ID: <20231112103526.11245-10-nuomi2021@gmail.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 2 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: TYSPR06MB6433:EE_|TYSPR06MB6768:EE_ X-MS-Office365-Filtering-Correlation-Id: eb2663db-b4fe-4d0a-384b-08dbe36b2a12 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: PiR5upm9NPFS1XUloFtTAkAXGy5dybQGWN9ebwoOUH4sG8AB9CCuVNPlWkspvJ8zRiqVW89ie75pkFKjTaT4+bleGSdqnMpbCC5g7sJZXZL+F74GAweUgFGGx7vxGMY8ZLblBrXT4BqEL1cFfc8Zkfq+Kt48Q1bh/Y25ajCftQnI+ezREIvM3vDPyahcDB7vT77/iZ/BEWkjNZQokRi4B+nwXc/6W6AXKd16sJz5J4Y0QPurpBmkFj1UvQ6lS14k0wywGxapeWb5oVsX3cY61hs+mKIlfBJdHzn0UqUIlpB+0CA76EIdUrDXOqyr/aWCCEgsTedT5BNLgmAv+CsCY7tidEs0e5IDXSLSj+u5dT7a0wdP9l9Q5la0uJzqUHTWOTF1rXIhIrs+/eLCDz9uioR9JuYH2fe0lQxho0mSDBVN4iDr3zcu63KYYn+Zpbxatzbo8Fm4LAI/quRpOzOhXGyl2OAn8DgjFrV5BkUAQCMotUf5XD0j7wrZEZhA2nOAMIdEOGdiVI+OCab0rpv+rfpJg+Gnr2nEztTg3ZrG1i2QEaIHDs2anwCLij7gJ8cDf2H0zeL9JzjmOe46292H2aYvXr2iVCVMpgfLI7Xqj6g= X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?q?5nO2DSGVXolV+UNnzXJ6SsYEAvzE?= =?utf-8?q?HXLXk6Wlv77kLtDFO1iYqyDCcmYo43MMKTpG2XZkMIkvI8VJSWxtO292XYDlkXFyF?= =?utf-8?q?wqG8CKHLHN4BkoeTlAcBbJhcbxweMjOil3ksLgiYWEMwq9XFPNXlkTAswlLGcr3y5?= =?utf-8?q?P1MzGiO/cDRBxdFQJEr4Bc9SS8Wpf6p4g5DgR28t900eaMYT9Iwmreo5ElX3mz+P8?= =?utf-8?q?//225TnRscPi+lPrKIk/PixqQIfC7l9LWevAZNJiL6l/67gwrbCx3yHYx7d36xp5e?= =?utf-8?q?uMGzsYzD/zH1VaCyi5GF5+zrWn/LG2AKOtRnBXnQwipEzvWnE5dDfKD9q0juZlZds?= =?utf-8?q?qSH/Zlv7eSO067u7krhzHTr4gpBK0/jfogVych8zh6U7Nnshv7E/BPjA99JJdJuET?= =?utf-8?q?Jyf++0xvQEPKbYrLdKO8Cn68dzrtiHxm1YQf0/Umd6zmbG9hW8qxgQPO1QnaEkq/g?= =?utf-8?q?HYqYgQzzfU1hNc/Q6RuYRx6aYHM2ngD5fjeOhw2ehGCbvNuu36P/YDYhXA/HcxFSH?= =?utf-8?q?0i/sPjwHZOYrhVJii0J+9msLLyWTKOAlP1cMr2ehqNf+IGCEJaZkST2ARMBx2o4rB?= =?utf-8?q?yOhIQRzS8mq442bR6z1Tb7oyLOvy6ma/RABcHjf5ji/LTbFhoxI3KUMtXJftioYSy?= =?utf-8?q?JGno8+BY1uSDvX2ROy+39SFizT+YVVIZVP5Csfz2PqsMnK9TFfbrNW8keHLKBPfd5?= =?utf-8?q?e+BzeV5ljREU+MDbULGTSSN29xr1zJSCetKe61+ulWt6lAz3wiSLhjWiPQgOmd3dg?= =?utf-8?q?STKE1nhPNPIuOQaeo9YevudZEeGwDq0Sm/Q6KpwuMg1XQJ8g0lAiea80nchTsU5ca?= =?utf-8?q?D9s98s7wzzA42h7XsOJCqB+yIKZbS1RxIHA0wEI6dKNYfOZCD7zU45W2sMdtK1ZQP?= =?utf-8?q?fASuyPmgv2rY5BxkxqBbQxIzbZ5CxAaqiL6grDGxhlz8xv1G+4fUmLVlJ8T0Fn6LB?= =?utf-8?q?cCOZ0YL7xTllkh+8KfX3xrBrrhBFqDD832ydb429Ss7aMyYWNuFP3Tg1nYxJWx2p5?= =?utf-8?q?nIipmKkHmTpHGstlexKP7dcOJLOQZrV7QUMXJw9Tzh5gAULHfoluiuI37yVqAAZHt?= =?utf-8?q?hgMsx0lQ6f+oqooJXof8jS6l6FtACWSq4t4CfCoed9yKEiFYwglkxExBhxJetHhb8?= =?utf-8?q?6VIaQydLsLNVLZKYcy49Nuh9J4xQKeEl3V8tRUmC6wtLeIC0wVqW46K+AuuUF1Idx?= =?utf-8?q?B3DcUgxrfQj8jlW7a?= X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: eb2663db-b4fe-4d0a-384b-08dbe36b2a12 X-MS-Exchange-CrossTenant-AuthSource: TYSPR06MB6433.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Nov 2023 10:36:01.2756 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYSPR06MB6768 Subject: [FFmpeg-devel] [PATCH v5 10/14] vvcdec: add LMCS, Deblocking, SAO, and ALF filters X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Nuo Mi Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: ac0U2lzbE7B1 --- libavcodec/vvc/Makefile | 1 + libavcodec/vvc/vvc_ctu.h | 1 + libavcodec/vvc/vvc_filter.c | 1348 ++++++++++++++++++++++++++ libavcodec/vvc/vvc_filter.h | 71 ++ libavcodec/vvc/vvc_filter_template.c | 1135 ++++++++++++++++++++++ 5 files changed, 2556 insertions(+) create mode 100644 libavcodec/vvc/vvc_filter.c create mode 100644 libavcodec/vvc/vvc_filter.h create mode 100644 libavcodec/vvc/vvc_filter_template.c diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile index 3b1ac72029..9e7fef7d38 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -5,6 +5,7 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ vvc/vvc_cabac.o \ vvc/vvc_ctu.o \ vvc/vvc_data.o \ + vvc/vvc_filter.o \ vvc/vvc_inter.o \ vvc/vvc_intra.o \ vvc/vvc_itx_1d.o \ diff --git a/libavcodec/vvc/vvc_ctu.h b/libavcodec/vvc/vvc_ctu.h index 6925b621a6..3f5e677fad 100644 --- a/libavcodec/vvc/vvc_ctu.h +++ b/libavcodec/vvc/vvc_ctu.h @@ -462,6 +462,7 @@ typedef struct ALFParams { void ff_vvc_set_neighbour_available(VVCLocalContext *lc, int x0, int y0, int w, int h); void ff_vvc_decode_neighbour(VVCLocalContext *lc, int x_ctb, int y_ctb, int rx, int ry, int rs); void ff_vvc_ctu_free_cus(CTU *ctu); +int ff_vvc_get_qPy(const VVCFrameContext *fc, int xc, int yc); void ff_vvc_ep_init_stat_coeff(EntryPoint *ep, int bit_depth, int persistent_rice_adaptation_enabled_flag); #endif // AVCODEC_VVC_CTU_H diff --git a/libavcodec/vvc/vvc_filter.c b/libavcodec/vvc/vvc_filter.c new file mode 100644 index 0000000000..19dcdbe3c8 --- /dev/null +++ b/libavcodec/vvc/vvc_filter.c @@ -0,0 +1,1348 @@ +/* + * VVC filters + * + * Copyright (C) 2021 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "libavutil/frame.h" + +#include "vvc_ctu.h" +#include "vvc_data.h" +#include "vvc_filter.h" +#include "vvc_refs.h" + +#define LEFT 0 +#define TOP 1 +#define RIGHT 2 +#define BOTTOM 3 +#define MAX_EDGES 4 + +#define DEFAULT_INTRA_TC_OFFSET 2 + +//Table 43 Derivation of threshold variables beta' and tc' from input Q +static const uint16_t tctable[66] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 4, 4, 4, 4, 5, 5, 5, 5, 7, 7, 8, 9, 10, + 10, 11, 13, 14, 15, 17, 19, 21, 24, 25, 29, 33, 36, 41, 45, 51, + 57, 64, 71, 80, 89, 100, 112, 125, 141, 157, 177, 198, 222, 250, 280, 314, + 352, 395, + +}; + +//Table 43 Derivation of threshold variables beta' and tc' from input Q +static const uint8_t betatable[64] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 22, 24, + 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, + 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, +}; + + +static int get_qPc(const VVCFrameContext *fc, const int x0, const int y0, const int chroma) +{ + const int x = x0 >> MIN_TU_LOG2; + const int y = y0 >> MIN_TU_LOG2; + const int min_tu_width = fc->ps.pps->min_tu_width; + return fc->tab.qp[chroma][x + y * min_tu_width]; +} + +static void copy_ctb(uint8_t *dst, const uint8_t *src, const int width, const int height, + const ptrdiff_t dst_stride, const ptrdiff_t src_stride) +{ + for (int y = 0; y < height; y++) { + memcpy(dst, src, width); + + dst += dst_stride; + src += src_stride; + } +} + +static void copy_pixel(uint8_t *dst, const uint8_t *src, const int pixel_shift) +{ + if (pixel_shift) + *(uint16_t *)dst = *(uint16_t *)src; + else + *dst = *src; +} + +static void copy_vert(uint8_t *dst, const uint8_t *src, const int pixel_shift, const int height, + const ptrdiff_t dst_stride, const ptrdiff_t src_stride) +{ + int i; + if (pixel_shift == 0) { + for (i = 0; i < height; i++) { + *dst = *src; + dst += dst_stride; + src += src_stride; + } + } else { + for (i = 0; i < height; i++) { + *(uint16_t *)dst = *(uint16_t *)src; + dst += dst_stride; + src += src_stride; + } + } +} + +static void copy_ctb_to_hv(VVCFrameContext *fc, const uint8_t *src, + const ptrdiff_t src_stride, const int x, const int y, const int width, const int height, + const int c_idx, const int x_ctb, const int y_ctb, const int top) +{ + int ps = fc->ps.sps->pixel_shift; + int w = fc->ps.sps->width >> fc->ps.sps->hshift[c_idx]; + int h = fc->ps.sps->height >> fc->ps.sps->vshift[c_idx]; + + if (top) { + /* top */ + memcpy(fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb) * w + x) << ps), + src, width << ps); + } else { + /* bottom */ + memcpy(fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb + 1) * w + x) << ps), + src + src_stride * (height - 1), width << ps); + + /* copy vertical edges */ + copy_vert(fc->tab.sao_pixel_buffer_v[c_idx] + (((2 * x_ctb) * h + y) << ps), src, ps, height, 1 << ps, src_stride); + copy_vert(fc->tab.sao_pixel_buffer_v[c_idx] + (((2 * x_ctb + 1) * h + y) << ps), src + ((width - 1) << ps), ps, height, 1 << ps, src_stride); + } +} + +static void sao_copy_ctb_to_hv(VVCLocalContext *lc, const int rx, const int ry, const int top) +{ + VVCFrameContext *fc = lc->fc; + const int ctb_size_y = fc->ps.sps->ctb_size_y; + const int x0 = rx << fc->ps.sps->ctb_log2_size_y; + const int y0 = ry << fc->ps.sps->ctb_log2_size_y; + + for (int c_idx = 0; c_idx < (fc->ps.sps->r->sps_chroma_format_idc ? 3 : 1); c_idx++) { + const int x = x0 >> fc->ps.sps->hshift[c_idx]; + const int y = y0 >> fc->ps.sps->vshift[c_idx]; + const ptrdiff_t src_stride = fc->frame->linesize[c_idx]; + const int ctb_size_h = ctb_size_y >> fc->ps.sps->hshift[c_idx]; + const int ctb_size_v = ctb_size_y >> fc->ps.sps->vshift[c_idx]; + const int width = FFMIN(ctb_size_h, (fc->ps.sps->width >> fc->ps.sps->hshift[c_idx]) - x); + const int height = FFMIN(ctb_size_v, (fc->ps.sps->height >> fc->ps.sps->vshift[c_idx]) - y); + uint8_t *src = &fc->frame->data[c_idx][y * src_stride + (x << fc->ps.sps->pixel_shift)]; + copy_ctb_to_hv(fc, src, src_stride, x, y, width, height, c_idx, rx, ry, top); + } + +} + +void ff_vvc_sao_copy_ctb_to_hv(VVCLocalContext *lc, const int rx, const int ry, const int last_row) +{ + if (ry) + sao_copy_ctb_to_hv(lc, rx, ry - 1, 0); + + sao_copy_ctb_to_hv(lc, rx, ry, 1); + + if (last_row) + sao_copy_ctb_to_hv(lc, rx, ry, 0); +} + +void ff_vvc_sao_filter(VVCLocalContext *lc, int x, int y) +{ + VVCFrameContext *fc = lc->fc; + const int ctb_size_y = fc->ps.sps->ctb_size_y; + static const uint8_t sao_tab[16] = { 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8 }; + int c_idx; + int edges[4]; // 0 left 1 top 2 right 3 bottom + int x_ctb = x >> fc->ps.sps->ctb_log2_size_y; + int y_ctb = y >> fc->ps.sps->ctb_log2_size_y; + SAOParams *sao = &CTB(fc->tab.sao, x_ctb, y_ctb); + // flags indicating unfilterable edges + uint8_t vert_edge[] = { 0, 0 }; + uint8_t horiz_edge[] = { 0, 0 }; + uint8_t diag_edge[] = { 0, 0, 0, 0 }; + uint8_t lfase = fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag; + uint8_t no_tile_filter = fc->ps.pps->r->num_tiles_in_pic > 1 && + !fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag; + uint8_t restore = no_tile_filter || !lfase; + uint8_t left_tile_edge = 0; + uint8_t right_tile_edge = 0; + uint8_t up_tile_edge = 0; + uint8_t bottom_tile_edge = 0; + + edges[LEFT] = x_ctb == 0; + edges[TOP] = y_ctb == 0; + edges[RIGHT] = x_ctb == fc->ps.pps->ctb_width - 1; + edges[BOTTOM] = y_ctb == fc->ps.pps->ctb_height - 1; + + if (restore) { + if (!edges[LEFT]) { + left_tile_edge = no_tile_filter && fc->ps.pps->ctb_to_col_bd[x_ctb] == x_ctb; + vert_edge[0] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb - 1, y_ctb)) || left_tile_edge; + } + if (!edges[RIGHT]) { + right_tile_edge = no_tile_filter && fc->ps.pps->ctb_to_col_bd[x_ctb] != fc->ps.pps->ctb_to_col_bd[x_ctb + 1]; + vert_edge[1] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb + 1, y_ctb)) || right_tile_edge; + } + if (!edges[TOP]) { + up_tile_edge = no_tile_filter && fc->ps.pps->ctb_to_row_bd[y_ctb] == y_ctb; + horiz_edge[0] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb, y_ctb - 1)) || up_tile_edge; + } + if (!edges[BOTTOM]) { + bottom_tile_edge = no_tile_filter && fc->ps.pps->ctb_to_row_bd[y_ctb] != fc->ps.pps->ctb_to_row_bd[y_ctb + 1]; + horiz_edge[1] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb, y_ctb + 1)) || bottom_tile_edge; + } + if (!edges[LEFT] && !edges[TOP]) { + diag_edge[0] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb - 1, y_ctb - 1)) || left_tile_edge || up_tile_edge; + } + if (!edges[TOP] && !edges[RIGHT]) { + diag_edge[1] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb + 1, y_ctb - 1)) || right_tile_edge || up_tile_edge; + } + if (!edges[RIGHT] && !edges[BOTTOM]) { + diag_edge[2] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb + 1, y_ctb + 1)) || right_tile_edge || bottom_tile_edge; + } + if (!edges[LEFT] && !edges[BOTTOM]) { + diag_edge[3] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb - 1, y_ctb + 1)) || left_tile_edge || bottom_tile_edge; + } + } + + for (c_idx = 0; c_idx < (fc->ps.sps->r->sps_chroma_format_idc ? 3 : 1); c_idx++) { + int x0 = x >> fc->ps.sps->hshift[c_idx]; + int y0 = y >> fc->ps.sps->vshift[c_idx]; + ptrdiff_t src_stride = fc->frame->linesize[c_idx]; + int ctb_size_h = ctb_size_y >> fc->ps.sps->hshift[c_idx]; + int ctb_size_v = ctb_size_y >> fc->ps.sps->vshift[c_idx]; + int width = FFMIN(ctb_size_h, (fc->ps.sps->width >> fc->ps.sps->hshift[c_idx]) - x0); + int height = FFMIN(ctb_size_v, (fc->ps.sps->height >> fc->ps.sps->vshift[c_idx]) - y0); + int tab = sao_tab[(FFALIGN(width, 8) >> 3) - 1]; + uint8_t *src = &fc->frame->data[c_idx][y0 * src_stride + (x0 << fc->ps.sps->pixel_shift)]; + ptrdiff_t dst_stride; + uint8_t *dst; + + switch (sao->type_idx[c_idx]) { + case SAO_BAND: + fc->vvcdsp.sao.band_filter[tab](src, src, src_stride, src_stride, + sao->offset_val[c_idx], sao->band_position[c_idx], width, height); + break; + case SAO_EDGE: + { + int w = fc->ps.sps->width >> fc->ps.sps->hshift[c_idx]; + int h = fc->ps.sps->height >> fc->ps.sps->vshift[c_idx]; + int sh = fc->ps.sps->pixel_shift; + + dst_stride = 2*MAX_PB_SIZE + AV_INPUT_BUFFER_PADDING_SIZE; + dst = lc->sao_buffer + dst_stride + AV_INPUT_BUFFER_PADDING_SIZE; + + if (!edges[TOP]) { + int left = 1 - edges[LEFT]; + int right = 1 - edges[RIGHT]; + const uint8_t *src1; + uint8_t *dst1; + int pos; + + dst1 = dst - dst_stride - (left << sh); + src1 = fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb - 1) * w + x0 - left) << sh); + pos = 0; + if (left) { + copy_pixel(dst1, src1, sh); + pos += (1 << sh); + } + memcpy(dst1 + pos, src1 + pos, width << sh); + if (right) { + pos += width << sh; + copy_pixel(dst1 + pos, src1 + pos, sh); + } + } + if (!edges[BOTTOM]) { + int left = 1 - edges[LEFT]; + int right = 1 - edges[RIGHT]; + const uint8_t *src1; + uint8_t *dst1; + int pos; + + dst1 = dst + height * dst_stride - (left << sh); + src1 = fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb + 2) * w + x0 - left) << sh); + pos = 0; + if (left) { + copy_pixel(dst1, src1, sh); + pos += (1 << sh); + } + memcpy(dst1 + pos, src1 + pos, width << sh); + if (right) { + pos += width << sh; + copy_pixel(dst1 + pos, src1 + pos, sh); + } + } + if (!edges[LEFT]) { + copy_vert(dst - (1 << sh), + fc->tab.sao_pixel_buffer_v[c_idx] + (((2 * x_ctb - 1) * h + y0) << sh), + sh, height, dst_stride, 1 << sh); + } + if (!edges[RIGHT]) { + copy_vert(dst + (width << sh), + fc->tab.sao_pixel_buffer_v[c_idx] + (((2 * x_ctb + 2) * h + y0) << sh), + sh, height, dst_stride, 1 << sh); + } + + copy_ctb(dst, src, width << sh, height, dst_stride, src_stride); + fc->vvcdsp.sao.edge_filter[tab](src, dst, src_stride, sao->offset_val[c_idx], + sao->eo_class[c_idx], width, height); + fc->vvcdsp.sao.edge_restore[restore](src, dst, + src_stride, dst_stride, + sao, + edges, width, + height, c_idx, + vert_edge, + horiz_edge, + diag_edge); + break; + } + } + } +} + +#define TAB_BS(t, x, y) (t)[((y) >> 2) * (fc->tab.bs_width) + ((x) >> 2)] +#define TAB_MAX_LEN(t, x, y) (t)[((y) >> 2) * (fc->tab.bs_width) + ((x) >> 2)] + +//8 samples a time +#define DEBLOCK_STEP 8 +#define LUMA_GRID 4 +#define CHROMA_GRID 8 + +static int boundary_strength(const VVCLocalContext *lc, MvField *curr, MvField *neigh, + const RefPicList *neigh_rpl) +{ + RefPicList *rpl = lc->sc->rpl; + if (curr->pred_flag == PF_BI && neigh->pred_flag == PF_BI) { + // same L0 and L1 + if (rpl[0].list[curr->ref_idx[0]] == neigh_rpl[0].list[neigh->ref_idx[0]] && + rpl[0].list[curr->ref_idx[0]] == rpl[1].list[curr->ref_idx[1]] && + neigh_rpl[0].list[neigh->ref_idx[0]] == neigh_rpl[1].list[neigh->ref_idx[1]]) { + if ((FFABS(neigh->mv[0].x - curr->mv[0].x) >= 8 || FFABS(neigh->mv[0].y - curr->mv[0].y) >= 8 || + FFABS(neigh->mv[1].x - curr->mv[1].x) >= 8 || FFABS(neigh->mv[1].y - curr->mv[1].y) >= 8) && + (FFABS(neigh->mv[1].x - curr->mv[0].x) >= 8 || FFABS(neigh->mv[1].y - curr->mv[0].y) >= 8 || + FFABS(neigh->mv[0].x - curr->mv[1].x) >= 8 || FFABS(neigh->mv[0].y - curr->mv[1].y) >= 8)) + return 1; + else + return 0; + } else if (neigh_rpl[0].list[neigh->ref_idx[0]] == rpl[0].list[curr->ref_idx[0]] && + neigh_rpl[1].list[neigh->ref_idx[1]] == rpl[1].list[curr->ref_idx[1]]) { + if (FFABS(neigh->mv[0].x - curr->mv[0].x) >= 8 || FFABS(neigh->mv[0].y - curr->mv[0].y) >= 8 || + FFABS(neigh->mv[1].x - curr->mv[1].x) >= 8 || FFABS(neigh->mv[1].y - curr->mv[1].y) >= 8) + return 1; + else + return 0; + } else if (neigh_rpl[1].list[neigh->ref_idx[1]] == rpl[0].list[curr->ref_idx[0]] && + neigh_rpl[0].list[neigh->ref_idx[0]] == rpl[1].list[curr->ref_idx[1]]) { + if (FFABS(neigh->mv[1].x - curr->mv[0].x) >= 8 || FFABS(neigh->mv[1].y - curr->mv[0].y) >= 8 || + FFABS(neigh->mv[0].x - curr->mv[1].x) >= 8 || FFABS(neigh->mv[0].y - curr->mv[1].y) >= 8) + return 1; + else + return 0; + } else { + return 1; + } + } else if ((curr->pred_flag != PF_BI) && (neigh->pred_flag != PF_BI)){ // 1 MV + Mv A, B; + int ref_A, ref_B; + + if (curr->pred_flag & 1) { + A = curr->mv[0]; + ref_A = rpl[0].list[curr->ref_idx[0]]; + } else { + A = curr->mv[1]; + ref_A = rpl[1].list[curr->ref_idx[1]]; + } + + if (neigh->pred_flag & 1) { + B = neigh->mv[0]; + ref_B = neigh_rpl[0].list[neigh->ref_idx[0]]; + } else { + B = neigh->mv[1]; + ref_B = neigh_rpl[1].list[neigh->ref_idx[1]]; + } + + if (ref_A == ref_B) { + if (FFABS(A.x - B.x) >= 8 || FFABS(A.y - B.y) >= 8) + return 1; + else + return 0; + } else + return 1; + } + + return 1; +} + +//part of 8.8.3.3 Derivation process of transform block boundary +static void derive_max_filter_length_luma(const VVCFrameContext *fc, const int qx, const int qy, + const int is_intra, const int has_subblock, const int vertical, uint8_t *max_len_p, uint8_t *max_len_q) +{ + const int px = vertical ? qx - 1 : qx; + const int py = !vertical ? qy - 1 : qy; + const uint8_t *tb_size = vertical ? fc->tab.tb_width[LUMA] : fc->tab.tb_height[LUMA]; + const int size_p = tb_size[(py >> MIN_TU_LOG2) * fc->ps.pps->min_tu_width + (px >> MIN_TU_LOG2)]; + const int size_q = tb_size[(qy >> MIN_TU_LOG2) * fc->ps.pps->min_tu_width + (qx >> MIN_TU_LOG2)]; + const int min_cb_log2 = fc->ps.sps->min_cb_log2_size_y; + const int off_p = (py >> min_cb_log2) * fc->ps.pps->min_cb_width + (px >> min_cb_log2); + if (size_p <= 4 || size_q <= 4) { + *max_len_p = *max_len_q = 1; + } else { + *max_len_p = *max_len_q = 3; + if (size_p >= 32) + *max_len_p = 7; + if (size_q >= 32) + *max_len_q = 7; + } + if (has_subblock) + *max_len_q = FFMIN(5, *max_len_q); + if (fc->tab.msf[off_p] || fc->tab.iaf[off_p]) + *max_len_p = FFMIN(5, *max_len_p); +} + +static void vvc_deblock_subblock_bs_vertical(const VVCLocalContext *lc, + const int cb_x, const int cb_y, const int x0, const int y0, const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + MvField *tab_mvf = fc->tab.mvf; + RefPicList *rpl = lc->sc->rpl; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int log2_min_pu_size = MIN_PU_LOG2; + uint8_t max_len_p, max_len_q; + int bs, i, j; + + // bs for TU internal vertical PU boundaries + for (j = 0; j < height; j += 4) { + int y_pu = (y0 + j) >> log2_min_pu_size; + + for (i = 8 - ((x0 - cb_x) % 8); i < width; i += 8) { + int xp_pu = (x0 + i - 1) >> log2_min_pu_size; + int xq_pu = (x0 + i) >> log2_min_pu_size; + MvField *left = &tab_mvf[y_pu * min_pu_width + xp_pu]; + MvField *curr = &tab_mvf[y_pu * min_pu_width + xq_pu]; + const int x = x0 + i; + const int y = y0 + j; + + bs = boundary_strength(lc, curr, left, rpl); + TAB_BS(fc->tab.vertical_bs[LUMA], x, y) = bs; + + + max_len_p = max_len_q = 0; + if (i == 4 || i == width - 4) + max_len_p = max_len_q = 1; + else if (i == 8 || i == width - 8) + max_len_p = max_len_q = 2; + else + max_len_p = max_len_q = 3; + + TAB_MAX_LEN(fc->tab.vertical_p, x, y) = max_len_p; + TAB_MAX_LEN(fc->tab.vertical_q, x, y) = max_len_q; + } + } +} + +static void vvc_deblock_subblock_bs_horizontal(const VVCLocalContext *lc, + const int cb_x, const int cb_y, const int x0, const int y0, const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + MvField* tab_mvf = fc->tab.mvf; + RefPicList* rpl = lc->sc->rpl; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int log2_min_pu_size = MIN_PU_LOG2; + uint8_t max_len_p, max_len_q; + int bs, i, j; + + // bs for TU internal horizontal PU boundaries + for (j = 8 - ((y0 - cb_y) % 8); j < height; j += 8) { + int yp_pu = (y0 + j - 1) >> log2_min_pu_size; + int yq_pu = (y0 + j) >> log2_min_pu_size; + + for (i = 0; i < width; i += 4) { + int x_pu = (x0 + i) >> log2_min_pu_size; + MvField *top = &tab_mvf[yp_pu * min_pu_width + x_pu]; + MvField *curr = &tab_mvf[yq_pu * min_pu_width + x_pu]; + const int x = x0 + i; + const int y = y0 + j; + + bs = boundary_strength(lc, curr, top, rpl); + TAB_BS(fc->tab.horizontal_bs[LUMA], x, y) = bs; + + //fixme: + //edgeTbFlags[ x − sbW ][ y ] is equal to 1 + //edgeTbFlags[ x + sbW ][ y ] is equal to 1 + max_len_p = max_len_q = 0; + if (j == 4 || j == height - 4) + max_len_p = max_len_q = 1; + else if (j == 8 || j == height - 8) + max_len_p = max_len_q = 2; + else + max_len_p = max_len_q = 3; + TAB_MAX_LEN(fc->tab.horizontal_p, x, y) = max_len_p; + TAB_MAX_LEN(fc->tab.horizontal_q, x, y) = max_len_q; + } + } + +} + +static void vvc_deblock_bs_luma_vertical(const VVCLocalContext *lc, + const int x0, const int y0, const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + MvField *tab_mvf = fc->tab.mvf; + const int log2_min_pu_size = MIN_PU_LOG2; + const int log2_min_tu_size = MIN_TU_LOG2; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int min_tu_width = fc->ps.pps->min_tu_width; + const int min_cb_log2 = fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + int is_intra = tab_mvf[(y0 >> log2_min_pu_size) * min_pu_width + + (x0 >> log2_min_pu_size)].pred_flag == PF_INTRA; + int boundary_left; + int i, bs, has_vertical_sb = 0; + uint8_t max_len_p, max_len_q; + + const int off_q = (y0 >> min_cb_log2) * min_cb_width + (x0 >> min_cb_log2); + const int cb_x = fc->tab.cb_pos_x[LUMA][off_q]; + const int cb_y = fc->tab.cb_pos_y[LUMA][off_q]; + const int cb_width = fc->tab.cb_width[LUMA][off_q]; + + if (!is_intra) { + if (fc->tab.msf[off_q] || fc->tab.iaf[off_q]) + has_vertical_sb = cb_width > 8; + } + + // bs for vertical TU boundaries + boundary_left = x0 > 0 && !(x0 & 3); + if (boundary_left && + ((!fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag && + lc->boundary_flags & BOUNDARY_LEFT_SLICE && + (x0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0) || + (!fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag && + lc->boundary_flags & BOUNDARY_LEFT_TILE && + (x0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0))) + boundary_left = 0; + + if (boundary_left) { + const RefPicList *rpl_left = + (lc->boundary_flags & BOUNDARY_LEFT_SLICE) ? ff_vvc_get_ref_list(fc, fc->ref, x0 - 1, y0) : lc->sc->rpl; + int xp_pu = (x0 - 1) >> log2_min_pu_size; + int xq_pu = x0 >> log2_min_pu_size; + int xp_tu = (x0 - 1) >> log2_min_tu_size; + int xq_tu = x0 >> log2_min_tu_size; + + for (i = 0; i < height; i += 4) { + const int off_x = cb_x - x0; + int y_pu = (y0 + i) >> log2_min_pu_size; + int y_tu = (y0 + i) >> log2_min_tu_size; + MvField *left = &tab_mvf[y_pu * min_pu_width + xp_pu]; + MvField *curr = &tab_mvf[y_pu * min_pu_width + xq_pu]; + uint8_t left_cbf_luma = fc->tab.tu_coded_flag[LUMA][y_tu * min_tu_width + xp_tu]; + uint8_t curr_cbf_luma = fc->tab.tu_coded_flag[LUMA][y_tu * min_tu_width + xq_tu]; + uint8_t pcmf = fc->tab.pcmf[LUMA][y_tu * min_tu_width + xp_tu] && + fc->tab.pcmf[LUMA][y_tu * min_tu_width + xq_tu]; + + if (pcmf) + bs = 0; + else if (curr->pred_flag == PF_INTRA || left->pred_flag == PF_INTRA || curr->ciip_flag || left->ciip_flag) + bs = 2; + else if (curr_cbf_luma || left_cbf_luma) + bs = 1; + else if (off_x && ((off_x % 8) || !has_vertical_sb)) + bs = 0; ////inside a cu, not aligned to 8 or with no subblocks + else + bs = boundary_strength(lc, curr, left, rpl_left); + + TAB_BS(fc->tab.vertical_bs[LUMA], x0, (y0 + i)) = bs; + + derive_max_filter_length_luma(fc, x0, y0 + i, is_intra, has_vertical_sb, 1, &max_len_p, &max_len_q); + TAB_MAX_LEN(fc->tab.vertical_p, x0, y0 + i) = max_len_p; + TAB_MAX_LEN(fc->tab.vertical_q, x0, y0 + i) = max_len_q; + } + } + + if (!is_intra) { + if (fc->tab.msf[off_q] || fc->tab.iaf[off_q]) + vvc_deblock_subblock_bs_vertical(lc, cb_x, cb_y, x0, y0, width, height); + } + +} +static void vvc_deblock_bs_luma_horizontal(const VVCLocalContext *lc, + const int x0, const int y0, const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + MvField *tab_mvf = fc->tab.mvf; + const int log2_min_pu_size = MIN_PU_LOG2; + const int log2_min_tu_size = MIN_TU_LOG2; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int min_tu_width = fc->ps.pps->min_tu_width; + const int min_cb_log2 = fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + int is_intra = tab_mvf[(y0 >> log2_min_pu_size) * min_pu_width + + (x0 >> log2_min_pu_size)].pred_flag == PF_INTRA; + int boundary_upper; + int i, bs, has_horizontal_sb = 0; + uint8_t max_len_p, max_len_q; + + const int off_q = (y0 >> min_cb_log2) * min_cb_width + (x0 >> min_cb_log2); + const int cb_x = fc->tab.cb_pos_x[LUMA][off_q]; + const int cb_y = fc->tab.cb_pos_y[LUMA][off_q]; + const int cb_height = fc->tab.cb_height[LUMA][off_q]; + + if (!is_intra) { + if (fc->tab.msf[off_q] || fc->tab.iaf[off_q]) + has_horizontal_sb = cb_height > 8; + } + + boundary_upper = y0 > 0 && !(y0 & 3); + if (boundary_upper && + ((!fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag && + lc->boundary_flags & BOUNDARY_UPPER_SLICE && + (y0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0) || + (!fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag && + lc->boundary_flags & BOUNDARY_UPPER_TILE && + (y0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0))) + boundary_upper = 0; + + if (boundary_upper) { + const RefPicList *rpl_top = + (lc->boundary_flags & BOUNDARY_UPPER_SLICE) ? ff_vvc_get_ref_list(fc, fc->ref, x0, y0 - 1) : lc->sc->rpl; + int yp_pu = (y0 - 1) >> log2_min_pu_size; + int yq_pu = y0 >> log2_min_pu_size; + int yp_tu = (y0 - 1) >> log2_min_tu_size; + int yq_tu = y0 >> log2_min_tu_size; + + for (i = 0; i < width; i += 4) { + const int off_y = y0 - cb_y; + int x_pu = (x0 + i) >> log2_min_pu_size; + int x_tu = (x0 + i) >> log2_min_tu_size; + MvField *top = &tab_mvf[yp_pu * min_pu_width + x_pu]; + MvField *curr = &tab_mvf[yq_pu * min_pu_width + x_pu]; + uint8_t top_cbf_luma = fc->tab.tu_coded_flag[LUMA][yp_tu * min_tu_width + x_tu]; + uint8_t curr_cbf_luma = fc->tab.tu_coded_flag[LUMA][yq_tu * min_tu_width + x_tu]; + const uint8_t pcmf = fc->tab.pcmf[LUMA][yp_tu * min_tu_width + x_tu] && + fc->tab.pcmf[LUMA][yq_tu * min_tu_width + x_tu]; + + if (pcmf) + bs = 0; + else if (curr->pred_flag == PF_INTRA || top->pred_flag == PF_INTRA || curr->ciip_flag || top->ciip_flag) + bs = 2; + else if (curr_cbf_luma || top_cbf_luma) + bs = 1; + else if (off_y && ((off_y % 8) || !has_horizontal_sb)) + bs = 0; //inside a cu, not aligned to 8 or with no subblocks + else + bs = boundary_strength(lc, curr, top, rpl_top); + + TAB_BS(fc->tab.horizontal_bs[LUMA], x0 + i, y0) = bs; + + derive_max_filter_length_luma(fc, x0 + i, y0, is_intra, has_horizontal_sb, 0, &max_len_p, &max_len_q); + TAB_MAX_LEN(fc->tab.horizontal_p, x0 + i, y0) = max_len_p; + TAB_MAX_LEN(fc->tab.horizontal_q, x0 + i, y0) = max_len_q; + } + } + + if (!is_intra) { + if (fc->tab.msf[off_q] || fc->tab.iaf[off_q]) + vvc_deblock_subblock_bs_horizontal(lc, cb_x, cb_y, x0, y0, width, height); + } +} + +static void vvc_deblock_bs_chroma_vertical(const VVCLocalContext *lc, + const int x0, const int y0, const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + MvField *tab_mvf = fc->tab.mvf; + const int log2_min_pu_size = MIN_PU_LOG2; + const int log2_min_tu_size = MIN_PU_LOG2; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int min_tu_width = fc->ps.pps->min_tu_width; + int boundary_left, i; + + // bs for vertical TU boundaries + boundary_left = x0 > 0 && !(x0 & ((CHROMA_GRID << fc->ps.sps->hshift[1]) - 1)); + if (boundary_left && + ((!fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag && + lc->boundary_flags & BOUNDARY_LEFT_SLICE && + (x0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0) || + (!fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag && + lc->boundary_flags & BOUNDARY_LEFT_TILE && + (x0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0))) + boundary_left = 0; + + if (boundary_left) { + int xp_pu = (x0 - 1) >> log2_min_pu_size; + int xq_pu = x0 >> log2_min_pu_size; + int xp_tu = (x0 - 1) >> log2_min_tu_size; + int xq_tu = x0 >> log2_min_tu_size; + + for (i = 0; i < height; i += 2) { + int y_pu = (y0 + i) >> log2_min_pu_size; + int y_tu = (y0 + i) >> log2_min_tu_size; + MvField *left = &tab_mvf[y_pu * min_pu_width + xp_pu]; + MvField *curr = &tab_mvf[y_pu * min_pu_width + xq_pu]; + const int left_tu = y_tu * min_tu_width + xp_tu; + const int curr_tu = y_tu * min_tu_width + xq_tu; + const uint8_t pcmf = fc->tab.pcmf[CHROMA][left_tu] && fc->tab.pcmf[CHROMA][curr_tu]; + + for (int c = CB; c <= CR; c++) { + uint8_t cbf = fc->tab.tu_coded_flag[c][left_tu] | + fc->tab.tu_coded_flag[c][curr_tu] | + fc->tab.tu_joint_cbcr_residual_flag[left_tu] | + fc->tab.tu_joint_cbcr_residual_flag[curr_tu]; + int bs = 0; + + if (pcmf) + bs = 0; + else if (curr->pred_flag == PF_INTRA || left->pred_flag == PF_INTRA || curr->ciip_flag || left->ciip_flag) + bs = 2; + else if (cbf) + bs = 1; + TAB_BS(fc->tab.vertical_bs[c], x0, (y0 + i)) = bs; + } + } + } +} + +static void vvc_deblock_bs_chroma_horizontal(const VVCLocalContext *lc, + const int x0, const int y0, const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + MvField *tab_mvf = fc->tab.mvf; + const int log2_min_pu_size = MIN_PU_LOG2; + const int log2_min_tu_size = MIN_PU_LOG2; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int min_tu_width = fc->ps.pps->min_tu_width; + int boundary_upper; + int i; + + boundary_upper = y0 > 0 && !(y0 & ((CHROMA_GRID << fc->ps.sps->vshift[1]) - 1)); + if (boundary_upper && + ((!fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag && + lc->boundary_flags & BOUNDARY_UPPER_SLICE && + (y0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0) || + (!fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag && + lc->boundary_flags & BOUNDARY_UPPER_TILE && + (y0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0))) + boundary_upper = 0; + + if (boundary_upper) { + int yp_pu = (y0 - 1) >> log2_min_pu_size; + int yq_pu = y0 >> log2_min_pu_size; + int yp_tu = (y0 - 1) >> log2_min_tu_size; + int yq_tu = y0 >> log2_min_tu_size; + + for (i = 0; i < width; i += 2) { + int x_pu = (x0 + i) >> log2_min_pu_size; + int x_tu = (x0 + i) >> log2_min_tu_size; + MvField *top = &tab_mvf[yp_pu * min_pu_width + x_pu]; + MvField *curr = &tab_mvf[yq_pu * min_pu_width + x_pu]; + const int top_tu = yp_tu * min_tu_width + x_tu; + const int curr_tu = yq_tu * min_tu_width + x_tu; + const uint8_t pcmf = fc->tab.pcmf[CHROMA][top_tu] && fc->tab.pcmf[CHROMA][curr_tu]; + + for (int c = CB; c <= CR; c++) { + uint8_t cbf = fc->tab.tu_coded_flag[c][top_tu] | + fc->tab.tu_coded_flag[c][curr_tu] | + fc->tab.tu_joint_cbcr_residual_flag[top_tu] | + fc->tab.tu_joint_cbcr_residual_flag[curr_tu]; + int bs = 0; + + if (pcmf) + bs = 0; + else if (curr->pred_flag == PF_INTRA || top->pred_flag == PF_INTRA || curr->ciip_flag || top->ciip_flag) + bs = 2; + else if (cbf) + bs = 1; + TAB_BS(fc->tab.horizontal_bs[c], x0 + i, y0) = bs; + } + } + } +} + +typedef void (*deblock_bs_fn)(const VVCLocalContext *lc, const int x0, const int y0, + const int width, const int height); + +static void vvc_deblock_bs(const VVCLocalContext *lc, const int x0, const int y0, const int vertical) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int ctb_size = sps->ctb_size_y; + const int x_end = FFMIN(x0 + ctb_size, pps->width) >> MIN_TU_LOG2; + const int y_end = FFMIN(y0 + ctb_size, pps->height) >> MIN_TU_LOG2; + deblock_bs_fn deblock_bs[2][2] = { + { vvc_deblock_bs_luma_horizontal, vvc_deblock_bs_chroma_horizontal }, + { vvc_deblock_bs_luma_vertical, vvc_deblock_bs_chroma_vertical } + }; + + for (int is_chroma = 0; is_chroma <= 1; is_chroma++) { + const int hs = sps->hshift[is_chroma]; + const int vs = sps->vshift[is_chroma]; + for (int y = y0 >> MIN_TU_LOG2; y < y_end; y++) { + for (int x = x0 >> MIN_TU_LOG2; x < x_end; x++) { + const int off = y * fc->ps.pps->min_tu_width + x; + if ((fc->tab.tb_pos_x0[is_chroma][off] >> MIN_TU_LOG2) == x && (fc->tab.tb_pos_y0[is_chroma][off] >> MIN_TU_LOG2) == y) { + deblock_bs[vertical][is_chroma](lc, x << MIN_TU_LOG2, y << MIN_TU_LOG2, + fc->tab.tb_width[is_chroma][off] << hs, fc->tab.tb_height[is_chroma][off] << vs); + } + } + } + } +} + +//part of 8.8.3.3 Derivation process of transform block boundary +static void max_filter_length_luma(const VVCFrameContext *fc, const int qx, const int qy, + const int vertical, uint8_t *max_len_p, uint8_t *max_len_q) +{ + const uint8_t *tab_len_p = vertical ? fc->tab.vertical_p : fc->tab.horizontal_p; + const uint8_t *tab_len_q = vertical ? fc->tab.vertical_q : fc->tab.horizontal_q; + *max_len_p = TAB_MAX_LEN(tab_len_p, qx, qy); + *max_len_q = TAB_MAX_LEN(tab_len_q, qx, qy); +} + +//part of 8.8.3.3 Derivation process of transform block boundary +static void max_filter_length_chroma(const VVCFrameContext *fc, const int qx, const int qy, + const int vertical, const int horizontal_ctu_edge, const int bs, uint8_t *max_len_p, uint8_t *max_len_q) +{ + const int px = vertical ? qx - 1 : qx; + const int py = !vertical ? qy - 1 : qy; + const uint8_t *tb_size = vertical ? fc->tab.tb_width[CHROMA] : fc->tab.tb_height[CHROMA]; + + const int size_p = tb_size[(py >> MIN_TU_LOG2) * fc->ps.pps->min_tu_width + (px >> MIN_TU_LOG2)]; + const int size_q = tb_size[(qy >> MIN_TU_LOG2) * fc->ps.pps->min_tu_width + (qx >> MIN_TU_LOG2)]; + if (size_p >= 8 && size_q >= 8) { + *max_len_p = *max_len_q = 3; + if (horizontal_ctu_edge) + *max_len_p = 1; + } else { + //part of 8.8.3.6.4 Decision process for chroma block edges + *max_len_p = *max_len_q = (bs == 2); + } +} + +static void max_filter_length(const VVCFrameContext *fc, const int qx, const int qy, + const int c_idx, const int vertical, const int horizontal_ctu_edge, const int bs, uint8_t *max_len_p, uint8_t *max_len_q) +{ + if (!c_idx) + max_filter_length_luma(fc, qx, qy, vertical, max_len_p, max_len_q); + else + max_filter_length_chroma(fc, qx, qy, vertical, horizontal_ctu_edge, bs, max_len_p, max_len_q); +} + +#define TC_CALC(qp, bs) \ + tctable[av_clip((qp) + DEFAULT_INTRA_TC_OFFSET * ((bs) - 1) + \ + (tc_offset & -2), \ + 0, MAX_QP + DEFAULT_INTRA_TC_OFFSET)] + +// part of 8.8.3.6.2 Decision process for luma block edges +static int get_qp_y(const VVCFrameContext *fc, const uint8_t *src, const int x, const int y, const int vertical) +{ + const VVCSPS *sps = fc->ps.sps; + const int qp = (ff_vvc_get_qPy(fc, x - vertical, y - !vertical) + ff_vvc_get_qPy(fc, x, y) + 1) >> 1; + int qp_offset = 0; + int level; + + if (!sps->r->sps_ladf_enabled_flag) + return qp; + + level = fc->vvcdsp.lf.ladf_level[vertical](src, fc->frame->linesize[LUMA]); + qp_offset = sps->r->sps_ladf_lowest_interval_qp_offset; + for (int i = 0; i < sps->num_ladf_intervals - 1 && level > sps->ladf_interval_lower_bound[i + 1]; i++) + qp_offset = sps->r->sps_ladf_qp_offset[i]; + + return qp + qp_offset; +} + +// part of 8.8.3.6.2 Decision process for luma block edges +static int get_qp_c(const VVCFrameContext *fc, const int x, const int y, const int c_idx, const int vertical) +{ + const VVCSPS *sps = fc->ps.sps; + return (get_qPc(fc, x - vertical, y - !vertical, c_idx) + get_qPc(fc, x, y, c_idx) - 2 * sps->qp_bd_offset + 1) >> 1; +} + +static int get_qp(const VVCFrameContext *fc, const uint8_t *src, const int x, const int y, const int c_idx, const int vertical) +{ + if (!c_idx) + return get_qp_y(fc, src, x, y, vertical); + return get_qp_c(fc, x, y, c_idx, vertical); +} + +void ff_vvc_deblock_vertical(const VVCLocalContext *lc, int x0, int y0) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int c_end = sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; + uint8_t *src; + int x, y, qp; + + //not use this yet, may needed by plt. + const uint8_t no_p[4] = { 0 }; + const uint8_t no_q[4] = { 0 } ; + + int ctb_log2_size_y = fc->ps.sps->ctb_log2_size_y; + int x_end, y_end; + int ctb_size = 1 << ctb_log2_size_y; + int ctb = (x0 >> ctb_log2_size_y) + + (y0 >> ctb_log2_size_y) * fc->ps.pps->ctb_width; + DBParams *params = fc->tab.deblock + ctb; + + vvc_deblock_bs(lc, x0, y0, 1); + + x_end = x0 + ctb_size; + if (x_end > fc->ps.sps->width) + x_end = fc->ps.sps->width; + y_end = y0 + ctb_size; + if (y_end > fc->ps.sps->height) + y_end = fc->ps.sps->height; + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int hs = sps->hshift[c_idx]; + const int vs = sps->vshift[c_idx]; + const int grid = c_idx ? (CHROMA_GRID << hs) : LUMA_GRID; + const int tc_offset = params->tc_offset[c_idx]; + const int beta_offset = params->beta_offset[c_idx]; + + for (y = y0; y < y_end; y += (DEBLOCK_STEP << vs)) { + for (x = x0 ? x0 : grid; x < x_end; x += grid) { + int32_t bs[4], beta[4], tc[4], all_zero_bs = 1; + uint8_t max_len_p[4], max_len_q[4]; + + for (int i = 0; i < DEBLOCK_STEP >> (2 - vs); i++) { + const int dy = i << 2; + bs[i] = (y + dy < y_end) ? TAB_BS(fc->tab.vertical_bs[c_idx], x, y + dy) : 0; + if (bs[i]) { + src = &fc->frame->data[c_idx][((y + dy) >> vs) * fc->frame->linesize[c_idx] + ((x >> hs) << fc->ps.sps->pixel_shift)]; + qp = get_qp(fc, src, x, y + dy, c_idx, 1); + + beta[i] = betatable[av_clip(qp + beta_offset, 0, MAX_QP)]; + + max_filter_length(fc, x, y + dy, c_idx, 1, 0, bs[i], &max_len_p[i], &max_len_q[i]); + all_zero_bs = 0; + } + tc[i] = bs[i] ? TC_CALC(qp, bs[i]) : 0; + } + + if (!all_zero_bs) { + src = &fc->frame->data[c_idx][(y >> vs) * fc->frame->linesize[c_idx] + ((x >> hs) << fc->ps.sps->pixel_shift)]; + if (!c_idx) { + fc->vvcdsp.lf.filter_luma[1](src, fc->frame->linesize[c_idx], + beta, tc, no_p, no_q, max_len_p, max_len_q, 0); + } else { + fc->vvcdsp.lf.filter_chroma[1](src, fc->frame->linesize[c_idx], + beta, tc, no_p, no_q, max_len_p, max_len_q, vs); + } + } + } + } + } +} + +void ff_vvc_deblock_horizontal(const VVCLocalContext *lc, int x0, int y0) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int c_end = fc->ps.sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; + uint8_t* src; + int x, y, qp; + + //not use this yet, may needed by plt. + const uint8_t no_p[4] = { 0 }; + const uint8_t no_q[4] = { 0 } ; + + int ctb_log2_size_y = fc->ps.sps->ctb_log2_size_y; + int x_end, y_end; + int ctb_size = 1 << ctb_log2_size_y; + int ctb = (x0 >> ctb_log2_size_y) + + (y0 >> ctb_log2_size_y) * fc->ps.pps->ctb_width; + + vvc_deblock_bs(lc, x0, y0, 0); + + x_end = x0 + ctb_size; + if (x_end > fc->ps.sps->width) + x_end = fc->ps.sps->width; + y_end = y0 + ctb_size; + if (y_end > fc->ps.sps->height) + y_end = fc->ps.sps->height; + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int hs = sps->hshift[c_idx]; + const int vs = sps->vshift[c_idx]; + const int grid = c_idx ? (CHROMA_GRID << vs) : LUMA_GRID; + + for (y = y0; y < y_end; y += grid) { + const uint8_t horizontal_ctu_edge = !(y % fc->ps.sps->ctb_size_y); + if (!y) + continue; + + for (x = x0 ? x0: 0; x < x_end; x += (DEBLOCK_STEP << hs)) { + int32_t bs[4], beta[4], tc[4], all_zero_bs = 1; + uint8_t max_len_p[4], max_len_q[4]; + + for (int i = 0; i < DEBLOCK_STEP >> (2 - hs); i++) { + const int dx = i << 2; + const DBParams *params = fc->tab.deblock + ctb - (x + dx < x0); + const int beta_offset = params->beta_offset[c_idx]; + const int tc_offset = params->tc_offset[c_idx]; + + bs[i] = (x + dx < x_end) ? TAB_BS(fc->tab.horizontal_bs[c_idx], x + dx, y) : 0; + if (bs[i]) { + src = &fc->frame->data[c_idx][(y >> vs) * fc->frame->linesize[c_idx] + (((x + dx)>> hs) << fc->ps.sps->pixel_shift)]; + qp = get_qp(fc, src, x + dx, y, c_idx, 0); + + beta[i] = betatable[av_clip(qp + beta_offset, 0, MAX_QP)]; + + max_filter_length(fc, x + dx, y, c_idx, 0, horizontal_ctu_edge, bs[i], &max_len_p[i], &max_len_q[i]); + all_zero_bs = 0; + } + tc[i] = bs[i] ? TC_CALC(qp, bs[i]) : 0; + } + if (!all_zero_bs) { + src = &fc->frame->data[c_idx][(y >> vs) * fc->frame->linesize[c_idx] + ((x >> hs) << fc->ps.sps->pixel_shift)]; + if (!c_idx) { + fc->vvcdsp.lf.filter_luma[0](src, fc->frame->linesize[c_idx], + beta, tc, no_p, no_q, max_len_p, max_len_q, horizontal_ctu_edge); + } else { + fc->vvcdsp.lf.filter_chroma[0](src, fc->frame->linesize[c_idx], + beta, tc, no_p, no_q, max_len_p, max_len_q, hs); + } + } + } + } + } +} + +static void alf_copy_border(uint8_t *dst, const uint8_t *src, + const int pixel_shift, int width, const int height, const ptrdiff_t dst_stride, const ptrdiff_t src_stride) +{ + width <<= pixel_shift; + for (int i = 0; i < height; i++) { + memcpy(dst, src, width); + dst += dst_stride; + src += src_stride; + } +} + +static void alf_extend_vert(uint8_t *_dst, const uint8_t *_src, + const int pixel_shift, const int width, const int height, ptrdiff_t stride) +{ + if (pixel_shift == 0) { + for (int i = 0; i < height; i++) { + memset(_dst, *_src, width); + _src += stride; + _dst += stride; + } + } else { + const uint16_t *src = (const uint16_t *)_src; + uint16_t *dst = (uint16_t *)_dst; + stride >>= pixel_shift; + + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) + dst[j] = *src; + src += stride; + dst += stride; + } + } +} + +static void alf_extend_horz(uint8_t *dst, const uint8_t *src, + const int pixel_shift, int width, const int height, const ptrdiff_t stride) +{ + width <<= pixel_shift; + for (int i = 0; i < height; i++) { + memcpy(dst, src, width); + dst += stride; + } +} + +static void alf_copy_ctb_to_hv(VVCFrameContext *fc, const uint8_t *src, const ptrdiff_t src_stride, + const int x, const int y, const int width, const int height, const int x_ctb, const int y_ctb, const int c_idx) +{ + const int ps = fc->ps.sps->pixel_shift; + const int w = fc->ps.sps->width >> fc->ps.sps->hshift[c_idx]; + const int h = fc->ps.sps->height >> fc->ps.sps->vshift[c_idx]; + const int border_pixels = (c_idx == 0) ? ALF_BORDER_LUMA : ALF_BORDER_CHROMA; + const int offset_h[] = { 0, height - border_pixels }; + const int offset_v[] = { 0, width - border_pixels }; + + /* copy horizontal edges */ + for (int i = 0; i < FF_ARRAY_ELEMS(offset_h); i++) { + alf_copy_border(fc->tab.alf_pixel_buffer_h[c_idx][i] + ((border_pixels * y_ctb * w + x)<< ps), + src + offset_h[i] * src_stride, ps, width, border_pixels, w << ps, src_stride); + } + /* copy vertical edges */ + for (int i = 0; i < FF_ARRAY_ELEMS(offset_v); i++) { + alf_copy_border(fc->tab.alf_pixel_buffer_v[c_idx][i] + ((h * x_ctb + y) * (border_pixels << ps)), + src + (offset_v[i] << ps), ps, border_pixels, height, border_pixels << ps, src_stride); + } +} + +static void alf_fill_border_h(uint8_t *dst, const ptrdiff_t dst_stride, const uint8_t *src, const ptrdiff_t src_stride, + const uint8_t *border, const int width, const int border_pixels, const int ps, const int edge) +{ + if (edge) + alf_extend_horz(dst, border, ps, width, border_pixels, dst_stride); + else + alf_copy_border(dst, src, ps, width, border_pixels, dst_stride, src_stride); +} + +static void alf_fill_border_v(uint8_t *dst, const ptrdiff_t dst_stride, const uint8_t *src, + const uint8_t *border, const int border_pixels, const int height, const int pixel_shift, const int *edges, const int edge) +{ + const ptrdiff_t src_stride = (border_pixels << pixel_shift); + + if (edge) { + alf_extend_vert(dst, border, pixel_shift, border_pixels, height + 2 * border_pixels, dst_stride); + return; + } + + //left/right + alf_copy_border(dst + dst_stride * border_pixels * edges[TOP], src + src_stride * border_pixels * edges[TOP], + pixel_shift, border_pixels, height + (!edges[TOP] + !edges[BOTTOM]) * border_pixels, dst_stride, src_stride); + + //top left/right + if (edges[TOP]) + alf_extend_horz(dst, dst + dst_stride * border_pixels, pixel_shift, border_pixels, border_pixels, dst_stride); + + //bottom left/right + if (edges[BOTTOM]) { + dst += dst_stride * (border_pixels + height); + alf_extend_horz(dst, dst - dst_stride, pixel_shift, border_pixels, border_pixels, dst_stride); + } +} + +static void alf_prepare_buffer(VVCFrameContext *fc, uint8_t *_dst, const uint8_t *_src, const int x, const int y, + const int x_ctb, const int y_ctb, const int width, const int height, const ptrdiff_t dst_stride, const ptrdiff_t src_stride, + const int c_idx, const int *edges) +{ + const int ps = fc->ps.sps->pixel_shift; + const int w = fc->ps.sps->width >> fc->ps.sps->hshift[c_idx]; + const int h = fc->ps.sps->height >> fc->ps.sps->vshift[c_idx]; + const int border_pixels = c_idx == 0 ? ALF_BORDER_LUMA : ALF_BORDER_CHROMA; + uint8_t *dst, *src; + + copy_ctb(_dst, _src, width << ps, height, dst_stride, src_stride); + + //top + src = fc->tab.alf_pixel_buffer_h[c_idx][1] + (((border_pixels * (y_ctb - 1)) * w + x) << ps); + dst = _dst - border_pixels * dst_stride; + alf_fill_border_h(dst, dst_stride, src, w << ps, _dst, width, border_pixels, ps, edges[TOP]); + + //bottom + src = fc->tab.alf_pixel_buffer_h[c_idx][0] + ((border_pixels * (y_ctb + 1) * w + x) << ps); + dst = _dst + height * dst_stride; + alf_fill_border_h(dst, dst_stride, src, w << ps, _dst + (height - 1) * dst_stride, width, border_pixels, ps, edges[BOTTOM]); + + + //left + src = fc->tab.alf_pixel_buffer_v[c_idx][1] + (h * (x_ctb - 1) + y - border_pixels) * (border_pixels << ps); + dst = _dst - (border_pixels << ps) - border_pixels * dst_stride; + alf_fill_border_v(dst, dst_stride, src, dst + (border_pixels << ps), border_pixels, height, ps, edges, edges[LEFT]); + + //right + src = fc->tab.alf_pixel_buffer_v[c_idx][0] + (h * (x_ctb + 1) + y - border_pixels) * (border_pixels << ps); + dst = _dst + (width << ps) - border_pixels * dst_stride; + alf_fill_border_v(dst, dst_stride, src, dst - (1 << ps), border_pixels, height, ps, edges, edges[RIGHT]); +} + +#define ALF_MAX_BLOCKS_IN_CTU (MAX_CTU_SIZE * MAX_CTU_SIZE / ALF_BLOCK_SIZE / ALF_BLOCK_SIZE) +#define ALF_MAX_FILTER_SIZE (ALF_MAX_BLOCKS_IN_CTU * ALF_NUM_COEFF_LUMA) + +static void alf_get_coeff_and_clip(VVCLocalContext *lc, int16_t *coeff, int16_t *clip, + const uint8_t *src, ptrdiff_t src_stride, int width, int height, int vb_pos, ALFParams *alf) +{ + const VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + uint8_t fixed_clip_set[ALF_NUM_FILTERS_LUMA][ALF_NUM_COEFF_LUMA] = { 0 }; + const int16_t *coeff_set; + const uint8_t *clip_idx_set; + const uint8_t *class_to_filt; + const int size = width * height / ALF_BLOCK_SIZE / ALF_BLOCK_SIZE; + int class_idx[ALF_MAX_BLOCKS_IN_CTU]; + int transpose_idx[ALF_MAX_BLOCKS_IN_CTU]; + + if (alf->ctb_filt_set_idx_y < 16) { + coeff_set = &ff_vvc_alf_fix_filt_coeff[0][0]; + clip_idx_set = &fixed_clip_set[0][0]; + class_to_filt = ff_vvc_alf_class_to_filt_map[alf->ctb_filt_set_idx_y]; + } else { + const int id = rsh->sh_alf_aps_id_luma[alf->ctb_filt_set_idx_y - 16]; + const VVCALF *aps = fc->ps.alf_list[id]; + coeff_set = &aps->luma_coeff[0][0]; + clip_idx_set = &aps->luma_clip_idx[0][0]; + class_to_filt = ff_vvc_alf_aps_class_to_filt_map; + } + fc->vvcdsp.alf.classify(class_idx, transpose_idx, src, src_stride, width, height, + vb_pos, lc->alf_gradient_tmp); + fc->vvcdsp.alf.recon_coeff_and_clip(coeff, clip, class_idx, transpose_idx, size, + coeff_set, clip_idx_set, class_to_filt); +} + +static void alf_filter_luma(VVCLocalContext *lc, uint8_t *dst, const uint8_t *src, + const ptrdiff_t dst_stride, const ptrdiff_t src_stride, const int x0, const int y0, + const int width, const int height, const int _vb_pos, ALFParams *alf) +{ + const VVCFrameContext *fc = lc->fc; + int vb_pos = _vb_pos - y0; + int16_t *coeff = (int16_t*)lc->tmp; + int16_t *clip = (int16_t *)lc->tmp1; + + av_assert0(ALF_MAX_FILTER_SIZE <= sizeof(lc->tmp)); + av_assert0(ALF_MAX_FILTER_SIZE * sizeof(int16_t) <= sizeof(lc->tmp1)); + + alf_get_coeff_and_clip(lc, coeff, clip, src, src_stride, width, height, vb_pos, alf); + fc->vvcdsp.alf.filter[LUMA](dst, dst_stride, src, src_stride, width, height, coeff, clip, vb_pos); +} + +static int alf_clip_from_idx(const VVCFrameContext *fc, const int idx) +{ + const VVCSPS *sps = fc->ps.sps; + const int offset[] = {0, 3, 5, 7}; + + return 1 << (sps->bit_depth - offset[idx]); +} + +static void alf_filter_chroma(VVCLocalContext *lc, uint8_t *dst, const uint8_t *src, + const ptrdiff_t dst_stride, const ptrdiff_t src_stride, const int c_idx, + const int width, const int height, const int vb_pos, ALFParams *alf) +{ + VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const VVCALF *aps = fc->ps.alf_list[rsh->sh_alf_aps_id_chroma]; + const int idx = alf->alf_ctb_filter_alt_idx[c_idx - 1]; + const int16_t *coeff = aps->chroma_coeff[idx]; + int16_t clip[ALF_NUM_COEFF_CHROMA]; + + for (int i = 0; i < ALF_NUM_COEFF_CHROMA; i++) + clip[i] = alf_clip_from_idx(fc, aps->chroma_clip_idx[idx][i]); + + fc->vvcdsp.alf.filter[CHROMA](dst, dst_stride, src, src_stride, width, height, coeff, clip, vb_pos); +} + +static void alf_filter_cc(VVCLocalContext *lc, uint8_t *dst, const uint8_t *luma, + const ptrdiff_t dst_stride, const ptrdiff_t luma_stride, const int c_idx, + const int width, const int height, const int hs, const int vs, const int vb_pos, ALFParams *alf) +{ + VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int idx = c_idx - 1; + const int cc_aps_id = c_idx == CB ? rsh->sh_alf_cc_cb_aps_id : rsh->sh_alf_cc_cr_aps_id; + const VVCALF *aps = fc->ps.alf_list[cc_aps_id]; + + if (aps) { + const int16_t *coeff = aps->cc_coeff[idx][alf->ctb_cc_idc[idx] - 1]; + + fc->vvcdsp.alf.filter_cc(dst, dst_stride, luma, luma_stride, width, height, hs, vs, coeff, vb_pos); + } +} + +void ff_vvc_alf_copy_ctu_to_hv(VVCLocalContext* lc, const int x0, const int y0) +{ + VVCFrameContext *fc = lc->fc; + const int x_ctb = x0 >> fc->ps.sps->ctb_log2_size_y; + const int y_ctb = y0 >> fc->ps.sps->ctb_log2_size_y; + const int ctb_size_y = fc->ps.sps->ctb_size_y; + const int ps = fc->ps.sps->pixel_shift; + const int c_end = fc->ps.sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int hs = fc->ps.sps->hshift[c_idx]; + const int vs = fc->ps.sps->vshift[c_idx]; + const int x = x0 >> hs; + const int y = y0 >> vs; + const int width = FFMIN(fc->ps.sps->width - x0, ctb_size_y) >> hs; + const int height = FFMIN(fc->ps.sps->height - y0, ctb_size_y) >> vs; + + const int src_stride = fc->frame->linesize[c_idx]; + uint8_t* src = &fc->frame->data[c_idx][y * src_stride + (x << ps)]; + + alf_copy_ctb_to_hv(fc, src, src_stride, x, y, width, height, x_ctb, y_ctb, c_idx); + } +} + +void ff_vvc_alf_filter(VVCLocalContext *lc, const int x0, const int y0) +{ + VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const int x_ctb = x0 >> fc->ps.sps->ctb_log2_size_y; + const int y_ctb = y0 >> fc->ps.sps->ctb_log2_size_y; + const int ctb_size_y = fc->ps.sps->ctb_size_y; + const int ps = fc->ps.sps->pixel_shift; + const int padded_stride = EDGE_EMU_BUFFER_STRIDE << ps; + const int padded_offset = padded_stride * ALF_PADDING_SIZE + (ALF_PADDING_SIZE << ps); + const int c_end = fc->ps.sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; + ALFParams *alf = &CTB(fc->tab.alf, x_ctb, y_ctb); + int edges[MAX_EDGES] = { x_ctb == 0, y_ctb == 0, x_ctb == pps->ctb_width - 1, y_ctb == pps->ctb_height - 1 }; + + if (!pps->r->pps_loop_filter_across_tiles_enabled_flag) { + edges[LEFT] = edges[LEFT] || (lc->boundary_flags & BOUNDARY_LEFT_TILE); + edges[TOP] = edges[TOP] || (lc->boundary_flags & BOUNDARY_UPPER_TILE); + edges[RIGHT] = edges[RIGHT] || pps->ctb_to_col_bd[x_ctb] != pps->ctb_to_col_bd[x_ctb + 1]; + edges[BOTTOM] = edges[BOTTOM] || pps->ctb_to_row_bd[y_ctb] != pps->ctb_to_row_bd[y_ctb + 1]; + } + + if (!pps->r->pps_loop_filter_across_slices_enabled_flag) { + edges[LEFT] = edges[LEFT] || (lc->boundary_flags & BOUNDARY_LEFT_SLICE); + edges[TOP] = edges[TOP] || (lc->boundary_flags & BOUNDARY_UPPER_SLICE); + edges[RIGHT] = edges[RIGHT] || CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb + 1, y_ctb); + edges[BOTTOM] = edges[BOTTOM] || CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb, y_ctb + 1); + } + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int hs = fc->ps.sps->hshift[c_idx]; + const int vs = fc->ps.sps->vshift[c_idx]; + const int ctb_size_h = ctb_size_y >> hs; + const int ctb_size_v = ctb_size_y >> vs; + const int x = x0 >> hs; + const int y = y0 >> vs; + const int pic_width = fc->ps.sps->width >> hs; + const int pic_height = fc->ps.sps->height >> vs; + const int width = FFMIN(pic_width - x, ctb_size_h); + const int height = FFMIN(pic_height - y, ctb_size_v); + const int src_stride = fc->frame->linesize[c_idx]; + uint8_t *src = &fc->frame->data[c_idx][y * src_stride + (x << ps)]; + uint8_t *padded; + + if (alf->ctb_flag[c_idx] || (!c_idx && (alf->ctb_cc_idc[0] || alf->ctb_cc_idc[1]))) { + padded = (c_idx ? lc->alf_buffer_chroma : lc->alf_buffer_luma) + padded_offset; + alf_prepare_buffer(fc, padded, src, x, y, x_ctb, y_ctb, width, height, + padded_stride, src_stride, c_idx, edges); + } + if (alf->ctb_flag[c_idx]) { + if (!c_idx) { + alf_filter_luma(lc, src, padded, src_stride, padded_stride, x, y, + width, height, y + ctb_size_v - ALF_VB_POS_ABOVE_LUMA, alf); + } else { + alf_filter_chroma(lc, src, padded, src_stride, padded_stride, c_idx, + width, height, ctb_size_v - ALF_VB_POS_ABOVE_CHROMA, alf); + } + } + if (c_idx && alf->ctb_cc_idc[c_idx - 1]) { + padded = lc->alf_buffer_luma + padded_offset; + alf_filter_cc(lc, src, padded, src_stride, padded_stride, c_idx, + width, height, hs, vs, (ctb_size_v << vs) - ALF_VB_POS_ABOVE_LUMA, alf); + } + + alf->applied[c_idx] = 1; + } +} + + +void ff_vvc_lmcs_filter(const VVCLocalContext *lc, const int x, const int y) +{ + const SliceContext *sc = lc->sc; + const VVCFrameContext *fc = lc->fc; + const int ctb_size = fc->ps.sps->ctb_size_y; + const int width = FFMIN(fc->ps.pps->width - x, ctb_size); + const int height = FFMIN(fc->ps.pps->height - y, ctb_size); + uint8_t *data = fc->frame->data[LUMA] + y * fc->frame->linesize[LUMA] + (x << fc->ps.sps->pixel_shift); + if (sc->sh.r->sh_lmcs_used_flag) + fc->vvcdsp.lmcs.filter(data, fc->frame->linesize[LUMA], width, height, fc->ps.lmcs.inv_lut); +} diff --git a/libavcodec/vvc/vvc_filter.h b/libavcodec/vvc/vvc_filter.h new file mode 100644 index 0000000000..bc42e55523 --- /dev/null +++ b/libavcodec/vvc/vvc_filter.h @@ -0,0 +1,71 @@ +/* + * VVC filters + * + * Copyright (C) 2022 Nuo Mi + * + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef AVCODEC_VVC_FILTER_H +#define AVCODEC_VVC_FILTER_H + +#include "vvcdec.h" + +/** + * lmcs filter for the CTU + * @param lc local context for CTU + * @param x0 x position for the CTU + * @param y0 y position for the CTU + */ +void ff_vvc_lmcs_filter(const VVCLocalContext *lc, const int x0, const int y0); + +/** + * vertical deblock filter for the CTU + * @param lc local context for CTU + * @param x0 x position for the CTU + * @param y0 y position for the CTU + */ +void ff_vvc_deblock_vertical(const VVCLocalContext *lc, int x0, int y0); + +/** + * horizontal deblock filter for the CTU + * @param lc local context for CTU + * @param x0 x position for the CTU + * @param y0 y position for the CTU + */ +void ff_vvc_deblock_horizontal(const VVCLocalContext *lc, int x0, int y0); + +/** + * sao filter for the CTU + * @param lc local context for CTU + * @param x0 x position for the CTU + * @param y0 y position for the CTU + */ +void ff_vvc_sao_filter(VVCLocalContext *lc, const int x0, const int y0); + +void ff_vvc_sao_copy_ctb_to_hv(VVCLocalContext* lc, int rx, int ry, int last_row); +void ff_vvc_alf_copy_ctu_to_hv(VVCLocalContext* lc, int x0, int y0); + +/** + * alf filter for the CTU + * @param lc local context for CTU + * @param x0 x position for the CTU + * @param y0 y position for the CTU + */ +void ff_vvc_alf_filter(VVCLocalContext *lc, const int x0, const int y0); + +#endif // AVCODEC_VVC_CTU_H diff --git a/libavcodec/vvc/vvc_filter_template.c b/libavcodec/vvc/vvc_filter_template.c new file mode 100644 index 0000000000..a4f1792ec4 --- /dev/null +++ b/libavcodec/vvc/vvc_filter_template.c @@ -0,0 +1,1135 @@ +/* + * VVC filters DSP + * + * Copyright (C) 2022 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +static void FUNC(lmcs_filter_luma)(uint8_t *_dst, ptrdiff_t dst_stride, const int width, const int height, const uint8_t *_lut) +{ + const pixel *lut = (const pixel *)_lut; + pixel *dst = (pixel*)_dst; + dst_stride /= sizeof(pixel); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = lut[dst[x]]; + dst += dst_stride; + } +} + +static void FUNC(sao_band_filter)(uint8_t *_dst, const uint8_t *_src, ptrdiff_t dst_stride, ptrdiff_t src_stride, + const int16_t *sao_offset_val, const int sao_left_class, const int width, const int height) +{ + pixel *dst = (pixel *)_dst; + const pixel *src = (pixel *)_src; + int offset_table[32] = { 0 }; + int k, y, x; + int shift = BIT_DEPTH - 5; + + dst_stride /= sizeof(pixel); + src_stride /= sizeof(pixel); + + for (k = 0; k < 4; k++) + offset_table[(k + sao_left_class) & 31] = sao_offset_val[k + 1]; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) + dst[x] = av_clip_pixel(src[x] + offset_table[src[x] >> shift]); + dst += dst_stride; + src += src_stride; + } +} + +#define CMP(a, b) (((a) > (b)) - ((a) < (b))) + +static void FUNC(sao_edge_filter)(uint8_t *_dst, const uint8_t *_src, ptrdiff_t dst_stride, + const int16_t *sao_offset_val, const int eo, const int width, const int height) +{ + static const uint8_t edge_idx[] = { 1, 2, 0, 3, 4 }; + static const int8_t pos[4][2][2] = { + { { -1, 0 }, { 1, 0 } }, // horizontal + { { 0, -1 }, { 0, 1 } }, // vertical + { { -1, -1 }, { 1, 1 } }, // 45 degree + { { 1, -1 }, { -1, 1 } }, // 135 degree + }; + pixel *dst = (pixel *)_dst; + const pixel *src = (pixel *)_src; + int a_stride, b_stride; + int x, y; + ptrdiff_t src_stride = (2 * MAX_PB_SIZE + AV_INPUT_BUFFER_PADDING_SIZE) / sizeof(pixel); + dst_stride /= sizeof(pixel); + + a_stride = pos[eo][0][0] + pos[eo][0][1] * src_stride; + b_stride = pos[eo][1][0] + pos[eo][1][1] * src_stride; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + int diff0 = CMP(src[x], src[x + a_stride]); + int diff1 = CMP(src[x], src[x + b_stride]); + int offset_val = edge_idx[2 + diff0 + diff1]; + dst[x] = av_clip_pixel(src[x] + sao_offset_val[offset_val]); + } + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(sao_edge_restore_0)(uint8_t *_dst, const uint8_t *_src, + ptrdiff_t dst_stride, ptrdiff_t src_stride, const SAOParams *sao, + const int *borders, const int _width, const int _height, const int c_idx, + const uint8_t *vert_edge, const uint8_t *horiz_edge, const uint8_t *diag_edge) +{ + int x, y; + pixel *dst = (pixel *)_dst; + const pixel *src = (pixel *)_src; + const int16_t *sao_offset_val = sao->offset_val[c_idx]; + const int sao_eo_class = sao->eo_class[c_idx]; + int init_x = 0, width = _width, height = _height; + + dst_stride /= sizeof(pixel); + src_stride /= sizeof(pixel); + + if (sao_eo_class != SAO_EO_VERT) { + if (borders[0]) { + int offset_val = sao_offset_val[0]; + for (y = 0; y < height; y++) { + dst[y * dst_stride] = av_clip_pixel(src[y * src_stride] + offset_val); + } + init_x = 1; + } + if (borders[2]) { + int offset_val = sao_offset_val[0]; + int offset = width - 1; + for (x = 0; x < height; x++) { + dst[x * dst_stride + offset] = av_clip_pixel(src[x * src_stride + offset] + offset_val); + } + width--; + } + } + if (sao_eo_class != SAO_EO_HORIZ) { + if (borders[1]) { + int offset_val = sao_offset_val[0]; + for (x = init_x; x < width; x++) + dst[x] = av_clip_pixel(src[x] + offset_val); + } + if (borders[3]) { + int offset_val = sao_offset_val[0]; + ptrdiff_t y_dst_stride = dst_stride * (height - 1); + ptrdiff_t y_src_stride = src_stride * (height - 1); + for (x = init_x; x < width; x++) + dst[x + y_dst_stride] = av_clip_pixel(src[x + y_src_stride] + offset_val); + height--; + } + } +} + +static void FUNC(sao_edge_restore_1)(uint8_t *_dst, const uint8_t *_src, + ptrdiff_t dst_stride, ptrdiff_t src_stride, const SAOParams *sao, + const int *borders, const int _width, const int _height, const int c_idx, + const uint8_t *vert_edge, const uint8_t *horiz_edge, const uint8_t *diag_edge) +{ + int x, y; + pixel *dst = (pixel *)_dst; + const pixel *src = (pixel *)_src; + const int16_t *sao_offset_val = sao->offset_val[c_idx]; + const int sao_eo_class = sao->eo_class[c_idx]; + int init_x = 0, init_y = 0, width = _width, height = _height; + + dst_stride /= sizeof(pixel); + src_stride /= sizeof(pixel); + + if (sao_eo_class != SAO_EO_VERT) { + if (borders[0]) { + int offset_val = sao_offset_val[0]; + for (y = 0; y < height; y++) { + dst[y * dst_stride] = av_clip_pixel(src[y * src_stride] + offset_val); + } + init_x = 1; + } + if (borders[2]) { + int offset_val = sao_offset_val[0]; + int offset = width - 1; + for (x = 0; x < height; x++) { + dst[x * dst_stride + offset] = av_clip_pixel(src[x * src_stride + offset] + offset_val); + } + width--; + } + } + if (sao_eo_class != SAO_EO_HORIZ) { + if (borders[1]) { + int offset_val = sao_offset_val[0]; + for (x = init_x; x < width; x++) + dst[x] = av_clip_pixel(src[x] + offset_val); + init_y = 1; + } + if (borders[3]) { + int offset_val = sao_offset_val[0]; + ptrdiff_t y_dst_stride = dst_stride * (height - 1); + ptrdiff_t y_src_stride = src_stride * (height - 1); + for (x = init_x; x < width; x++) + dst[x + y_dst_stride] = av_clip_pixel(src[x + y_src_stride] + offset_val); + height--; + } + } + + { + int save_upper_left = !diag_edge[0] && sao_eo_class == SAO_EO_135D && !borders[0] && !borders[1]; + int save_upper_right = !diag_edge[1] && sao_eo_class == SAO_EO_45D && !borders[1] && !borders[2]; + int save_lower_right = !diag_edge[2] && sao_eo_class == SAO_EO_135D && !borders[2] && !borders[3]; + int save_lower_left = !diag_edge[3] && sao_eo_class == SAO_EO_45D && !borders[0] && !borders[3]; + + // Restore pixels that can't be modified + if (vert_edge[0] && sao_eo_class != SAO_EO_VERT) { + for (y = init_y + save_upper_left; y < height - save_lower_left; y++) + dst[y * dst_stride] = src[y * src_stride]; + } + if (vert_edge[1] && sao_eo_class != SAO_EO_VERT) { + for (y = init_y + save_upper_right; y < height - save_lower_right; y++) + dst[y * dst_stride + width - 1] = src[y * src_stride + width - 1]; + } + + if (horiz_edge[0] && sao_eo_class != SAO_EO_HORIZ) { + for (x = init_x + save_upper_left; x < width - save_upper_right; x++) + dst[x] = src[x]; + } + if (horiz_edge[1] && sao_eo_class != SAO_EO_HORIZ) { + for (x = init_x + save_lower_left; x < width - save_lower_right; x++) + dst[(height - 1) * dst_stride + x] = src[(height - 1) * src_stride + x]; + } + if (diag_edge[0] && sao_eo_class == SAO_EO_135D) + dst[0] = src[0]; + if (diag_edge[1] && sao_eo_class == SAO_EO_45D) + dst[width - 1] = src[width - 1]; + if (diag_edge[2] && sao_eo_class == SAO_EO_135D) + dst[dst_stride * (height - 1) + width - 1] = src[src_stride * (height - 1) + width - 1]; + if (diag_edge[3] && sao_eo_class == SAO_EO_45D) + dst[dst_stride * (height - 1)] = src[src_stride * (height - 1)]; + + } +} + +#undef CMP + +static av_always_inline int16_t FUNC(alf_clip)(pixel curr, pixel v0, pixel v1, int16_t clip) +{ + return av_clip(v0 - curr, -clip, clip) + av_clip(v1 - curr, -clip, clip); +} + +static void FUNC(alf_filter_luma)(uint8_t *_dst, ptrdiff_t dst_stride, const uint8_t *_src, ptrdiff_t src_stride, + const int width, const int height, const int16_t *filter, const int16_t *clip, const int vb_pos) +{ + const pixel *src = (pixel *)_src; + const int shift = 7; + const int offset = 1 << ( shift - 1 ); + const int vb_above = vb_pos - 4; + const int vb_below = vb_pos + 3; + + dst_stride /= sizeof(pixel); + src_stride /= sizeof(pixel); + + for (int y = 0; y < height; y += ALF_BLOCK_SIZE) { + for (int x = 0; x < width; x += ALF_BLOCK_SIZE) { + const pixel *s0 = src + y * src_stride + x; + const pixel *s1 = s0 + src_stride; + const pixel *s2 = s0 - src_stride; + const pixel *s3 = s1 + src_stride; + const pixel *s4 = s2 - src_stride; + const pixel *s5 = s3 + src_stride; + const pixel *s6 = s4 - src_stride; + + for (int i = 0; i < ALF_BLOCK_SIZE; i++) { + pixel *dst = (pixel *)_dst + (y + i) * dst_stride + x; + + const pixel *p0 = s0 + i * src_stride; + const pixel *p1 = s1 + i * src_stride; + const pixel *p2 = s2 + i * src_stride; + const pixel *p3 = s3 + i * src_stride; + const pixel *p4 = s4 + i * src_stride; + const pixel *p5 = s5 + i * src_stride; + const pixel *p6 = s6 + i * src_stride; + + const int is_near_vb_above = (y + i < vb_pos) && (y + i >= vb_pos - 1); + const int is_near_vb_below = (y + i >= vb_pos) && (y + i <= vb_pos); + const int is_near_vb = is_near_vb_above || is_near_vb_below; + + if ((y + i < vb_pos) && ((y + i) >= vb_above)) { + p1 = (y + i == vb_pos - 1) ? p0 : p1; + p3 = (y + i >= vb_pos - 2) ? p1 : p3; + p5 = (y + i >= vb_pos - 3) ? p3 : p5; + + p2 = (y + i == vb_pos - 1) ? p0 : p2; + p4 = (y + i >= vb_pos - 2) ? p2 : p4; + p6 = (y + i >= vb_pos - 3) ? p4 : p6; + } else if ((y + i >= vb_pos) && ((y + i) <= vb_below)) { + p2 = (y + i == vb_pos ) ? p0 : p2; + p4 = (y + i <= vb_pos + 1) ? p2 : p4; + p6 = (y + i <= vb_pos + 2) ? p4 : p6; + + p1 = (y + i == vb_pos ) ? p0 : p1; + p3 = (y + i <= vb_pos + 1) ? p1 : p3; + p5 = (y + i <= vb_pos + 2) ? p3 : p5; + } + + for (int j = 0; j < ALF_BLOCK_SIZE; j++) { + int sum = 0; + const pixel curr = *p0; + + sum += filter[0] * FUNC(alf_clip)(curr, p5[+0], p6[+0], clip[0]); + sum += filter[1] * FUNC(alf_clip)(curr, p3[+1], p4[-1], clip[1]); + sum += filter[2] * FUNC(alf_clip)(curr, p3[+0], p4[+0], clip[2]); + sum += filter[3] * FUNC(alf_clip)(curr, p3[-1], p4[+1], clip[3]); + sum += filter[4] * FUNC(alf_clip)(curr, p1[+2], p2[-2], clip[4]); + sum += filter[5] * FUNC(alf_clip)(curr, p1[+1], p2[-1], clip[5]); + sum += filter[6] * FUNC(alf_clip)(curr, p1[+0], p2[+0], clip[6]); + sum += filter[7] * FUNC(alf_clip)(curr, p1[-1], p2[+1], clip[7]); + sum += filter[8] * FUNC(alf_clip)(curr, p1[-2], p2[+2], clip[8]); + sum += filter[9] * FUNC(alf_clip)(curr, p0[+3], p0[-3], clip[9]); + sum += filter[10] * FUNC(alf_clip)(curr, p0[+2], p0[-2], clip[10]); + sum += filter[11] * FUNC(alf_clip)(curr, p0[+1], p0[-1], clip[11]); + + if (!is_near_vb) + sum = (sum + offset) >> shift; + else + sum = (sum + (1 << ((shift + 3) - 1))) >> (shift + 3); + sum += curr; + dst[j] = CLIP(sum); + + p0++; + p1++; + p2++; + p3++; + p4++; + p5++; + p6++; + } + } + filter += ALF_NUM_COEFF_LUMA; + clip += ALF_NUM_COEFF_LUMA; + } + } +} + +static void FUNC(alf_filter_chroma)(uint8_t* _dst, ptrdiff_t dst_stride, const uint8_t* _src, ptrdiff_t src_stride, + const int width, const int height, const int16_t* filter, const int16_t* clip, const int vb_pos) +{ + const pixel *src = (pixel *)_src; + const int shift = 7; + const int offset = 1 << ( shift - 1 ); + const int vb_above = vb_pos - 2; + const int vb_below = vb_pos + 1; + + dst_stride /= sizeof(pixel); + src_stride /= sizeof(pixel); + + for (int y = 0; y < height; y += ALF_BLOCK_SIZE) { + for (int x = 0; x < width; x += ALF_BLOCK_SIZE) { + const pixel *s0 = src + y * src_stride + x; + const pixel *s1 = s0 + src_stride; + const pixel *s2 = s0 - src_stride; + const pixel *s3 = s1 + src_stride; + const pixel *s4 = s2 - src_stride; + const pixel *s5 = s3 + src_stride; + const pixel *s6 = s4 - src_stride; + + for (int i = 0; i < ALF_BLOCK_SIZE; i++) { + pixel *dst = (pixel *)_dst + (y + i) * dst_stride + x; + + const pixel *p0 = s0 + i * src_stride; + const pixel *p1 = s1 + i * src_stride; + const pixel *p2 = s2 + i * src_stride; + const pixel *p3 = s3 + i * src_stride; + const pixel *p4 = s4 + i * src_stride; + const pixel *p5 = s5 + i * src_stride; + const pixel *p6 = s6 + i * src_stride; + + const int is_near_vb_above = (y + i < vb_pos) && (y + i >= vb_pos - 1); + const int is_near_vb_below = (y + i >= vb_pos) && (y + i <= vb_pos); + const int is_near_vb = is_near_vb_above || is_near_vb_below; + + if ((y + i < vb_pos) && ((y + i) >= vb_above)) { + p1 = (y + i == vb_pos - 1) ? p0 : p1; + p3 = (y + i >= vb_pos - 2) ? p1 : p3; + p5 = (y + i >= vb_pos - 3) ? p3 : p5; + + p2 = (y + i == vb_pos - 1) ? p0 : p2; + p4 = (y + i >= vb_pos - 2) ? p2 : p4; + p6 = (y + i >= vb_pos - 3) ? p4 : p6; + } else if ((y + i >= vb_pos) && ((y + i) <= vb_below)) { + p2 = (y + i == vb_pos ) ? p0 : p2; + p4 = (y + i <= vb_pos + 1) ? p2 : p4; + p6 = (y + i <= vb_pos + 2) ? p4 : p6; + + p1 = (y + i == vb_pos ) ? p0 : p1; + p3 = (y + i <= vb_pos + 1) ? p1 : p3; + p5 = (y + i <= vb_pos + 2) ? p3 : p5; + } + + for (int j = 0; j < ALF_BLOCK_SIZE; j++) { + int sum = 0; + const pixel curr = *p0; + + sum += filter[0] * FUNC(alf_clip)(curr, p3[+0], p4[+0], clip[0]); + sum += filter[1] * FUNC(alf_clip)(curr, p1[+1], p2[-1], clip[1]); + sum += filter[2] * FUNC(alf_clip)(curr, p1[+0], p2[+0], clip[2]); + sum += filter[3] * FUNC(alf_clip)(curr, p1[-1], p2[+1], clip[3]); + sum += filter[4] * FUNC(alf_clip)(curr, p0[+2], p0[-2], clip[4]); + sum += filter[5] * FUNC(alf_clip)(curr, p0[+1], p0[-1], clip[5]); + + if (!is_near_vb) + sum = (sum + offset) >> shift; + else + sum = (sum + (1 << ((shift + 3) - 1))) >> (shift + 3); + sum += curr; + dst[j] = CLIP(sum); + + p0++; + p1++; + p2++; + p3++; + p4++; + p5++; + p6++; + } + } + } + } +} + +static void FUNC(alf_filter_cc)(uint8_t *_dst, ptrdiff_t dst_stride, const uint8_t *_luma, const ptrdiff_t luma_stride, + const int width, const int height, const int hs, const int vs, const int16_t *filter, const int vb_pos) +{ + const ptrdiff_t stride = luma_stride / sizeof(pixel); + + dst_stride /= sizeof(pixel); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int sum = 0; + pixel *dst = (pixel *)_dst + y * dst_stride + x; + const pixel *src = (pixel *)_luma + (y << vs) * stride + (x << hs); + + const pixel *s0 = src - stride; + const pixel *s1 = src; + const pixel *s2 = src + stride; + const pixel *s3 = src + 2 * stride; + + const int pos = y << vs; + if (!vs && (pos == vb_pos || pos == vb_pos + 1)) + continue; + + if (pos == (vb_pos - 2) || pos == (vb_pos + 1)) + s3 = s2; + else if (pos == (vb_pos - 1) || pos == vb_pos) + s3 = s2 = s0 = s1; + + + sum += filter[0] * (*s0 - *src); + sum += filter[1] * (*(s1 - 1) - *src); + sum += filter[2] * (*(s1 + 1) - *src); + sum += filter[3] * (*(s2 - 1) - *src); + sum += filter[4] * (*s2 - *src); + sum += filter[5] * (*(s2 + 1) - *src); + sum += filter[6] * (*s3 - *src); + sum = av_clip((sum + 64) >> 7, -(1 << (BIT_DEPTH - 1)), (1 << (BIT_DEPTH - 1)) - 1); + sum += *dst; + *dst = av_clip_pixel(sum); + } + } +} + +#define ALF_DIR_VERT 0 +#define ALF_DIR_HORZ 1 +#define ALF_DIR_DIGA0 2 +#define ALF_DIR_DIGA1 3 + +static void FUNC(alf_get_idx)(int *class_idx, int *transpose_idx, const int *sum, const int ac) +{ + static const int arg_var[] = {0, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4 }; + + int hv0, hv1, dir_hv, d0, d1, dir_d, hvd1, hvd0, sum_hv, dir1; + + dir_hv = sum[ALF_DIR_VERT] <= sum[ALF_DIR_HORZ]; + hv1 = FFMAX(sum[ALF_DIR_VERT], sum[ALF_DIR_HORZ]); + hv0 = FFMIN(sum[ALF_DIR_VERT], sum[ALF_DIR_HORZ]); + + dir_d = sum[ALF_DIR_DIGA0] <= sum[ALF_DIR_DIGA1]; + d1 = FFMAX(sum[ALF_DIR_DIGA0], sum[ALF_DIR_DIGA1]); + d0 = FFMIN(sum[ALF_DIR_DIGA0], sum[ALF_DIR_DIGA1]); + + //promote to avoid overflow + dir1 = (uint64_t)d1 * hv0 <= (uint64_t)hv1 * d0; + hvd1 = dir1 ? hv1 : d1; + hvd0 = dir1 ? hv0 : d0; + + sum_hv = sum[ALF_DIR_HORZ] + sum[ALF_DIR_VERT]; + *class_idx = arg_var[av_clip_uintp2(sum_hv * ac >> (BIT_DEPTH - 1), 4)]; + if (hvd1 * 2 > 9 * hvd0) + *class_idx += ((dir1 << 1) + 2) * 5; + else if (hvd1 > 2 * hvd0) + *class_idx += ((dir1 << 1) + 1) * 5; + + *transpose_idx = dir_d * 2 + dir_hv; +} + +static void FUNC(alf_classify)(int *class_idx, int *transpose_idx, + const uint8_t *_src, const ptrdiff_t _src_stride, const int width, const int height, + const int vb_pos, int *gradient_tmp) +{ + int *grad; + + const int h = height + ALF_GRADIENT_BORDER * 2; + const int w = width + ALF_GRADIENT_BORDER * 2; + const int size = (ALF_BLOCK_SIZE + ALF_GRADIENT_BORDER * 2) / ALF_GRADIENT_STEP; + const int gstride = (w / ALF_GRADIENT_STEP) * ALF_NUM_DIR; + + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + src -= (ALF_GRADIENT_BORDER + 1) * src_stride + ALF_GRADIENT_BORDER; + + grad = gradient_tmp; + for (int y = 0; y < h; y += ALF_GRADIENT_STEP) { + const pixel *s0 = src + y * src_stride; + const pixel *s1 = s0 + src_stride; + const pixel *s2 = s1 + src_stride; + const pixel *s3 = s2 + src_stride; + + if (y == vb_pos) //above + s3 = s2; + else if (y == vb_pos + ALF_GRADIENT_BORDER) + s0 = s1; + + for (int x = 0; x < w; x += ALF_GRADIENT_STEP) { + //two points a time + const pixel *a0 = s0 + x; + const pixel *p0 = s1 + x; + const pixel *b0 = s2 + x; + const int val0 = (*p0) << 1; + + const pixel *a1 = s1 + x + 1; + const pixel *p1 = s2 + x + 1; + const pixel *b1 = s3 + x + 1; + const int val1 = (*p1) << 1; + + grad[ALF_DIR_VERT] = FFABS(val0 - *a0 - *b0) + FFABS(val1 - *a1 - *b1); + grad[ALF_DIR_HORZ] = FFABS(val0 - *(p0 - 1) - *(p0 + 1)) + FFABS(val1 - *(p1 - 1) - *(p1 + 1)); + grad[ALF_DIR_DIGA0] = FFABS(val0 - *(a0 - 1) - *(b0 + 1)) + FFABS(val1 - *(a1 - 1) - *(b1 + 1)); + grad[ALF_DIR_DIGA1] = FFABS(val0 - *(a0 + 1) - *(b0 - 1)) + FFABS(val1 - *(a1 + 1) - *(b1 - 1)); + grad += ALF_NUM_DIR; + } + } + + for (int y = 0; y < height ; y += ALF_BLOCK_SIZE ) { + int start = 0; + int end = (ALF_BLOCK_SIZE + ALF_GRADIENT_BORDER * 2) / ALF_GRADIENT_STEP; + int ac = 2; + if (y + ALF_BLOCK_SIZE == vb_pos) { + end -= ALF_GRADIENT_BORDER / ALF_GRADIENT_STEP; + ac = 3; + } else if (y == vb_pos) { + start += ALF_GRADIENT_BORDER / ALF_GRADIENT_STEP; + ac = 3; + } + for (int x = 0; x < width; x += ALF_BLOCK_SIZE) { + const int xg = x / ALF_GRADIENT_STEP; + const int yg = y / ALF_GRADIENT_STEP; + int sum[ALF_NUM_DIR] = { 0 }; + + grad = gradient_tmp + (yg + start) * gstride + xg * ALF_NUM_DIR; + //todo: optimize this loop + for (int i = start; i < end; i++) { + for (int j = 0; j < size; j++) { + sum[ALF_DIR_VERT] += grad[ALF_DIR_VERT]; + sum[ALF_DIR_HORZ] += grad[ALF_DIR_HORZ]; + sum[ALF_DIR_DIGA0] += grad[ALF_DIR_DIGA0]; + sum[ALF_DIR_DIGA1] += grad[ALF_DIR_DIGA1]; + grad += ALF_NUM_DIR; + } + grad += gstride - size * ALF_NUM_DIR; + } + FUNC(alf_get_idx)(class_idx, transpose_idx, sum, ac); + + class_idx++; + transpose_idx++; + } + } + +} + +static void FUNC(alf_recon_coeff_and_clip)(int16_t *coeff, int16_t *clip, + const int *class_idx, const int *transpose_idx, const int size, + const int16_t *coeff_set, const uint8_t *clip_idx_set, const uint8_t *class_to_filt) +{ + const static int index[][ALF_NUM_COEFF_LUMA] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, + { 9, 4, 10, 8, 1, 5, 11, 7, 3, 0, 2, 6 }, + { 0, 3, 2, 1, 8, 7, 6, 5, 4, 9, 10, 11 }, + { 9, 8, 10, 4, 3, 7, 11, 5, 1, 0, 2, 6 }, + }; + + const int16_t clip_set[] = { + 1 << BIT_DEPTH, 1 << (BIT_DEPTH - 3), 1 << (BIT_DEPTH - 5), 1 << (BIT_DEPTH - 7) + }; + + for (int i = 0; i < size; i++) { + const int16_t *src_coeff = coeff_set + class_to_filt[class_idx[i]] * ALF_NUM_COEFF_LUMA; + const uint8_t *clip_idx = clip_idx_set + class_idx[i] * ALF_NUM_COEFF_LUMA; + + for (int j = 0; j < ALF_NUM_COEFF_LUMA; j++) { + const int idx = index[transpose_idx[i]][j]; + *coeff++ = src_coeff[idx]; + *clip++ = clip_set[clip_idx[idx]]; + } + } +} + +#undef ALF_DIR_HORZ +#undef ALF_DIR_VERT +#undef ALF_DIR_DIGA0 +#undef ALF_DIR_DIGA1 + +// line zero +#define P7 pix[-8 * xstride] +#define P6 pix[-7 * xstride] +#define P5 pix[-6 * xstride] +#define P4 pix[-5 * xstride] +#define P3 pix[-4 * xstride] +#define P2 pix[-3 * xstride] +#define P1 pix[-2 * xstride] +#define P0 pix[-1 * xstride] +#define Q0 pix[0 * xstride] +#define Q1 pix[1 * xstride] +#define Q2 pix[2 * xstride] +#define Q3 pix[3 * xstride] +#define Q4 pix[4 * xstride] +#define Q5 pix[5 * xstride] +#define Q6 pix[6 * xstride] +#define Q7 pix[7 * xstride] +#define P(x) pix[(-(x)-1) * xstride] +#define Q(x) pix[(x) * xstride] + +// line three. used only for deblocking decision +#define TP7 pix[-8 * xstride + 3 * ystride] +#define TP6 pix[-7 * xstride + 3 * ystride] +#define TP5 pix[-6 * xstride + 3 * ystride] +#define TP4 pix[-5 * xstride + 3 * ystride] +#define TP3 pix[-4 * xstride + 3 * ystride] +#define TP2 pix[-3 * xstride + 3 * ystride] +#define TP1 pix[-2 * xstride + 3 * ystride] +#define TP0 pix[-1 * xstride + 3 * ystride] +#define TQ0 pix[0 * xstride + 3 * ystride] +#define TQ1 pix[1 * xstride + 3 * ystride] +#define TQ2 pix[2 * xstride + 3 * ystride] +#define TQ3 pix[3 * xstride + 3 * ystride] +#define TQ4 pix[4 * xstride + 3 * ystride] +#define TQ5 pix[5 * xstride + 3 * ystride] +#define TQ6 pix[6 * xstride + 3 * ystride] +#define TQ7 pix[7 * xstride + 3 * ystride] +#define TP(x) pix[(-(x)-1) * xstride + 3 * ystride] +#define TQ(x) pix[(x) * xstride + 3 * ystride] + +#define FP3 pix[-4 * xstride + 1 * ystride] +#define FP2 pix[-3 * xstride + 1 * ystride] +#define FP1 pix[-2 * xstride + 1 * ystride] +#define FP0 pix[-1 * xstride + 1 * ystride] +#define FQ0 pix[0 * xstride + 1 * ystride] +#define FQ1 pix[1 * xstride + 1 * ystride] +#define FQ2 pix[2 * xstride + 1 * ystride] +#define FQ3 pix[3 * xstride + 1 * ystride] + +static void FUNC(loop_filter_luma_large)(pixel *pix, const ptrdiff_t xstride, const ptrdiff_t ystride, const int32_t tc, + const uint8_t no_p, const uint8_t no_q, const uint8_t max_len_p, const uint8_t max_len_q) +{ + for (int d = 0; d < 4; d++) { + const int p6 = P6; + const int p5 = P5; + const int p4 = P4; + const int p3 = P3; + const int p2 = P2; + const int p1 = P1; + const int p0 = P0; + const int q0 = Q0; + const int q1 = Q1; + const int q2 = Q2; + const int q3 = Q3; + const int q4 = Q4; + const int q5 = Q5; + const int q6 = Q6; + int m; + if (max_len_p == 5 && max_len_q == 5) + m = (p4 + p3 + 2 * (p2 + p1 + p0 + q0 + q1 + q2) + q3 + q4 + 8) >> 4; + else if (max_len_p == max_len_q) + m = (p6 + p5 + p4 + p3 + p2 + p1 + 2 * (p0 + q0) + q1 + q2 + q3 + q4 + q5 + q6 + 8) >> 4; + else if (max_len_p + max_len_q == 12) + m = (p5 + p4 + p3 + p2 + 2 * (p1 + p0 + q0 + q1) + q2 + q3 + q4 + q5 + 8) >> 4; + else if (max_len_p + max_len_q == 8) + m = (p3 + p2 + p1 + p0 + q0 + q1 + q2 + q3 + 4) >> 3; + else if (max_len_q == 7) + m = (2 * (p2 + p1 + p0 + q0) + p0 + p1 + q1 + q2 + q3 + q4 + q5 + q6 + 8) >> 4; + else + m = (p6 + p5 + p4 + p3 + p2 + p1 + 2 * (q2 + q1 + q0 + p0) + q0 + q1 + 8) >> 4; + if (!no_p) { + const int refp = (P(max_len_p) + P(max_len_p - 1) + 1) >> 1; + if (max_len_p == 3) { + P0 = p0 + av_clip(((m * 53 + refp * 11 + 32) >> 6) - p0, -(tc * 6 >> 1), (tc * 6 >> 1)); + P1 = p1 + av_clip(((m * 32 + refp * 32 + 32) >> 6) - p1, -(tc * 4 >> 1), (tc * 4 >> 1)); + P2 = p2 + av_clip(((m * 11 + refp * 53 + 32) >> 6) - p2, -(tc * 2 >> 1), (tc * 2 >> 1)); + } else if (max_len_p == 5) { + P0 = p0 + av_clip(((m * 58 + refp * 6 + 32) >> 6) - p0, -(tc * 6 >> 1), (tc * 6 >> 1)); + P1 = p1 + av_clip(((m * 45 + refp * 19 + 32) >> 6) - p1, -(tc * 5 >> 1), (tc * 5 >> 1)); + P2 = p2 + av_clip(((m * 32 + refp * 32 + 32) >> 6) - p2, -(tc * 4 >> 1), (tc * 4 >> 1)); + P3 = p3 + av_clip(((m * 19 + refp * 45 + 32) >> 6) - p3, -(tc * 3 >> 1), (tc * 3 >> 1)); + P4 = p4 + av_clip(((m * 6 + refp * 58 + 32) >> 6) - p4, -(tc * 2 >> 1), (tc * 2 >> 1)); + } else { + P0 = p0 + av_clip(((m * 59 + refp * 5 + 32) >> 6) - p0, -(tc * 6 >> 1), (tc * 6 >> 1)); + P1 = p1 + av_clip(((m * 50 + refp * 14 + 32) >> 6) - p1, -(tc * 5 >> 1), (tc * 5 >> 1)); + P2 = p2 + av_clip(((m * 41 + refp * 23 + 32) >> 6) - p2, -(tc * 4 >> 1), (tc * 4 >> 1)); + P3 = p3 + av_clip(((m * 32 + refp * 32 + 32) >> 6) - p3, -(tc * 3 >> 1), (tc * 3 >> 1)); + P4 = p4 + av_clip(((m * 23 + refp * 41 + 32) >> 6) - p4, -(tc * 2 >> 1), (tc * 2 >> 1)); + P5 = p5 + av_clip(((m * 14 + refp * 50 + 32) >> 6) - p5, -(tc * 1 >> 1), (tc * 1 >> 1)); + P6 = p6 + av_clip(((m * 5 + refp * 59 + 32) >> 6) - p6, -(tc * 1 >> 1), (tc * 1 >> 1)); + } + } + if (!no_q) { + const int refq = (Q(max_len_q) + Q(max_len_q - 1) + 1) >> 1; + if (max_len_q == 3) { + Q0 = q0 + av_clip(((m * 53 + refq * 11 + 32) >> 6) - q0, -(tc * 6 >> 1), (tc * 6 >> 1)); + Q1 = q1 + av_clip(((m * 32 + refq * 32 + 32) >> 6) - q1, -(tc * 4 >> 1), (tc * 4 >> 1)); + Q2 = q2 + av_clip(((m * 11 + refq * 53 + 32) >> 6) - q2, -(tc * 2 >> 1), (tc * 2 >> 1)); + } else if (max_len_q == 5) { + Q0 = q0 + av_clip(((m * 58 + refq * 6 + 32) >> 6) - q0, -(tc * 6 >> 1), (tc * 6 >> 1)); + Q1 = q1 + av_clip(((m * 45 + refq * 19 + 32) >> 6) - q1, -(tc * 5 >> 1), (tc * 5 >> 1)); + Q2 = q2 + av_clip(((m * 32 + refq * 32 + 32) >> 6) - q2, -(tc * 4 >> 1), (tc * 4 >> 1)); + Q3 = q3 + av_clip(((m * 19 + refq * 45 + 32) >> 6) - q3, -(tc * 3 >> 1), (tc * 3 >> 1)); + Q4 = q4 + av_clip(((m * 6 + refq * 58 + 32) >> 6) - q4, -(tc * 2 >> 1), (tc * 2 >> 1)); + } else { + Q0 = q0 + av_clip(((m * 59 + refq * 5 + 32) >> 6) - q0, -(tc * 6 >> 1), (tc * 6 >> 1)); + Q1 = q1 + av_clip(((m * 50 + refq * 14 + 32) >> 6) - q1, -(tc * 5 >> 1), (tc * 5 >> 1)); + Q2 = q2 + av_clip(((m * 41 + refq * 23 + 32) >> 6) - q2, -(tc * 4 >> 1), (tc * 4 >> 1)); + Q3 = q3 + av_clip(((m * 32 + refq * 32 + 32) >> 6) - q3, -(tc * 3 >> 1), (tc * 3 >> 1)); + Q4 = q4 + av_clip(((m * 23 + refq * 41 + 32) >> 6) - q4, -(tc * 2 >> 1), (tc * 2 >> 1)); + Q5 = q5 + av_clip(((m * 14 + refq * 50 + 32) >> 6) - q5, -(tc * 1 >> 1), (tc * 1 >> 1)); + Q6 = q6 + av_clip(((m * 5 + refq * 59 + 32) >> 6) - q6, -(tc * 1 >> 1), (tc * 1 >> 1)); + } + + } + pix += ystride; + } +} + +static void FUNC(loop_filter_luma_strong)(pixel *pix, const ptrdiff_t xstride, const ptrdiff_t ystride, const int32_t tc, + const uint8_t no_p, const uint8_t no_q) +{ + const int tc2 = tc << 1; + const int tc3 = tc * 3; + for (int d = 0; d < 4; d++) { + const int p3 = P3; + const int p2 = P2; + const int p1 = P1; + const int p0 = P0; + const int q0 = Q0; + const int q1 = Q1; + const int q2 = Q2; + const int q3 = Q3; + if (!no_p) { + P0 = p0 + av_clip(((p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3) - p0, -tc3, tc3); + P1 = p1 + av_clip(((p2 + p1 + p0 + q0 + 2) >> 2) - p1, -tc2, tc2); + P2 = p2 + av_clip(((2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3) - p2, -tc, tc); + } + if (!no_q) { + Q0 = q0 + av_clip(((p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4) >> 3) - q0, -tc3, tc3); + Q1 = q1 + av_clip(((p0 + q0 + q1 + q2 + 2) >> 2) - q1, -tc2, tc2); + Q2 = q2 + av_clip(((2 * q3 + 3 * q2 + q1 + q0 + p0 + 4) >> 3) - q2, -tc, tc); + } + pix += ystride; + } +} + +static void FUNC(loop_filter_luma_weak)(pixel *pix, const ptrdiff_t xstride, const ptrdiff_t ystride, + const int32_t tc, const int32_t beta, const uint8_t no_p, const uint8_t no_q, const int nd_p, const int nd_q) +{ + const int tc_2 = tc >> 1; + for (int d = 0; d < 4; d++) { + const int p2 = P2; + const int p1 = P1; + const int p0 = P0; + const int q0 = Q0; + const int q1 = Q1; + const int q2 = Q2; + int delta0 = (9 * (q0 - p0) - 3 * (q1 - p1) + 8) >> 4; + if (abs(delta0) < 10 * tc) { + delta0 = av_clip(delta0, -tc, tc); + if (!no_p) + P0 = av_clip_pixel(p0 + delta0); + if (!no_q) + Q0 = av_clip_pixel(q0 - delta0); + if (!no_p && nd_p > 1) { + const int deltap1 = av_clip((((p2 + p0 + 1) >> 1) - p1 + delta0) >> 1, -tc_2, tc_2); + P1 = av_clip_pixel(p1 + deltap1); + } + if (!no_q && nd_q > 1) { + const int deltaq1 = av_clip((((q2 + q0 + 1) >> 1) - q1 - delta0) >> 1, -tc_2, tc_2); + Q1 = av_clip_pixel(q1 + deltaq1); + } + } + pix += ystride; + } + +} + +static void FUNC(vvc_loop_filter_luma)(uint8_t* _pix, ptrdiff_t _xstride, ptrdiff_t _ystride, + const int32_t *_beta, const int32_t *_tc, const uint8_t *_no_p, const uint8_t *_no_q, + const uint8_t *_max_len_p, const uint8_t *_max_len_q, int hor_ctu_edge) +{ + const ptrdiff_t xstride = _xstride / sizeof(pixel); + const ptrdiff_t ystride = _ystride / sizeof(pixel); + + for (int i = 0; i < 2; i++) { + pixel* pix = (pixel*)_pix + i * 4 * ystride; + const int dp0 = abs(P2 - 2 * P1 + P0); + const int dq0 = abs(Q2 - 2 * Q1 + Q0); + const int dp3 = abs(TP2 - 2 * TP1 + TP0); + const int dq3 = abs(TQ2 - 2 * TQ1 + TQ0); + const int d0 = dp0 + dq0; + const int d3 = dp3 + dq3; +#if BIT_DEPTH < 10 + const int tc = (_tc[i] + (1 << (9 - BIT_DEPTH))) >> (10 - BIT_DEPTH); +#else + const int tc = _tc[i] << (BIT_DEPTH - 10); +#endif + const int tc25 = ((tc * 5 + 1) >> 1); + + const int no_p = _no_p[i]; + const int no_q = _no_q[i]; + + int max_len_p = _max_len_p[i]; + int max_len_q = _max_len_q[i]; + + const int large_p = (max_len_p > 3 && !hor_ctu_edge); + const int large_q = max_len_q > 3; + const int beta = _beta[i] << BIT_DEPTH - 8; + + const int beta_3 = beta >> 3; + const int beta_2 = beta >> 2; + + if (!tc) + continue; + + if (large_p || large_q) { + const int dp0l = large_p ? ((dp0 + abs(P5 - 2 * P4 + P3) + 1) >> 1) : dp0; + const int dq0l = large_q ? ((dq0 + abs(Q5 - 2 * Q4 + Q3) + 1) >> 1) : dq0; + const int dp3l = large_p ? ((dp3 + abs(TP5 - 2 * TP4 + TP3) + 1) >> 1) : dp3; + const int dq3l = large_q ? ((dq3 + abs(TQ5 - 2 * TQ4 + TQ3) + 1) >> 1) : dq3; + const int d0l = dp0l + dq0l; + const int d3l = dp3l + dq3l; + const int beta53 = beta * 3 >> 5; + const int beta_4 = beta >> 4; + max_len_p = large_p ? max_len_p : 3; + max_len_q = large_q ? max_len_q : 3; + + if (d0l + d3l < beta) { + const int sp0l = abs(P3 - P0) + (max_len_p == 7 ? abs(P7 - P6 - P5 + P4) : 0); + const int sq0l = abs(Q0 - Q3) + (max_len_q == 7 ? abs(Q4 - Q5 - Q6 + Q7) : 0); + const int sp3l = abs(TP3 - TP0) + (max_len_p == 7 ? abs(TP7 - TP6 - TP5 + TP4) : 0); + const int sq3l = abs(TQ0 - TQ3) + (max_len_q == 7 ? abs(TQ4 - TQ5 - TQ6 + TQ7) : 0); + const int sp0 = large_p ? ((sp0l + abs(P3 - P(max_len_p)) + 1) >> 1) : sp0l; + const int sp3 = large_p ? ((sp3l + abs(TP3 - TP(max_len_p)) + 1) >> 1) : sp3l; + const int sq0 = large_q ? ((sq0l + abs(Q3 - Q(max_len_q)) + 1) >> 1) : sq0l; + const int sq3 = large_q ? ((sq3l + abs(TQ3 - TQ(max_len_q)) + 1) >> 1) : sq3l; + if (sp0 + sq0 < beta53 && abs(P0 - Q0) < tc25 && + sp3 + sq3 < beta53 && abs(TP0 - TQ0) < tc25 && + (d0l << 1) < beta_4 && (d3l << 1) < beta_4) { + FUNC(loop_filter_luma_large)(pix, xstride, ystride, tc, no_p, no_q, max_len_p, max_len_q); + continue; + } + } + } + if (d0 + d3 < beta) { + if (max_len_p > 2 && max_len_q > 2 && + abs(P3 - P0) + abs(Q3 - Q0) < beta_3 && abs(P0 - Q0) < tc25 && + abs(TP3 - TP0) + abs(TQ3 - TQ0) < beta_3 && abs(TP0 - TQ0) < tc25 && + (d0 << 1) < beta_2 && (d3 << 1) < beta_2) { + FUNC(loop_filter_luma_strong)(pix, xstride, ystride, tc, no_p, no_q); + } else { // weak filtering + int nd_p = 1; + int nd_q = 1; + if (max_len_p > 1 && max_len_q > 1) { + if (dp0 + dp3 < ((beta + (beta >> 1)) >> 3)) + nd_p = 2; + if (dq0 + dq3 < ((beta + (beta >> 1)) >> 3)) + nd_q = 2; + } + FUNC(loop_filter_luma_weak)(pix, xstride, ystride, tc, beta, no_p, no_q, nd_p, nd_q); + } + } + } +} + +static void FUNC(loop_filter_chroma_strong)(pixel *pix, const ptrdiff_t xstride, const ptrdiff_t ystride, + const int size, const int32_t tc, const uint8_t no_p, const uint8_t no_q) +{ + for (int d = 0; d < size; d++) { + const int p3 = P3; + const int p2 = P2; + const int p1 = P1; + const int p0 = P0; + const int q0 = Q0; + const int q1 = Q1; + const int q2 = Q2; + const int q3 = Q3; + if (!no_p) { + P0 = av_clip((p3 + p2 + p1 + 2 * p0 + q0 + q1 + q2 + 4) >> 3, p0 - tc, p0 + tc); + P1 = av_clip((2 * p3 + p2 + 2 * p1 + p0 + q0 + q1 + 4) >> 3, p1 - tc, p1 + tc); + P2 = av_clip((3 * p3 + 2 * p2 + p1 + p0 + q0 + 4) >> 3, p2 - tc, p2 + tc ); + } + if (!no_q) { + Q0 = av_clip((p2 + p1 + p0 + 2 * q0 + q1 + q2 + q3 + 4) >> 3, q0 - tc, q0 + tc); + Q1 = av_clip((p1 + p0 + q0 + 2 * q1 + q2 + 2 * q3 + 4) >> 3, q1 - tc, q1 + tc); + Q2 = av_clip((p0 + q0 + q1 + 2 * q2 + 3 * q3 + 4) >> 3, q2 - tc, q2 + tc); + } + pix += ystride; + } +} + +static void FUNC(loop_filter_chroma_strong_one_side)(pixel *pix, const ptrdiff_t xstride, const ptrdiff_t ystride, + const int size, const int32_t tc, const uint8_t no_p, const uint8_t no_q) +{ + for (int d = 0; d < size; d++) { + const int p1 = P1; + const int p0 = P0; + const int q0 = Q0; + const int q1 = Q1; + const int q2 = Q2; + const int q3 = Q3; + if (!no_p) { + P0 = av_clip((3 * p1 + 2 * p0 + q0 + q1 + q2 + 4) >> 3, p0 - tc, p0 + tc); + } + if (!no_q) { + Q0 = av_clip((2 * p1 + p0 + 2 * q0 + q1 + q2 + q3 + 4) >> 3, q0 - tc, q0 + tc); + Q1 = av_clip((p1 + p0 + q0 + 2 * q1 + q2 + 2 * q3 + 4) >> 3, q1 - tc, q1 + tc); + Q2 = av_clip((p0 + q0 + q1 + 2 * q2 + 3 * q3 + 4) >> 3, q2 - tc, q2 + tc); + } + pix += ystride; + } +} + +static void FUNC(loop_filter_chroma_weak)(pixel *pix, const ptrdiff_t xstride, const ptrdiff_t ystride, + const int size, const int32_t tc, const uint8_t no_p, const uint8_t no_q) +{ + for (int d = 0; d < size; d++) { + int delta0; + const int p1 = P1; + const int p0 = P0; + const int q0 = Q0; + const int q1 = Q1; + delta0 = av_clip((((q0 - p0) * 4) + p1 - q1 + 4) >> 3, -tc, tc); + if (!no_p) + P0 = av_clip_pixel(p0 + delta0); + if (!no_q) + Q0 = av_clip_pixel(q0 - delta0); + pix += ystride; + } +} + +static void FUNC(vvc_loop_filter_chroma)(uint8_t *_pix, const ptrdiff_t _xstride, const ptrdiff_t _ystride, + const int32_t *_beta, const int32_t *_tc, const uint8_t *_no_p, const uint8_t *_no_q, + const uint8_t *_max_len_p, const uint8_t *_max_len_q, const int shift) +{ + const ptrdiff_t xstride = _xstride / sizeof(pixel); + const ptrdiff_t ystride = _ystride / sizeof(pixel); + const int size = shift ? 2 : 4; + const int end = 8 / size; // 8 samples a loop + + for (int i = 0; i < end; i++) { + pixel *pix = (pixel *)_pix + i * size * ystride; + const uint8_t no_p = _no_p[i]; + const uint8_t no_q = _no_q[i]; + const int beta = _beta[i] << (BIT_DEPTH - 8); + const int beta_3 = beta >> 3; + const int beta_2 = beta >> 2; + +#if BIT_DEPTH < 10 + const int tc = (_tc[i] + (1 << (9 - BIT_DEPTH))) >> (10 - BIT_DEPTH); +#else + const int tc = _tc[i] << (BIT_DEPTH - 10); +#endif + const int tc25 = ((tc * 5 + 1) >> 1); + + uint8_t max_len_p = _max_len_p[i]; + uint8_t max_len_q = _max_len_q[i]; + + if (!max_len_p || !max_len_q || !tc) + continue; + + if (max_len_q == 3){ + const int p1n = shift ? FP1 : TP1; + const int p2n = max_len_p == 1 ? p1n : (shift ? FP2 : TP2); + const int p0n = shift ? FP0 : TP0; + const int q0n = shift ? FQ0 : TQ0; + const int q1n = shift ? FQ1 : TQ1; + const int q2n = shift ? FQ2 : TQ2; + const int p3 = max_len_p == 1 ? P1 : P3; + const int p2 = max_len_p == 1 ? P1 : P2; + const int p1 = P1; + const int p0 = P0; + const int dp0 = abs(p2 - 2 * p1 + p0); + const int dq0 = abs(Q2 - 2 * Q1 + Q0); + + const int dp1 = abs(p2n - 2 * p1n + p0n); + const int dq1 = abs(q2n - 2 * q1n + q0n); + const int d0 = dp0 + dq0; + const int d1 = dp1 + dq1; + + if (d0 + d1 < beta) { + const int p3n = max_len_p == 1 ? p1n : (shift ? FP3 : TP3); + const int q3n = shift ? FQ3 : TQ3; + const int dsam0 = (d0 << 1) < beta_2 && (abs(p3 - p0) + abs(Q0 - Q3) < beta_3) && + abs(p0 - Q0) < tc25; + const int dsam1 = (d1 << 1) < beta_2 && (abs(p3n - p0n) + abs(q0n - q3n) < beta_3) && + abs(p0n - q0n) < tc25; + if (!dsam0 || !dsam1) + max_len_p = max_len_q = 1; + } else { + max_len_p = max_len_q = 1; + } + } + + if (max_len_p == 3 && max_len_q == 3) + FUNC(loop_filter_chroma_strong)(pix, xstride, ystride, size, tc, no_p, no_q); + else if (max_len_q == 3) + FUNC(loop_filter_chroma_strong_one_side)(pix, xstride, ystride, size, tc, no_p, no_q); + else + FUNC(loop_filter_chroma_weak)(pix, xstride, ystride, size, tc, no_p, no_q); + } +} + +static void FUNC(vvc_h_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride, + const int32_t *beta, const int32_t *tc, const uint8_t *no_p, const uint8_t *no_q, + const uint8_t *max_len_p, const uint8_t *max_len_q, int shift) +{ + FUNC(vvc_loop_filter_chroma)(pix, stride, sizeof(pixel), beta, tc, + no_p, no_q, max_len_p, max_len_q, shift); +} + +static void FUNC(vvc_v_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride, + const int32_t *beta, const int32_t *tc, const uint8_t *no_p, const uint8_t *no_q, + const uint8_t *max_len_p, const uint8_t *max_len_q, int shift) +{ + FUNC(vvc_loop_filter_chroma)(pix, sizeof(pixel), stride, beta, tc, + no_p, no_q, max_len_p, max_len_q, shift); +} + +static void FUNC(vvc_h_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride, + const int32_t *beta, const int32_t *tc, const uint8_t *no_p, const uint8_t *no_q, + const uint8_t *max_len_p, const uint8_t *max_len_q, int hor_ctu_edge) +{ + FUNC(vvc_loop_filter_luma)(pix, stride, sizeof(pixel), beta, tc, + no_p, no_q, max_len_p, max_len_q, hor_ctu_edge); +} + +static void FUNC(vvc_v_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride, + const int32_t *beta, const int32_t *tc, const uint8_t *no_p, const uint8_t *no_q, + const uint8_t *max_len_p, const uint8_t *max_len_q, int hor_ctu_edge) +{ + FUNC(vvc_loop_filter_luma)(pix, sizeof(pixel), stride, beta, tc, + no_p, no_q, max_len_p, max_len_q, hor_ctu_edge); +} + +static int FUNC(vvc_loop_ladf_level)(const uint8_t *_pix, const ptrdiff_t _xstride, const ptrdiff_t _ystride) +{ + const pixel *pix = (pixel *)_pix; + const ptrdiff_t xstride = _xstride / sizeof(pixel); + const ptrdiff_t ystride = _ystride / sizeof(pixel); + return (P0 + TP0 + Q0 + TQ0) >> 2; +} + +static int FUNC(vvc_h_loop_ladf_level)(const uint8_t *pix, ptrdiff_t stride) +{ + return FUNC(vvc_loop_ladf_level)(pix, stride, sizeof(pixel)); +} + +static int FUNC(vvc_v_loop_ladf_level)(const uint8_t *pix, ptrdiff_t stride) +{ + return FUNC(vvc_loop_ladf_level)(pix, sizeof(pixel), stride); +} + +#undef P7 +#undef P6 +#undef P5 +#undef P4 +#undef P3 +#undef P2 +#undef P1 +#undef P0 +#undef Q0 +#undef Q1 +#undef Q2 +#undef Q3 +#undef Q4 +#undef Q5 +#undef Q6 +#undef Q7 + +#undef TP7 +#undef TP6 +#undef TP5 +#undef TP4 +#undef TP3 +#undef TP2 +#undef TP1 +#undef TP0 +#undef TQ0 +#undef TQ1 +#undef TQ2 +#undef TQ3 +#undef TQ4 +#undef TQ5 +#undef TQ6 +#undef TQ7 + +static void FUNC(ff_vvc_lmcs_dsp_init)(VVCLMCSDSPContext *const lmcs) +{ + lmcs->filter = FUNC(lmcs_filter_luma); +} + +static void FUNC(ff_vvc_lf_dsp_init)(VVCLFDSPContext *const lf) +{ + lf->ladf_level[0] = FUNC(vvc_h_loop_ladf_level); + lf->ladf_level[1] = FUNC(vvc_v_loop_ladf_level); + lf->filter_luma[0] = FUNC(vvc_h_loop_filter_luma); + lf->filter_luma[1] = FUNC(vvc_v_loop_filter_luma); + lf->filter_chroma[0] = FUNC(vvc_h_loop_filter_chroma); + lf->filter_chroma[1] = FUNC(vvc_v_loop_filter_chroma); +} + +static void FUNC(ff_vvc_sao_dsp_init)(VVCSAODSPContext *const sao) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(sao->band_filter); i++) + sao->band_filter[i] = FUNC(sao_band_filter); + for (int i = 0; i < FF_ARRAY_ELEMS(sao->edge_filter); i++) + sao->edge_filter[i] = FUNC(sao_edge_filter); + sao->edge_restore[0] = FUNC(sao_edge_restore_0); + sao->edge_restore[1] = FUNC(sao_edge_restore_1); +} + +static void FUNC(ff_vvc_alf_dsp_init)(VVCALFDSPContext *const alf) +{ + alf->filter[LUMA] = FUNC(alf_filter_luma); + alf->filter[CHROMA] = FUNC(alf_filter_chroma); + alf->filter_cc = FUNC(alf_filter_cc); + alf->classify = FUNC(alf_classify); + alf->recon_coeff_and_clip = FUNC(alf_recon_coeff_and_clip); +} From patchwork Sun Nov 12 10:35:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 44631 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:92a5:b0:181:818d:5e7f with SMTP id q37csp728679pzg; Sun, 12 Nov 2023 02:38:18 -0800 (PST) X-Google-Smtp-Source: AGHT+IEL0c53LbXIFixBOR0t7WLrn0A0xGdALb/5BLZ4KRwljKsHCKs41ClWKK2cKt9Afty2WcNG X-Received: by 2002:a05:6402:412:b0:547:2547:d973 with SMTP id q18-20020a056402041200b005472547d973mr2087013edv.6.1699785498550; Sun, 12 Nov 2023 02:38:18 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id i1-20020a50d741000000b0053deaf10390si1573327edj.339.2023.11.12.02.38.18; Sun, 12 Nov 2023 02:38:18 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@outlook.com header.s=selector1 header.b=kpxBMrGL; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 269E868CCB8; Sun, 12 Nov 2023 12:36:29 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from APC01-PSA-obe.outbound.protection.outlook.com (mail-psaapc01olkn2037.outbound.protection.outlook.com [40.92.52.37]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 43DDA68CC70 for ; Sun, 12 Nov 2023 12:36:20 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=ncgtqjQStK1UmO0jyoiOYUvOYqqDZVwG956PyLFb0ZupyVQEnIEq6N528j2S5s6dVAeVHq5MoHMb7Lgn31eKyucpS97+bChaG2sIEpvIXcF9jSnIH4QMdp7MfXjal3QuboIU41o3D5DnNpGPk5yqj0Xmj+vx1PbomstQIuK+9I4319FSI+OzOHRdcKi3lauRqyB/X3jDK74F+sEPh2/zxLCYsOMtPT6gNj8FmSHGDSnjz9evUIIWsXwMQjCJ8hmII+97bHjCVcrIhkmaLoPk2gnQJeToF2Dbvq+ubcU1RxzS7s+JrIQ+m6r1OZzXfxQ/iPqvzD7GEIp/0FUuUzPxEg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=VMcL0jtMx/VN4yEWr5RvYa01yy3SYVyzgWvoReByyaU=; b=FK6HZvP10m6fuD0jrHVzP43rwxZcRtkjm5+dwnpCO8NzPKF4oJi1puFIQaxG8b/xfEglPyJ5Og83NEQKnNJvs2D9YTuq4hyuvPQzj6a/8gzREnEXj2NSLaQix0cexDetgvOSvD+7i6+4Iif33Q0fExZlTiDURO2opSoKGEb5geo5xxSPIAWIaLMxxlY3O+iiOF6kE+ffatJjx2/tycmGJxWk6DDQUQb/eJNlfHLD5Hr3tU+nPUyVOFZfu/2AdKHLzgEB3755NEEC3Ctc6nFGGNxemNHCVn9Xx7qmn0ICuu9VxDucg9kp1YJUMWH4c4U8ONmN8qiG3AbHCZ/1z/EAdA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=outlook.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=VMcL0jtMx/VN4yEWr5RvYa01yy3SYVyzgWvoReByyaU=; b=kpxBMrGLWkPeDaDiadLkuTbCThaxa2TGe+ll6Uhj98THjcqOaZXpmPuaeru8kgPxiyR5KZkxw8QVb85J6L4ZCRLTpvR0fWGZnteZ4dmvxKsBNyRXgl4CCzi2ySYCvX0usA7VIbFUEaxBQsRYboeSs+7cd0eeKLkeUt978otJMABeFvU0cqY5ZKDWndMvxBsvH9/DnIYTbkm46yVuWWXNZA7WQZnZz0H0ctsk2xf2+heUzADWxcZRHE6McwpcIliZRUbe3EFuthRnu7hXYleHxF0NIiCvIIHbtYXuMi5vsQc0m/7ZlnTOe16LQson6Xg7ntpFFV5RiuJ0GJOf7sn7IQ== Received: from TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) by TYSPR06MB6768.apcprd06.prod.outlook.com (2603:1096:400:475::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6954.29; Sun, 12 Nov 2023 10:36:02 +0000 Received: from TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f]) by TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f%4]) with mapi id 15.20.6954.028; Sun, 12 Nov 2023 10:36:02 +0000 From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Nov 2023 18:35:23 +0800 Message-ID: X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231112103526.11245-1-nuomi2021@gmail.com> References: <20231112103526.11245-1-nuomi2021@gmail.com> X-TMN: [yrt63qO4QfheDHOljgfksh7sWtv9REm4] X-ClientProxiedBy: TYWP286CA0030.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:262::20) To TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) X-Microsoft-Original-Message-ID: <20231112103526.11245-11-nuomi2021@gmail.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 2 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: TYSPR06MB6433:EE_|TYSPR06MB6768:EE_ X-MS-Office365-Filtering-Correlation-Id: e3a1503c-959c-4d2a-4a22-08dbe36b2a82 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: p85UDLdsOaG9qoWHZj0jwQHk7928uVBJETJJCNmY7e4Im/6siP9W5aspiJCCUqmocJnQqBi1XuC7I+6vw+ZfpBDjOtZ/E9LOTV/2dwHub2fR/TDIABHteZR6XujFxNCJJVCIjeXC3EzLuglhpFDTUeLnPo3nV5s6/LCIlifXiHs/xyz6yNXHS9tCiaCV/Ta1irBWNMb1ysH6p6VW6Og2Ym2eVuxsyrScNF5kl3VD8kvHWiMuPX7NomA9m4nHw9HMdFpvG/BSfPDIzFT9iqI/n6y7uIhpHoOTf587yskrmr1VbcocR7MhifAOQEkDP3bxRCwC9ithqqUbsBCErGO67XMN0RDfOYSsEu/sS7ggp7/PHJ1DlwwELb3Z31YeLLjIphlKuDw4swQk/5XRSWIeDl/EVsuNF7huK4fUEeyhyq5ERmMltTJlgx/gKNrxzcdhmd9cp0hFh1eGrdSIazyYUNG2EDsdQjlxcLNMU8B83OFGBusQGGcGZbTj1Hcxe/RLObheGfhJ4LGFP9wmQW7uJGptk7qilT7EwTO+FXbsZzsjXMhfNgNCI+zvj2qxZaSbi/XfNsQXi/R52kf+SOpMgToZ7o3YKMGT+3T8C9BIah++oYPrKG80GTS3BQbdnA4O X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: u9NUIjHyeOJeCaMKX2sVG3q9lH6iLP45Qj6PojOqcI2ShugzCe6Wfv0RPXhY4wL9qHKHq1dAU9pksmUrzwez1uMnCTS3m9NY0jnDAdyeCBzKlI0x7zYYc0f+aOBurSMzKQw1RfDRqIytu+EBQR9KUUuYK16ZXKGhJN4q6T8v/LEYhl32xbQjXXPm6b4KH/hwFmvwNePrtCFAfirFO/6syxt9rCoX0xxR2Rf+1u7EyrE/iJYhNWAgNyQrbTxt95YjyjZjJcHDHkg9337ofKh8ttVYceREHZ/qYGV4GSklJ2rzfb1VQbuO0Cc84eflJofOJqBKrblICorgmyyA0wLl3VUOhjFMinPPsg4H4g4Dtvrinuxg0QjI/qHuX/w3k48VQW9nOHpCjlEvE0PR4kXQTR2g5mTH09/aU8tuBIul74D/+SATcytKpV3DIb0ilkN+ziNuzyAlnRTu3ijI+ew0AdAUX2zUbcPn7KMAm7aNSj88isCPOY8uDbg0QcP+rbWvJ7vmy92lq3L/QiWIVH/wpGW2Yb4wBha58XqZbqzkuFJFrlyWtHF0FGkx8EMpA6YFBfX2VCT5njFlLr8EB+wacEHgWp8D+3WgVjVqtghZ02LWQBhe5Sbqy9u1B+1CceFSIA2GtoHjqBSuESkgTAqa23agne0z9ZCAphOVDEtydcpXT6ePfZZxCCRwiCsIgQ2XG3KrXaf16FhbhbL8+npoJJBMn0p9l6+ecyL689LF/izC3ngDYmAsOA0+RMNIMADHEFZjg4ZUFTGXja16Q8pN38wIWftt47iUZ2NWx3/qeGrJ0Dpd4nhnT/9JRFHcEtB1dSo4Ozt7kW5nyJ+Wu3c6QEBEN9ANWJRUEgXHmHZ3yeoO0VrCRrjtWCOf79TkH+8GPWpVUa01NbYTGSi7g763G4SiKQw9hlXbjU/3OtcptLaeRwI7XcaISgc6iijlDpehV+RRZfKonuCoYFphBl87ZMby8eWEwNm8WVOXXP2CdzCXpaxs0PjoKRctYvTYZpIQTjszgKKgOQzhwcL6iKmi+kavU/jgh19v1CNH69Z0wIaNSZujoQZSADutMs/7/EdxemyccdhPotDtjz32fMggYGys4qi9LVTz6SEB4+W3HWnVpV0LEM+iVkw9ILKH4pEanOR87gDzpIh4TlTKk1FMQc6689NCwqrCpc5wQRo9J4XWM1VlIxn8P/rGaZ/g4OpI4GUXdeb/+GTzUO+QU298Q64Eji3EZZ0eZWGgAnkPXU6PQIYVK8htSU8Gp0OiN76e X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: e3a1503c-959c-4d2a-4a22-08dbe36b2a82 X-MS-Exchange-CrossTenant-AuthSource: TYSPR06MB6433.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Nov 2023 10:36:02.0103 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYSPR06MB6768 Subject: [FFmpeg-devel] [PATCH v5 11/14] vvcdec: add dsp init and inv transform X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Nuo Mi Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: p4Mul9WNWvpO --- libavcodec/vvc/Makefile | 1 + libavcodec/vvc/vvcdsp.c | 141 +++++++++++++++++++++++++++++++ libavcodec/vvc/vvcdsp_template.c | 120 ++++++++++++++++++++++++++ 3 files changed, 262 insertions(+) create mode 100644 libavcodec/vvc/vvcdsp.c create mode 100644 libavcodec/vvc/vvcdsp_template.c diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile index 9e7fef7d38..bc663a5b50 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -2,6 +2,7 @@ clean:: $(RM) $(CLEANSUFFIXES:%=libavcodec/vvc/%) OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ + vvc/vvcdsp.o \ vvc/vvc_cabac.o \ vvc/vvc_ctu.o \ vvc/vvc_data.o \ diff --git a/libavcodec/vvc/vvcdsp.c b/libavcodec/vvc/vvcdsp.c new file mode 100644 index 0000000000..fa14614397 --- /dev/null +++ b/libavcodec/vvc/vvcdsp.c @@ -0,0 +1,141 @@ +/* + * VVC DSP + * + * Copyright (C) 2021 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "vvcdsp.h" +#include "vvc_ctu.h" +#include "vvc_itx_1d.h" + +#define VVC_SIGN(v) (v < 0 ? -1 : !!v) + +static void av_always_inline pad_int16(int16_t *_dst, const ptrdiff_t dst_stride, const int width, const int height) +{ + const int padded_width = width + 2; + int16_t *dst; + for (int y = 0; y < height; y++) { + dst = _dst + y * dst_stride; + for (int x = 0; x < width; x++) { + dst[-1] = dst[0]; + dst[width] = dst[width - 1]; + } + } + + _dst--; + //top + memcpy(_dst - dst_stride, _dst, padded_width * sizeof(int16_t)); + //bottom + _dst += dst_stride * height; + memcpy(_dst, _dst - dst_stride, padded_width * sizeof(int16_t)); +} + +static int vvc_sad(const int16_t *src0, const int16_t *src1, int dx, int dy, + const int block_w, const int block_h) +{ + int sad = 0; + dx -= 2; + dy -= 2; + src0 += (2 + dy) * MAX_PB_SIZE + 2 + dx; + src1 += (2 - dy) * MAX_PB_SIZE + 2 - dx; + for (int y = 0; y < block_h; y += 2) { + for (int x = 0; x < block_w; x++) { + sad += FFABS(src0[x] - src1[x]); + } + src0 += 2 * MAX_PB_SIZE; + src1 += 2 * MAX_PB_SIZE; + } + return sad; +} + +#define itx_fn(type, s) \ +static void itx_##type##_##s(int *out, ptrdiff_t out_step, const int *in, ptrdiff_t in_step) \ +{ \ + ff_vvc_inv_##type##_##s(out, out_step, in, in_step); \ +} + +#define itx_fn_common(type) \ + itx_fn(type, 4); \ + itx_fn(type, 8); \ + itx_fn(type, 16); \ + itx_fn(type, 32); \ + +itx_fn_common(dct2); +itx_fn_common(dst7); +itx_fn_common(dct8); +itx_fn(dct2, 2); +itx_fn(dct2, 64); + +typedef struct IntraEdgeParams { + uint8_t* top; + uint8_t* left; + int filter_flag; + + uint16_t left_array[3 * MAX_TB_SIZE + 3]; + uint16_t filtered_left_array[3 * MAX_TB_SIZE + 3]; + uint16_t top_array[3 * MAX_TB_SIZE + 3]; + uint16_t filtered_top_array[3 * MAX_TB_SIZE + 3]; +} IntraEdgeParams; + +#define PROF_BORDER_EXT 1 +#define PROF_BLOCK_SIZE (AFFINE_MIN_BLOCK_SIZE + PROF_BORDER_EXT * 2) +#define BDOF_BORDER_EXT 1 + +#define BDOF_PADDED_SIZE (16 + BDOF_BORDER_EXT * 2) +#define BDOF_BLOCK_SIZE 4 +#define BDOF_GRADIENT_SIZE (BDOF_BLOCK_SIZE + BDOF_BORDER_EXT * 2) + +#define BIT_DEPTH 8 +#include "vvcdsp_template.c" +#undef BIT_DEPTH + +#define BIT_DEPTH 10 +#include "vvcdsp_template.c" +#undef BIT_DEPTH + +#define BIT_DEPTH 12 +#include "vvcdsp_template.c" +#undef BIT_DEPTH + +void ff_vvc_dsp_init(VVCDSPContext *vvcdsp, int bit_depth) +{ +#undef FUNC +#define FUNC(a, depth) a ## _ ## depth + +#define VVC_DSP(depth) \ + FUNC(ff_vvc_inter_dsp_init, depth)(&vvcdsp->inter); \ + FUNC(ff_vvc_intra_dsp_init, depth)(&vvcdsp->intra); \ + FUNC(ff_vvc_itx_dsp_init, depth)(&vvcdsp->itx); \ + FUNC(ff_vvc_lmcs_dsp_init, depth)(&vvcdsp->lmcs); \ + FUNC(ff_vvc_lf_dsp_init, depth)(&vvcdsp->lf); \ + FUNC(ff_vvc_sao_dsp_init, depth)(&vvcdsp->sao); \ + FUNC(ff_vvc_alf_dsp_init, depth)(&vvcdsp->alf); \ + + switch (bit_depth) { + case 12: + VVC_DSP(12); + break; + case 10: + VVC_DSP(10); + break; + default: + VVC_DSP(8); + break; + } +} diff --git a/libavcodec/vvc/vvcdsp_template.c b/libavcodec/vvc/vvcdsp_template.c new file mode 100644 index 0000000000..f92c266478 --- /dev/null +++ b/libavcodec/vvc/vvcdsp_template.c @@ -0,0 +1,120 @@ +/* + * VVC transform and residual DSP + * + * Copyright (C) 2021 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "libavutil/frame.h" +#include "libavcodec/bit_depth_template.c" + +#include "vvcdec.h" +#include "vvc_data.h" + +#include "vvc_inter_template.c" +#include "vvc_intra_template.c" +#include "vvc_filter_template.c" + +static void FUNC(add_residual)(uint8_t *_dst, const int *res, + const int w, const int h, const ptrdiff_t _stride) +{ + pixel *dst = (pixel *)_dst; + + const int stride = _stride / sizeof(pixel); + + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + dst[x] = av_clip_pixel(dst[x] + *res); + res++; + } + dst += stride; + } +} + +static void FUNC(add_residual_joint)(uint8_t *_dst, const int *res, + const int w, const int h, const ptrdiff_t _stride, const int c_sign, const int shift) +{ + pixel *dst = (pixel *)_dst; + + const int stride = _stride / sizeof(pixel); + + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + const int r = ((*res) * c_sign) >> shift; + dst[x] = av_clip_pixel(dst[x] + r); + res++; + } + dst += stride; + } +} + +static void FUNC(pred_residual_joint)(int *buf, const int w, const int h, + const int c_sign, const int shift) +{ + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + *buf = ((*buf) * c_sign) >> shift; + buf++; + } + } +} + +static void FUNC(transform_bdpcm)(int *coeffs, const int width, const int height, + const int vertical, const int log2_transform_range) +{ + int x, y; + + if (vertical) { + coeffs += width; + for (y = 0; y < height - 1; y++) { + for (x = 0; x < width; x++) + coeffs[x] = av_clip_intp2(coeffs[x] + coeffs[x - width], log2_transform_range); + coeffs += width; + } + } else { + for (y = 0; y < height; y++) { + for (x = 1; x < width; x++) + coeffs[x] = av_clip_intp2(coeffs[x] + coeffs[x - 1], log2_transform_range); + coeffs += width; + } + } +} + +static void FUNC(ff_vvc_itx_dsp_init)(VVCItxDSPContext *const itx) +{ +#define VVC_ITX(TYPE, type, s) \ + itx->itx[TYPE][TX_SIZE_##s] = itx_##type##_##s; \ + +#define VVC_ITX_COMMON(TYPE, type) \ + VVC_ITX(TYPE, type, 4); \ + VVC_ITX(TYPE, type, 8); \ + VVC_ITX(TYPE, type, 16); \ + VVC_ITX(TYPE, type, 32); + + itx->add_residual = FUNC(add_residual); + itx->add_residual_joint = FUNC(add_residual_joint); + itx->pred_residual_joint = FUNC(pred_residual_joint); + itx->transform_bdpcm = FUNC(transform_bdpcm); + VVC_ITX(DCT2, dct2, 2) + VVC_ITX(DCT2, dct2, 64) + VVC_ITX_COMMON(DCT2, dct2) + VVC_ITX_COMMON(DCT8, dct8) + VVC_ITX_COMMON(DST7, dst7) + +#undef VVC_ITX +#undef VVC_ITX_COMMON +} From patchwork Sun Nov 12 10:35:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 44632 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:92a5:b0:181:818d:5e7f with SMTP id q37csp728729pzg; Sun, 12 Nov 2023 02:38:29 -0800 (PST) X-Google-Smtp-Source: AGHT+IFYYqO045LruO44o3Ln0hAWYj9L8Mv3dIj79uxCKuKpRcRkMCW2vDd+4MbU1NiuhlWh3FBp X-Received: by 2002:a17:906:4555:b0:9de:52b:6938 with SMTP id s21-20020a170906455500b009de052b6938mr2716743ejq.9.1699785508832; Sun, 12 Nov 2023 02:38:28 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id lu6-20020a170906fac600b009be755055e6si1562222ejb.901.2023.11.12.02.38.28; Sun, 12 Nov 2023 02:38:28 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@outlook.com header.s=selector1 header.b=EstYbvjT; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 510FF68CCBE; Sun, 12 Nov 2023 12:36:30 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from APC01-SG2-obe.outbound.protection.outlook.com (mail-sgaapc01olkn2035.outbound.protection.outlook.com [40.92.53.35]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id BE5AA68CC6F for ; Sun, 12 Nov 2023 12:36:20 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=fqGsY7cHTZ6sZt6E+Eo/bXajTY0Lnc+xJq2WyxDP3AFv00ZZmIERpqVo9ZED9P0njd4pq6PTdjR3s8tO6kzJVC6EfGV6V+Xqp4OnDhXKLcv8qzrQZwMdI36PhfDs7zrGW+s2HaXRmxKvTQ6aN9pIjjAU9/fqRzNEBIcc2JHbsiAREEAQa0SvXyuNutkZ7QUpM0+8BJtUQf2WMCbnWO/uhs7MFI170XBVK3KO6yM4x+bA9lUtLTtxmBOAjAX4unKTRRfODLRxiKwprc7wQSsURrz1l7SkZMt752hI9npuWRJUDV4U+IZgZg39BOYgOuwQSoHZtrdT4wSg5QDSUo2yWQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=1ZaCcu1KQ2KYgRXXk9oqvsC2VENqN86xpYQcoX7MU3U=; b=gCWZkxQkw/MeNPg4OzDWOJpw0/gm0gr+Be6LmFQ64YTmhww8iN63R9dPuPMnmPZnhtQJCt8SLDB74CGXFLmXvHKBRo9qjJmJawmTR5+gOk7KKXS6j9bJexYNIvTHtiL6AiF+Y0XwT9/BBbGDm4jTlzN3EDgxiY97DB2BDbIiaT39GMMI3wY65EUpeCnOACrVUPQLpaNgbGs5ryjiy743AIA7SHDMl3MyHVT6teSoZCP1LU2uKY0OygFhdO2Tm/heTMlIVS3/edN9iUY3BMe20y264xmRGxVWfNYXL5hOtYvSZKNbV+jp3GoOwSn7XOgOQpQrFDowiiUbVTL4MEsu5A== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=outlook.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=1ZaCcu1KQ2KYgRXXk9oqvsC2VENqN86xpYQcoX7MU3U=; b=EstYbvjTvEfVHNbFNOGpQZEuj2FpsJJznEbPnrpjwbtPE/7kjHDB4O6jgaIThC60SHrXR7gviLbkhg3vMjR9qDsZ7cq2XHmVNXCP333iyK7f5qzC/uRzIwq8BGAkNNoOg/ZW7V/6Q+qDwApTOBoWRWIEBTyP2x+/ufFdWUptnr2iqb8ZCezgfLSZEhbfiJh/7VQSgnmzqLaNXzXcmvjr5OyrMwiuKhiWWapxOij6upBigZ0jhZkhVKcvXlBvhPKh+0SX7wiZ38RDeTAgslZvOfGb9u3KGw16wDeJjkdvKgEm+Zp+WiRDj0CipUd+k6EWs38r16w4Jyd9R9UOV5z+QQ== Received: from TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) by TYSPR06MB6768.apcprd06.prod.outlook.com (2603:1096:400:475::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6954.29; Sun, 12 Nov 2023 10:36:04 +0000 Received: from TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f]) by TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f%4]) with mapi id 15.20.6954.028; Sun, 12 Nov 2023 10:36:04 +0000 From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Nov 2023 18:35:24 +0800 Message-ID: X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231112103526.11245-1-nuomi2021@gmail.com> References: <20231112103526.11245-1-nuomi2021@gmail.com> X-TMN: [YwPpkKiRQfIuMrWod4vnrha64I8oCnJB] X-ClientProxiedBy: TYWP286CA0030.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:262::20) To TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) X-Microsoft-Original-Message-ID: <20231112103526.11245-12-nuomi2021@gmail.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 2 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: TYSPR06MB6433:EE_|TYSPR06MB6768:EE_ X-MS-Office365-Filtering-Correlation-Id: 0d5ca530-35d1-42b3-be54-08dbe36b2af4 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: eMe54jYg6rkg2yxgIplQmG4PTrsAThzfgueGZLfh1rFx+8rYvBUslOlx4jw6IQ3I0PGdlnQ3DP4aPtp3oo1mvCFWgqfmWFadMUAKSBdi+ImS6t1Z/K+yoIECasdgLLW+PYQzrTldclTQFBHvgpdNTH+bM8j9/6wMbasCmUhp8ZfrwCCOF3ismvKs6A98T9aXCIaeF9R+SBo3oDXCgzczpgSAJZmUFDrj063Ekie0Ev6ludgRqWufVrStRRFmNoHSn6W91DzLPgCaZL2glHpVpk2fsmXggB3FwezpWF4vNJLRZNljJmY1Rhae2boOq7rKdStSC2I+fWLiNmqYzatpV+2r1Rg/wVqm7cgiaxcRGaTn9XMwammxc8ZPwg6/2vO+0SPooSF744oq5yPj8rGs35ewLMVVtabI9MJiMj1HL4iqKWsmrqnTIwEU4nxJbTTp5dnzl95GbyJMw60oKXXmnBieYI7Ij81FqlSV9rmqPdbSarw2lXgiB6zW2+nMZThg5fqUFsA0M7aZgW9qbnrtZgY3nqHWmYjspNgXhDskte/i7xfYmKXbHzUPnjTYSKWA3OONJdMle0XNe1db6AehG7NXrUP1i6um7GGsO2L7/ioOQQGSvj4LVWjGIeqrvRDz X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: NTzI5Tn5C2OjSQyqPvcpImAYxCkY5Eb0rrWTJhJziPX/UzFuKbn5HYI/EReFgYs5TxcylGAghW5gLrHWsNgBIn9wN7m90ab+NhwDM3r6RJ8+JkhwU8Rzywwc5BQRtp/86wqDia/gWtuuGTPnsZwv4inezmrnutArw3eJCMZNHzHtkIM6/7Jq2sjDhrHvRBm5LVOIYO4eJ3b7XgBINMjejlYcLB6I5er1kFLzpnXyDem+KfbSVD3WlWSrDZCJ3SerGGuiMBmz1+Fnq1BsS/ecsGULoCnqFUnlKXXbCn8mVkEayXKzrf0FlpuCSgvIiMjUzW3KApVXHVEyi5oMHCg48HFBr3+s3aHx7QekNwrliLwL1Rt+uGOdhOYFaRd1GQ35nvSL+hM20cc+YKPLPsyrvuIzU8qoh2T4B3mpC8B7lJ6LOp3mCZN1OS3R8Y6//xKVXC2JN4f/DGTLhDthFEyhR3HYv63atQwJz7rGC7Pi5IF+0XUQgKV+qwhlltAAsaz7ZZmF2HuvtC0WERiZDAP2A2ixfnF2Q7TuvJB82ft8JFXp0yBGPETcLR7lkTu4dN7EBNJuCKXhfLLmbp1Jo0Cv/9rkmUGSbqfMtpZHBsaJjwxkT8g8gGOOI8eycIV45BV8c9X2ewOymjEVxebTvWGX576wdmtWsVoI3IQR/RFD65cM3WYcr5mPbdk4uGKTI1P3n5ZYuw4ZOJzhhJMEa4GmcMrv8vIbmtPjOwzCILvNOj+XzfpHgSv8oYh4A2bSn6NPWtyyuDlSUkW4k55E9zCyegdnOKkf0ONCwmZyoHZbh3u37k+g1WtkpLNMnTZyUF3UaF9s8sK+aDJZHZNQMmtPkXYnVcVORgTnVLtoZs1bujj2oSq/pWHCM+lZdG9G3kxtGLKTM0CQrqVqTFIlVb/bPP/h+tUnA/uzZP8cngDVNabFF5C1lrMN/Is1rnkWbeqkQ3kfvFL0kCZzjYvxahS0UHPabm+JMwi0/DdqCWQd/Dat2AYxkjW6smwUsLXMPHnmJuJjiAyXcBVhlB85N5IniFO3hZr6m1KeiEVH7gqy4I5eYS3DoMt28xAZxejmGwEhPltV56ouBsIR55MpTSNYSjpisGtUjEfd/aVnT7vZyiomiSofReTk0zHuAAVLjHTn7NWK7AMO/2+B8H2BUPP3mbEVAmEs/Gz0oQ8PoFyN0ddyy/mu5/jKS3rKG1nID/OPnrdpGHHuzytYjt8h+jfTonHuhmh4GU5ojzrFla+t4+HZxjHiggILFaxx6BR76So+ X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 0d5ca530-35d1-42b3-be54-08dbe36b2af4 X-MS-Exchange-CrossTenant-AuthSource: TYSPR06MB6433.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Nov 2023 10:36:02.7267 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYSPR06MB6768 Subject: [FFmpeg-devel] [PATCH v5 12/14] vvcdec: add CTU parser X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Nuo Mi Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 1CIkRUCRuj3w --- libavcodec/vvc/vvc_ctu.c | 2372 ++++++++++++++++++++++++++++++++++++++ libavcodec/vvc/vvc_ctu.h | 11 + 2 files changed, 2383 insertions(+) diff --git a/libavcodec/vvc/vvc_ctu.c b/libavcodec/vvc/vvc_ctu.c index 6138d2fc9f..de2bc9f931 100644 --- a/libavcodec/vvc/vvc_ctu.c +++ b/libavcodec/vvc/vvc_ctu.c @@ -24,8 +24,2372 @@ #include "vvc_cabac.h" #include "vvc_ctu.h" +#include "vvc_inter.h" #include "vvc_mvs.h" +#define PROF_TEMP_SIZE (PROF_BLOCK_SIZE) * sizeof(int16_t) + +#define TAB_MSM(fc, depth, x, y) fc->tab.msm[(depth)][((y) >> 5) * fc->ps.pps->width32 + ((x) >> 5)] +#define TAB_ISPMF(fc, x, y) fc->tab.ispmf[((y) >> 6) * fc->ps.pps->width64 + ((x) >> 6)] + +typedef enum VVCModeType { + MODE_TYPE_ALL, + MODE_TYPE_INTER, + MODE_TYPE_INTRA, +} VVCModeType; + +static void set_tb_pos(const VVCFrameContext *fc, const TransformBlock *tb) +{ + const int x_tb = tb->x0 >> MIN_TU_LOG2; + const int y_tb = tb->y0 >> MIN_TU_LOG2; + const int hs = fc->ps.sps->hshift[tb->c_idx]; + const int vs = fc->ps.sps->vshift[tb->c_idx]; + const int is_chroma = tb->c_idx != 0; + const int width = FFMAX(1, tb->tb_width >> (MIN_TU_LOG2 - hs)); + const int end = y_tb + FFMAX(1, tb->tb_height >> (MIN_TU_LOG2 - vs)); + + for (int y = y_tb; y < end; y++) { + const int off = y * fc->ps.pps->min_tu_width + x_tb; + for (int i = 0; i < width; i++) { + fc->tab.tb_pos_x0[is_chroma][off + i] = tb->x0; + fc->tab.tb_pos_y0[is_chroma][off + i] = tb->y0; + } + memset(fc->tab.tb_width [is_chroma] + off, tb->tb_width, width); + memset(fc->tab.tb_height[is_chroma] + off, tb->tb_height, width); + } +} + +static void set_tb_tab(uint8_t *tab, uint8_t v, const VVCFrameContext *fc, + const TransformBlock *tb) +{ + const int width = tb->tb_width << fc->ps.sps->hshift[tb->c_idx]; + const int height = tb->tb_height << fc->ps.sps->vshift[tb->c_idx]; + + for (int h = 0; h < height; h += MIN_TU_SIZE) { + const int y = (tb->y0 + h) >> MIN_TU_LOG2; + const int off = y * fc->ps.pps->min_tu_width + (tb->x0 >> MIN_TU_LOG2); + const int w = FFMAX(1, width >> MIN_TU_LOG2); + memset(tab + off, v, w); + } +} + +// 8.7.1 Derivation process for quantization parameters +static int get_qp_y_pred(const VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const CodingUnit *cu = lc->cu; + const int ctb_log2_size = sps->ctb_log2_size_y; + const int ctb_size_mask = (1 << ctb_log2_size) - 1; + const int xQg = lc->parse.cu_qg_top_left_x; + const int yQg = lc->parse.cu_qg_top_left_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int x_cb = cu->x0 >> sps->min_cb_log2_size_y; + const int y_cb = cu->y0 >> sps->min_cb_log2_size_y; + const int x_ctb = cu->x0 >> ctb_log2_size; + const int y_ctb = cu->y0 >> ctb_log2_size; + const int in_same_ctb_a = ((xQg - 1) >> ctb_log2_size) == x_ctb && (yQg >> ctb_log2_size) == y_ctb; + const int in_same_ctb_b = (xQg >> ctb_log2_size) == x_ctb && ((yQg - 1) >> ctb_log2_size) == y_ctb; + int qPy_pred, qPy_a, qPy_b; + + if (lc->na.cand_up) { + const int first_qg_in_ctu = !(xQg & ctb_size_mask) && !(yQg & ctb_size_mask); + const int qPy_up = fc->tab.qp[LUMA][x_cb + (y_cb - 1) * min_cb_width]; + if (first_qg_in_ctu && pps->ctb_to_col_bd[xQg >> ctb_log2_size] == xQg) + return qPy_up; + } + + // qPy_pred + qPy_pred = lc->ep->is_first_qg ? lc->sc->sh.slice_qp_y : lc->ep->qp_y; + + // qPy_b + if (!lc->na.cand_up || !in_same_ctb_b) + qPy_b = qPy_pred; + else + qPy_b = fc->tab.qp[LUMA][x_cb + (y_cb - 1) * min_cb_width]; + + // qPy_a + if (!lc->na.cand_left || !in_same_ctb_a) + qPy_a = qPy_pred; + else + qPy_a = fc->tab.qp[LUMA][(x_cb - 1) + y_cb * min_cb_width]; + + av_assert2(qPy_a >= -fc->ps.sps->qp_bd_offset && qPy_a < 63); + av_assert2(qPy_b >= -fc->ps.sps->qp_bd_offset && qPy_b < 63); + + return (qPy_a + qPy_b + 1) >> 1; +} + +static void set_cb_tab(const VVCLocalContext *lc, uint8_t *tab, const uint8_t v) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const CodingUnit *cu = lc->cu; + const int log2_min_cb_size = fc->ps.sps->min_cb_log2_size_y; + const int x_cb = cu->x0 >> log2_min_cb_size; + const int y_cb = cu->y0 >> log2_min_cb_size; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + int x = y_cb * pps->min_cb_width + x_cb; + + for (int y = 0; y < (cb_height >> log2_min_cb_size); y++) { + const int width = cb_width >> log2_min_cb_size; + + memset(&tab[x], v, width); + x += pps->min_cb_width; + } +} + +static int set_qp_y(VVCLocalContext *lc, const int x0, const int y0, const int has_qp_delta) +{ + const VVCSPS *sps = lc->fc->ps.sps; + EntryPoint *ep = lc->ep; + CodingUnit *cu = lc->cu; + int cu_qp_delta = 0; + + if (!lc->fc->ps.pps->r->pps_cu_qp_delta_enabled_flag) { + ep->qp_y = lc->sc->sh.slice_qp_y; + } else if (ep->is_first_qg || (lc->parse.cu_qg_top_left_x == x0 && lc->parse.cu_qg_top_left_y == y0)) { + ep->qp_y = get_qp_y_pred(lc); + ep->is_first_qg = 0; + } + + if (has_qp_delta) { + const int cu_qp_delta_abs = ff_vvc_cu_qp_delta_abs(lc); + + if (cu_qp_delta_abs) + cu_qp_delta = ff_vvc_cu_qp_delta_sign_flag(lc) ? -cu_qp_delta_abs : cu_qp_delta_abs; + if (cu_qp_delta > (31 + sps->qp_bd_offset / 2) || cu_qp_delta < -(32 + sps->qp_bd_offset / 2)) + return AVERROR_INVALIDDATA; + lc->parse.is_cu_qp_delta_coded = 1; + + if (cu_qp_delta) { + int off = sps->qp_bd_offset; + ep->qp_y = FFUMOD(ep->qp_y + cu_qp_delta + 64 + 2 * off, 64 + off) - off; + } + } + + set_cb_tab(lc, lc->fc->tab.qp[LUMA], ep->qp_y); + cu->qp[LUMA] = ep->qp_y; + + return 0; +} + +static void set_qp_c_tab(const VVCLocalContext *lc, const TransformUnit *tu, const TransformBlock *tb) +{ + const int is_jcbcr = tu->joint_cbcr_residual_flag && tu->coded_flag[CB] && tu->coded_flag[CR]; + const int idx = is_jcbcr ? JCBCR : tb->c_idx; + + set_tb_tab(lc->fc->tab.qp[tb->c_idx], lc->cu->qp[idx], lc->fc, tb); +} + +static void set_qp_c(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + CodingUnit *cu = lc->cu; + const int x_center = cu->x0 + cu->cb_width / 2; + const int y_center = cu->y0 + cu->cb_height / 2; + const int single_tree = cu->tree_type == SINGLE_TREE; + const int qp_luma = (single_tree ? lc->ep->qp_y : ff_vvc_get_qPy(fc, x_center, y_center)) + sps->qp_bd_offset; + const int qp_chroma = av_clip(qp_luma, 0, MAX_QP + sps->qp_bd_offset); + const int sh_chroma_qp_offset[] = { + rsh->sh_cb_qp_offset, + rsh->sh_cr_qp_offset, + rsh->sh_joint_cbcr_qp_offset, + }; + int qp; + + for (int i = CB - 1; i < CR + sps->r->sps_joint_cbcr_enabled_flag; i++) { + qp = sps->chroma_qp_table[i][qp_chroma]; + qp = qp + pps->chroma_qp_offset[i] + sh_chroma_qp_offset[i] + lc->parse.chroma_qp_offset[i]; + qp = av_clip(qp, -sps->qp_bd_offset, MAX_QP) + sps->qp_bd_offset; + cu->qp[i + 1] = qp; + } +} + +static TransformUnit* alloc_tu(VVCFrameContext *fc, CodingUnit *cu) +{ + TransformUnit *tu = ff_refstruct_pool_get(fc->tu_pool); + if (!tu) + return NULL; + + tu->next = NULL; + + if (cu->tus.tail) + cu->tus.tail->next = tu; + else + cu->tus.head = tu; + cu->tus.tail = tu; + + return tu; +} + +static TransformUnit* add_tu(VVCFrameContext *fc, CodingUnit *cu, const int x0, const int y0, const int tu_width, const int tu_height) +{ + TransformUnit *tu = alloc_tu(fc, cu); + + if (!tu) + return NULL; + + tu->x0 = x0; + tu->y0 = y0; + tu->width = tu_width; + tu->height = tu_height; + tu->joint_cbcr_residual_flag = 0; + memset(tu->coded_flag, 0, sizeof(tu->coded_flag)); + tu->nb_tbs = 0; + + return tu; +} + +static TransformBlock* add_tb(TransformUnit *tu, VVCLocalContext *lc, + const int x0, const int y0, const int tb_width, const int tb_height, const int c_idx) +{ + TransformBlock *tb; + + tb = &tu->tbs[tu->nb_tbs++]; + tb->has_coeffs = 0; + tb->x0 = x0; + tb->y0 = y0; + tb->tb_width = tb_width; + tb->tb_height = tb_height; + tb->log2_tb_width = log2(tb_width); + tb->log2_tb_height = log2(tb_height); + + tb->max_scan_x = tb->max_scan_y = 0; + tb->min_scan_x = tb->min_scan_y = 0; + + tb->c_idx = c_idx; + tb->ts = 0; + tb->coeffs = lc->coeffs; + lc->coeffs += tb_width * tb_height; + return tb; +} + +static uint8_t tu_y_coded_flag_decode(VVCLocalContext *lc, const int is_sbt_not_coded, + const int sub_tu_index, const int is_isp, const int is_chroma_coded) +{ + uint8_t tu_y_coded_flag = 0; + const VVCSPS *sps = lc->fc->ps.sps; + CodingUnit *cu = lc->cu; + + if (!is_sbt_not_coded) { + int has_y_coded_flag = sub_tu_index < cu->num_intra_subpartitions - 1 || !lc->parse.infer_tu_cbf_luma; + if (!is_isp) { + const int is_large = cu->cb_width > sps->max_tb_size_y || cu->cb_height > sps->max_tb_size_y; + has_y_coded_flag = (cu->pred_mode == MODE_INTRA && !cu->act_enabled_flag) || is_chroma_coded || is_large; + } + tu_y_coded_flag = has_y_coded_flag ? ff_vvc_tu_y_coded_flag(lc) : 1; + } + if (is_isp) + lc->parse.infer_tu_cbf_luma = lc->parse.infer_tu_cbf_luma && !tu_y_coded_flag; + return tu_y_coded_flag; +} + +static void chroma_qp_offset_decode(VVCLocalContext *lc, const int is_128, const int is_chroma_coded) +{ + const VVCPPS *pps = lc->fc->ps.pps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + + if ((is_128 || is_chroma_coded) && + rsh->sh_cu_chroma_qp_offset_enabled_flag && !lc->parse.is_cu_chroma_qp_offset_coded) { + const int cu_chroma_qp_offset_flag = ff_vvc_cu_chroma_qp_offset_flag(lc); + if (cu_chroma_qp_offset_flag) { + int cu_chroma_qp_offset_idx = 0; + if (pps->r->pps_chroma_qp_offset_list_len_minus1 > 0) + cu_chroma_qp_offset_idx = ff_vvc_cu_chroma_qp_offset_idx(lc); + for (int i = CB - 1; i < JCBCR; i++) + lc->parse.chroma_qp_offset[i] = pps->chroma_qp_offset_list[cu_chroma_qp_offset_idx][i]; + } else { + memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset)); + } + lc->parse.is_cu_chroma_qp_offset_coded = 1; + } +} + +static int hls_transform_unit(VVCLocalContext *lc, int x0, int y0,int tu_width, int tu_height, int sub_tu_index, int ch_type) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + CodingUnit *cu = lc->cu; + TransformUnit *tu = add_tu(fc, cu, x0, y0, tu_width, tu_height); + const int min_cb_width = pps->min_cb_width; + const VVCTreeType tree_type = cu->tree_type; + const int is_128 = cu->cb_width > 64 || cu->cb_height > 64; + const int is_isp = cu->isp_split_type != ISP_NO_SPLIT; + const int is_isp_last_tu = is_isp && (sub_tu_index == cu->num_intra_subpartitions - 1); + const int is_sbt_not_coded = cu->sbt_flag && + ((sub_tu_index == 0 && cu->sbt_pos_flag) || (sub_tu_index == 1 && !cu->sbt_pos_flag)); + const int chroma_available = tree_type != DUAL_TREE_LUMA && sps->r->sps_chroma_format_idc && + (!is_isp || is_isp_last_tu); + int ret, xc, yc, wc, hc, is_chroma_coded; + + if (!tu) + return AVERROR_INVALIDDATA; + + if (tree_type == SINGLE_TREE && is_isp_last_tu) { + const int x_cu = x0 >> fc->ps.sps->min_cb_log2_size_y; + const int y_cu = y0 >> fc->ps.sps->min_cb_log2_size_y; + xc = SAMPLE_CTB(fc->tab.cb_pos_x[ch_type], x_cu, y_cu); + yc = SAMPLE_CTB(fc->tab.cb_pos_y[ch_type], x_cu, y_cu); + wc = SAMPLE_CTB(fc->tab.cb_width[ch_type], x_cu, y_cu); + hc = SAMPLE_CTB(fc->tab.cb_height[ch_type], x_cu, y_cu); + } else { + xc = x0, yc = y0, wc = tu_width, hc = tu_height; + } + + if (chroma_available && !is_sbt_not_coded) { + tu->coded_flag[CB] = ff_vvc_tu_cb_coded_flag(lc); + tu->coded_flag[CR] = ff_vvc_tu_cr_coded_flag(lc, tu->coded_flag[CB]); + } + + is_chroma_coded = chroma_available && (tu->coded_flag[CB] || tu->coded_flag[CR]); + + if (tree_type != DUAL_TREE_CHROMA) { + int has_qp_delta; + tu->coded_flag[LUMA] = tu_y_coded_flag_decode(lc, is_sbt_not_coded, sub_tu_index, is_isp, is_chroma_coded); + has_qp_delta = (is_128 || tu->coded_flag[LUMA] || is_chroma_coded) && + pps->r->pps_cu_qp_delta_enabled_flag && !lc->parse.is_cu_qp_delta_coded; + ret = set_qp_y(lc, x0, y0, has_qp_delta); + if (ret < 0) + return ret; + add_tb(tu, lc, x0, y0, tu_width, tu_height, LUMA); + } + if (tree_type != DUAL_TREE_LUMA) { + chroma_qp_offset_decode(lc, is_128, is_chroma_coded); + if (chroma_available) { + const int hs = sps->hshift[CHROMA]; + const int vs = sps->vshift[CHROMA]; + add_tb(tu, lc, xc, yc, wc >> hs, hc >> vs, CB); + add_tb(tu, lc, xc, yc, wc >> hs, hc >> vs, CR); + } + } + if (sps->r->sps_joint_cbcr_enabled_flag && ((cu->pred_mode == MODE_INTRA && + (tu->coded_flag[CB] || tu->coded_flag[CR])) || + (tu->coded_flag[CB] && tu->coded_flag[CR])) && + chroma_available) { + tu->joint_cbcr_residual_flag = ff_vvc_tu_joint_cbcr_residual_flag(lc, tu->coded_flag[1], tu->coded_flag[2]); + } + + for (int i = 0; i < tu->nb_tbs; i++) { + TransformBlock *tb = &tu->tbs[i]; + const int is_chroma = tb->c_idx != LUMA; + tb->has_coeffs = tu->coded_flag[tb->c_idx]; + if (tb->has_coeffs && is_chroma) + tb->has_coeffs = tb->c_idx == CB ? 1 : !(tu->coded_flag[CB] && tu->joint_cbcr_residual_flag); + if (tb->has_coeffs) { + tb->ts = cu->bdpcm_flag[tb->c_idx]; + if (sps->r->sps_transform_skip_enabled_flag && !cu->bdpcm_flag[tb->c_idx] && + tb->tb_width <= sps->max_ts_size && tb->tb_height <= sps->max_ts_size && + !cu->sbt_flag && (is_chroma || !is_isp)) { + tb->ts = ff_vvc_transform_skip_flag(lc, is_chroma); + } + ret = ff_vvc_residual_coding(lc, tb); + if (ret < 0) + return ret; + set_tb_tab(fc->tab.tu_coded_flag[tb->c_idx], tu->coded_flag[tb->c_idx], fc, tb); + } + if (tb->c_idx != CR) + set_tb_pos(fc, tb); + if (tb->c_idx == CB) + set_tb_tab(fc->tab.tu_joint_cbcr_residual_flag, tu->joint_cbcr_residual_flag, fc, tb); + } + + return 0; +} + +static int hls_transform_tree(VVCLocalContext *lc, int x0, int y0,int tu_width, int tu_height, int ch_type) +{ + const CodingUnit *cu = lc->cu; + const VVCSPS *sps = lc->fc->ps.sps; + int ret; + + lc->parse.infer_tu_cbf_luma = 1; + if (cu->isp_split_type == ISP_NO_SPLIT && !cu->sbt_flag) { + if (tu_width > sps->max_tb_size_y || tu_height > sps->max_tb_size_y) { + const int ver_split_first = tu_width > sps->max_tb_size_y && tu_width > tu_height; + const int trafo_width = ver_split_first ? (tu_width / 2) : tu_width; + const int trafo_height = !ver_split_first ? (tu_height / 2) : tu_height; + + #define TRANSFORM_TREE(x, y) do { \ + ret = hls_transform_tree(lc, x, y, trafo_width, trafo_height, ch_type); \ + if (ret < 0) \ + return ret; \ + } while (0) + + TRANSFORM_TREE(x0, y0); + if (ver_split_first) + TRANSFORM_TREE(x0 + trafo_width, y0); + else + TRANSFORM_TREE(x0, y0 + trafo_height); + + } else { + ret = hls_transform_unit(lc, x0, y0, tu_width, tu_height, 0, ch_type); + if (ret < 0) + return ret; + + } + } else if (cu->sbt_flag) { + if (!cu->sbt_horizontal_flag) { + #define TRANSFORM_UNIT(x, width, idx) do { \ + ret = hls_transform_unit(lc, x, y0, width, tu_height, idx, ch_type); \ + if (ret < 0) \ + return ret; \ + } while (0) + + const int trafo_width = tu_width * lc->parse.sbt_num_fourths_tb0 / 4; + TRANSFORM_UNIT(x0, trafo_width, 0); + TRANSFORM_UNIT(x0 + trafo_width, tu_width - trafo_width, 1); + + #undef TRANSFORM_UNIT + } else { + #define TRANSFORM_UNIT(y, height, idx) do { \ + ret = hls_transform_unit(lc, x0, y, tu_width, height, idx, ch_type); \ + if (ret < 0) \ + return ret; \ + } while (0) + + const int trafo_height = tu_height * lc->parse.sbt_num_fourths_tb0 / 4; + TRANSFORM_UNIT(y0, trafo_height, 0); + TRANSFORM_UNIT(y0 + trafo_height, tu_height - trafo_height, 1); + + #undef TRANSFORM_UNIT + } + } else if (cu->isp_split_type == ISP_HOR_SPLIT) { + const int trafo_height = tu_height / cu->num_intra_subpartitions; + for (int i = 0; i < cu->num_intra_subpartitions; i++) { + ret = hls_transform_unit(lc, x0, y0 + trafo_height * i, tu_width, trafo_height, i, 0); + if (ret < 0) + return ret; + } + } else if (cu->isp_split_type == ISP_VER_SPLIT) { + const int trafo_width = tu_width / cu->num_intra_subpartitions; + for (int i = 0; i < cu->num_intra_subpartitions; i++) { + ret = hls_transform_unit(lc, x0 + trafo_width * i , y0, trafo_width, tu_height, i, 0); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static int skipped_transform_tree(VVCLocalContext *lc, int x0, int y0,int tu_width, int tu_height) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + + if (tu_width > sps->max_tb_size_y || tu_height > sps->max_tb_size_y) { + const int ver_split_first = tu_width > sps->max_tb_size_y && tu_width > tu_height; + const int trafo_width = ver_split_first ? (tu_width / 2) : tu_width; + const int trafo_height = !ver_split_first ? (tu_height / 2) : tu_height; + + #define SKIPPED_TRANSFORM_TREE(x, y) do { \ + int ret = skipped_transform_tree(lc, x, y, trafo_width, trafo_height); \ + if (ret < 0) \ + return ret; \ + } while (0) + + SKIPPED_TRANSFORM_TREE(x0, y0); + if (ver_split_first) + SKIPPED_TRANSFORM_TREE(x0 + trafo_width, y0); + else + SKIPPED_TRANSFORM_TREE(x0, y0 + trafo_height); + } else { + TransformUnit *tu = add_tu(fc, lc->cu, x0, y0, tu_width, tu_height); + const int c_end = sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : (LUMA + 1); + if (!tu) + return AVERROR_INVALIDDATA; + for (int i = LUMA; i < c_end; i++) { + TransformBlock *tb = add_tb(tu, lc, x0, y0, tu_width >> sps->hshift[i], tu_height >> sps->vshift[i], i); + if (i != CR) + set_tb_pos(fc, tb); + } + } + + return 0; +} + +//6.4.1 Allowed quad split process +//6.4.2 Allowed binary split process +//6.4.3 Allowed ternary split process +static void can_split(const VVCLocalContext *lc, int x0, int y0,int cb_width, int cb_height, + int mtt_depth, int depth_offset, int part_idx, VVCSplitMode last_split_mode, + VVCTreeType tree_type, VVCModeType mode_type, VVCAllowedSplit* split) +{ + int min_qt_size, max_bt_size, max_tt_size, max_mtt_depth; + const VVCFrameContext *fc = lc->fc; + const VVCSH *sh = &lc->sc->sh; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int chroma = tree_type == DUAL_TREE_CHROMA; + int min_cb_size_y = sps->min_cb_size_y; + int *qt = &split->qt; + int *btv = &split->btv; + int *bth = &split->bth; + int *ttv = &split->ttv; + int *tth = &split->tth; + + *qt = *bth = *btv = *tth = *ttv = 1; + + if (mtt_depth) + *qt = 0; + + min_qt_size = sh->min_qt_size[chroma]; + if (cb_width <= min_qt_size) + *qt = 0; + + if (chroma) { + int chroma_area = (cb_width >> sps->hshift[1]) * (cb_height >> sps->vshift[1]); + int chroma_width = cb_width >> sps->hshift[1]; + + if (chroma_width == 8) + *ttv = 0; + else if (chroma_width <= 4) { + if (chroma_width == 4) + *btv = 0; + *qt = 0; + } + if (mode_type == MODE_TYPE_INTRA) + *qt = *btv = *bth = *ttv = *tth = 0; + if (chroma_area <= 32) { + *ttv = *tth = 0; + if (chroma_area <= 16) + *btv = *bth = 0; + } + } + max_bt_size = sh->max_bt_size[chroma]; + max_tt_size = sh->max_tt_size[chroma]; + max_mtt_depth = sh->max_mtt_depth[chroma] + depth_offset; + + if (mode_type == MODE_TYPE_INTER) { + int area = cb_width * cb_height; + if (area == 32) + *btv = *bth = 0; + else if (area == 64) + *ttv = *tth = 0; + } + if (cb_width <= 2 * min_cb_size_y) { + *ttv = 0; + if (cb_width <= min_cb_size_y) + *btv = 0; + } + if (cb_height <= 2 * min_cb_size_y) { + *tth = 0; + if (cb_height <= min_cb_size_y) + *bth = 0; + } + if (cb_width > max_bt_size || cb_height > max_bt_size) + *btv = *bth = 0; + max_tt_size = FFMIN(64, max_tt_size); + if (cb_width > max_tt_size || cb_height > max_tt_size) + *ttv = *tth = 0; + if (mtt_depth >= max_mtt_depth) + *btv = *bth = *ttv = *tth = 0; + if (x0 + cb_width > pps->width) { + *ttv = *tth = 0; + if (cb_height > 64) + *btv = 0; + if (y0 + cb_height <= pps->height) + *bth = 0; + else if (cb_width > min_qt_size) + *btv = *bth = 0; + } + if (y0 + cb_height > pps->height) { + *btv = *ttv = *tth = 0; + if (cb_width > 64) + *bth = 0; + } + if (mtt_depth > 0 && part_idx == 1) { + if (last_split_mode == SPLIT_TT_VER) + *btv = 0; + else if (last_split_mode == SPLIT_TT_HOR) + *bth = 0; + } + if (cb_width <= 64 && cb_height > 64) + *btv = 0; + if (cb_width > 64 && cb_height <= 64) + *bth = 0; +} + +static int get_num_intra_subpartitions(enum IspType isp_split_type, int cb_width, int cb_height) +{ + if (isp_split_type == ISP_NO_SPLIT) + return 1; + if ((cb_width == 4 && cb_height == 8) || (cb_width == 8 && cb_height == 4)) + return 2; + return 4; +} + +static int get_cclm_enabled(const VVCLocalContext *lc, const int x0, const int y0) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + int enabled = 0; + + if (!sps->r->sps_cclm_enabled_flag) + return 0; + if (!sps->r->sps_qtbtt_dual_tree_intra_flag || !IS_I(lc->sc->sh.r) || sps->ctb_log2_size_y < 6) + return 1; + else { + const int x64 = x0 >> 6 << 6; + const int y64 = y0 >> 6 << 6; + const int y32 = y0 >> 5 << 5; + const int x64_cu = x64 >> fc->ps.sps->min_cb_log2_size_y; + const int y64_cu = y64 >> fc->ps.sps->min_cb_log2_size_y; + const int y32_cu = y32 >> fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int depth = SAMPLE_CTB(fc->tab.cqt_depth[1], x64_cu, y64_cu); + const int min_depth = fc->ps.sps->ctb_log2_size_y - 6; + const VVCSplitMode msm64 = (VVCSplitMode)TAB_MSM(fc, 0, x64, y64); + const VVCSplitMode msm32 = (VVCSplitMode)TAB_MSM(fc, 1, x64, y32); + + enabled = SAMPLE_CTB(fc->tab.cb_width[1], x64_cu, y64_cu) == 64 && + SAMPLE_CTB(fc->tab.cb_height[1], x64_cu, y64_cu) == 64; + enabled |= depth == min_depth && msm64 == SPLIT_BT_HOR && + SAMPLE_CTB(fc->tab.cb_width[1], x64_cu, y32_cu) == 64 && + SAMPLE_CTB(fc->tab.cb_height[1], x64_cu, y32_cu) == 32; + enabled |= depth > min_depth; + enabled |= depth == min_depth && msm64 == SPLIT_BT_HOR && msm32 == SPLIT_BT_VER; + + if (enabled) { + const int w = SAMPLE_CTB(fc->tab.cb_width[0], x64_cu, y64_cu); + const int h = SAMPLE_CTB(fc->tab.cb_height[0], x64_cu, y64_cu); + const int depth0 = SAMPLE_CTB(fc->tab.cqt_depth[0], x64_cu, y64_cu); + if ((w == 64 && h == 64 && TAB_ISPMF(fc, x64, y64)) || + ((w < 64 || h < 64) && depth0 == min_depth)) + return 0; + } + + } + + return enabled; +} + +static int less(const void *a, const void *b) +{ + return *(const int*)a - *(const int*)b; +} + +//8.4.2 Derivation process for luma intra prediction mode +static enum IntraPredMode luma_intra_pred_mode(VVCLocalContext* lc, const int intra_subpartitions_mode_flag) +{ + VVCFrameContext *fc = lc->fc; + CodingUnit *cu = lc->cu; + const int x0 = cu->x0; + const int y0 = cu->y0; + enum IntraPredMode pred; + int intra_luma_not_planar_flag = 1; + int intra_luma_mpm_remainder = 0; + int intra_luma_mpm_flag = 1; + int intra_luma_mpm_idx = 0; + + if (!cu->intra_luma_ref_idx) + intra_luma_mpm_flag = ff_vvc_intra_luma_mpm_flag(lc); + if (intra_luma_mpm_flag) { + if (!cu->intra_luma_ref_idx) + intra_luma_not_planar_flag = ff_vvc_intra_luma_not_planar_flag(lc, intra_subpartitions_mode_flag); + if (intra_luma_not_planar_flag) + intra_luma_mpm_idx = ff_vvc_intra_luma_mpm_idx(lc); + } else { + intra_luma_mpm_remainder = ff_vvc_intra_luma_mpm_remainder(lc); + } + + if (!intra_luma_not_planar_flag) { + pred = INTRA_PLANAR; + } else { + const VVCSPS *sps = fc->ps.sps; + const int x_a = (x0 - 1) >> sps->min_cb_log2_size_y; + const int y_a = (y0 + cu->cb_height - 1) >> sps->min_cb_log2_size_y; + const int x_b = (x0 + cu->cb_width - 1) >> sps->min_cb_log2_size_y; + const int y_b = (y0 - 1) >> sps->min_cb_log2_size_y; + int min_cb_width = fc->ps.pps->min_cb_width; + int x0b = av_mod_uintp2(x0, sps->ctb_log2_size_y); + int y0b = av_mod_uintp2(y0, sps->ctb_log2_size_y); + const int available_l = lc->ctb_left_flag || x0b; + const int available_u = lc->ctb_up_flag || y0b; + + int a, b, cand[5]; + + if (!available_l || (SAMPLE_CTB(fc->tab.cpm[0], x_a, y_a) != MODE_INTRA) || + SAMPLE_CTB(fc->tab.imf, x_a, y_a)) { + a = INTRA_PLANAR; + } else { + a = SAMPLE_CTB(fc->tab.ipm, x_a, y_a); + } + + if (!available_u || (SAMPLE_CTB(fc->tab.cpm[0], x_b, y_b) != MODE_INTRA) || + SAMPLE_CTB(fc->tab.imf, x_b, y_b) || !y0b) { + b = INTRA_PLANAR; + } else { + b = SAMPLE_CTB(fc->tab.ipm, x_b, y_b); + } + + if (a == b && a > INTRA_DC) { + cand[0] = a; + cand[1] = 2 + ((a + 61) % 64); + cand[2] = 2 + ((a - 1) % 64); + cand[3] = 2 + ((a + 60) % 64); + cand[4] = 2 + (a % 64); + } else { + const int minab = FFMIN(a, b); + const int maxab = FFMAX(a, b); + if (a > INTRA_DC && b > INTRA_DC) { + const int diff = maxab - minab; + cand[0] = a; + cand[1] = b; + if (diff == 1) { + cand[2] = 2 + ((minab + 61) % 64); + cand[3] = 2 + ((maxab - 1) % 64); + cand[4] = 2 + ((minab + 60) % 64); + } else if (diff >= 62) { + cand[2] = 2 + ((minab - 1) % 64); + cand[3] = 2 + ((maxab + 61) % 64); + cand[4] = 2 + (minab % 64); + } else if (diff == 2) { + cand[2] = 2 + ((minab - 1) % 64); + cand[3] = 2 + ((minab + 61) % 64); + cand[4] = 2 + ((maxab - 1) % 64); + } else { + cand[2] = 2 + ((minab + 61) % 64); + cand[3] = 2 + ((minab - 1) % 64); + cand[4] = 2 + ((maxab + 61) % 64); + } + } else if (a > INTRA_DC || b > INTRA_DC) { + cand[0] = maxab; + cand[1] = 2 + ((maxab + 61 ) % 64); + cand[2] = 2 + ((maxab - 1) % 64); + cand[3] = 2 + ((maxab + 60 ) % 64); + cand[4] = 2 + (maxab % 64); + } else { + cand[0] = INTRA_DC; + cand[1] = INTRA_VERT; + cand[2] = INTRA_HORZ; + cand[3] = INTRA_VERT - 4; + cand[4] = INTRA_VERT + 4; + } + } + if (intra_luma_mpm_flag) { + pred = cand[intra_luma_mpm_idx]; + } else { + qsort(cand, FF_ARRAY_ELEMS(cand), sizeof(cand[0]), less); + pred = intra_luma_mpm_remainder + 1; + for (int i = 0; i < FF_ARRAY_ELEMS(cand); i++) { + if (pred >= cand[i]) + pred++; + } + } + } + return pred; +} + +static int lfnst_idx_decode(VVCLocalContext *lc) +{ + CodingUnit *cu = lc->cu; + const VVCTreeType tree_type = cu->tree_type; + const VVCSPS *sps = lc->fc->ps.sps; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + const TransformUnit *tu = cu->tus.head; + int lfnst_width, lfnst_height, min_lfnst; + int lfnst_idx = 0; + + memset(cu->apply_lfnst_flag, 0, sizeof(cu->apply_lfnst_flag)); + + if (!sps->r->sps_lfnst_enabled_flag || cu->pred_mode != MODE_INTRA || FFMAX(cb_width, cb_height) > sps->max_tb_size_y) + return 0; + + while (tu) { + for (int j = 0; j < tu->nb_tbs; j++) { + const TransformBlock *tb = tu->tbs + j; + if (tu->coded_flag[tb->c_idx] && tb->ts) + return 0; + } + tu = tu->next; + } + + if (tree_type == DUAL_TREE_CHROMA) { + lfnst_width = cb_width >> sps->hshift[1]; + lfnst_height = cb_height >> sps->vshift[1]; + } else { + const int vs = cu->isp_split_type == ISP_VER_SPLIT; + const int hs = cu->isp_split_type == ISP_HOR_SPLIT; + lfnst_width = vs ? cb_width / cu->num_intra_subpartitions : cb_width; + lfnst_height = hs ? cb_height / cu->num_intra_subpartitions : cb_height; + } + min_lfnst = FFMIN(lfnst_width, lfnst_height); + if (tree_type != DUAL_TREE_CHROMA && cu->intra_mip_flag && min_lfnst < 16) + return 0; + + if (min_lfnst >= 4) { + if ((cu->isp_split_type != ISP_NO_SPLIT || !lc->parse.lfnst_dc_only) && lc->parse.lfnst_zero_out_sig_coeff_flag) + lfnst_idx = ff_vvc_lfnst_idx(lc, tree_type != SINGLE_TREE); + } + + if (lfnst_idx) { + cu->apply_lfnst_flag[LUMA] = tree_type != DUAL_TREE_CHROMA; + cu->apply_lfnst_flag[CB] = cu->apply_lfnst_flag[CR] = tree_type == DUAL_TREE_CHROMA; + } + + return lfnst_idx; +} + +static MtsIdx mts_idx_decode(VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + const VVCSPS *sps = lc->fc->ps.sps; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + const uint8_t transform_skip_flag = cu->tus.head->tbs[0].ts; //fix me + int mts_idx = MTS_DCT2_DCT2; + if (cu->tree_type != DUAL_TREE_CHROMA && !cu->lfnst_idx && + !transform_skip_flag && FFMAX(cb_width, cb_height) <= 32 && + cu->isp_split_type == ISP_NO_SPLIT && !cu->sbt_flag && + lc->parse.mts_zero_out_sig_coeff_flag && !lc->parse.mts_dc_only) { + if ((cu->pred_mode == MODE_INTER && sps->r->sps_explicit_mts_inter_enabled_flag) || + (cu->pred_mode == MODE_INTRA && sps->r->sps_explicit_mts_intra_enabled_flag)) { + mts_idx = ff_vvc_mts_idx(lc); + } + } + + return mts_idx; +} + +static enum IntraPredMode derive_center_luma_intra_pred_mode(const VVCFrameContext *fc, const VVCSPS *sps, const VVCPPS *pps, const CodingUnit *cu) +{ + const int x_center = (cu->x0 + cu->cb_width / 2) >> sps->min_cb_log2_size_y; + const int y_center = (cu->y0 + cu->cb_height / 2) >> sps->min_cb_log2_size_y; + const int min_cb_width = pps->min_cb_width; + const int intra_mip_flag = SAMPLE_CTB(fc->tab.imf, x_center, y_center); + const int cu_pred_mode = SAMPLE_CTB(fc->tab.cpm[0], x_center, y_center); + const int intra_pred_mode_y = SAMPLE_CTB(fc->tab.ipm, x_center, y_center); + + if (intra_mip_flag) { + if (cu->tree_type == SINGLE_TREE && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_444) + return INTRA_INVALID; + return INTRA_PLANAR; + } + if (cu_pred_mode == MODE_IBC || cu_pred_mode == MODE_PLT) + return INTRA_DC; + return intra_pred_mode_y; +} + +static void derive_chroma_intra_pred_mode(VVCLocalContext *lc, + const int cclm_mode_flag, const int cclm_mode_idx, const int intra_chroma_pred_mode) +{ + const VVCFrameContext *fc = lc->fc; + CodingUnit *cu = lc->cu; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int x_cb = cu->x0 >> sps->min_cb_log2_size_y; + const int y_cb = cu->y0 >> sps->min_cb_log2_size_y; + const int min_cb_width = pps->min_cb_width; + const int intra_mip_flag = SAMPLE_CTB(fc->tab.imf, x_cb, y_cb); + enum IntraPredMode luma_intra_pred_mode = SAMPLE_CTB(fc->tab.ipm, x_cb, y_cb); + + if (cu->tree_type == SINGLE_TREE && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_444 && + intra_chroma_pred_mode == 4 && intra_mip_flag) { + cu->mip_chroma_direct_flag = 1; + cu->intra_pred_mode_c = luma_intra_pred_mode; + return; + } + luma_intra_pred_mode = derive_center_luma_intra_pred_mode(fc, sps, pps, cu); + + if (cu->act_enabled_flag) { + cu->intra_pred_mode_c = luma_intra_pred_mode; + return; + } + if (cclm_mode_flag) { + cu->intra_pred_mode_c = INTRA_LT_CCLM + cclm_mode_idx; + } else if (intra_chroma_pred_mode == 4){ + cu->intra_pred_mode_c = luma_intra_pred_mode; + } else { + const static IntraPredMode pred_mode_c[][4 + 1] = { + {INTRA_VDIAG, INTRA_PLANAR, INTRA_PLANAR, INTRA_PLANAR, INTRA_PLANAR}, + {INTRA_VERT, INTRA_VDIAG, INTRA_VERT, INTRA_VERT, INTRA_VERT}, + {INTRA_HORZ, INTRA_HORZ, INTRA_VDIAG, INTRA_HORZ, INTRA_HORZ}, + {INTRA_DC, INTRA_DC, INTRA_DC, INTRA_VDIAG, INTRA_DC}, + }; + const int modes[4] = {INTRA_PLANAR, INTRA_VERT, INTRA_HORZ, INTRA_DC}; + int idx; + + // This workaround is necessary to have 4:4:4 video decode correctly + // See VVC ticket https://jvet.hhi.fraunhofer.de/trac/vvc/ticket/1602 + // and VTM source https://vcgit.hhi.fraunhofer.de/jvet/VVCSoftware_VTM/-/blob/master/source/Lib/CommonLib/UnitTools.cpp#L736 + if (cu->tree_type == SINGLE_TREE && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_444 && intra_mip_flag) { + idx = 4; + } else { + for (idx = 0; idx < FF_ARRAY_ELEMS(modes); idx++) { + if (modes[idx] == luma_intra_pred_mode) + break; + } + } + + cu->intra_pred_mode_c = pred_mode_c[intra_chroma_pred_mode][idx]; + } + if (sps->r->sps_chroma_format_idc == CHROMA_FORMAT_422 && cu->intra_pred_mode_c <= INTRA_VDIAG) { + const static int mode_map_422[INTRA_VDIAG + 1] = { + 0, 1, 61, 62, 63, 64, 65, 66, 2, 3, 5, 6, 8, 10, 12, 13, + 14, 16, 18, 20, 22, 23, 24, 26, 28, 30, 31, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 41, 42, 43, 43, 44, 44, 45, 45, 46, 47, 48, 48, + 49, 49, 50, 51, 51, 52, 52, 53, 54, 55, 55, 56, 56, 57, 57, 58, + 59, 59, 60, + }; + cu->intra_pred_mode_c = mode_map_422[cu->intra_pred_mode_c]; + } +} + +static void intra_luma_pred_modes(VVCLocalContext *lc) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + CodingUnit *cu = lc->cu; + const int log2_min_cb_size = sps->min_cb_log2_size_y; + const int x0 = cu->x0; + const int y0 = cu->y0; + const int x_cb = x0 >> log2_min_cb_size; + const int y_cb = y0 >> log2_min_cb_size; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + + cu->intra_luma_ref_idx = 0; + if (sps->r->sps_bdpcm_enabled_flag && cb_width <= sps->max_ts_size && cb_height <= sps->max_ts_size) + cu->bdpcm_flag[LUMA] = ff_vvc_intra_bdpcm_luma_flag(lc); + if (cu->bdpcm_flag[LUMA]) { + cu->intra_pred_mode_y = ff_vvc_intra_bdpcm_luma_dir_flag(lc) ? INTRA_VERT : INTRA_HORZ; + } else { + if (sps->r->sps_mip_enabled_flag) + cu->intra_mip_flag = ff_vvc_intra_mip_flag(lc, fc->tab.imf); + if (cu->intra_mip_flag) { + int intra_mip_transposed_flag = ff_vvc_intra_mip_transposed_flag(lc); + int intra_mip_mode = ff_vvc_intra_mip_mode(lc); + int x = y_cb * pps->min_cb_width + x_cb; + for (int y = 0; y < (cb_height>>log2_min_cb_size); y++) { + int width = cb_width>>log2_min_cb_size; + memset(&fc->tab.imf[x], cu->intra_mip_flag, width); + fc->tab.imtf[x] = intra_mip_transposed_flag; + fc->tab.imm[x] = intra_mip_mode; + x += pps->min_cb_width; + } + cu->intra_pred_mode_y = intra_mip_mode; + } else { + int intra_subpartitions_mode_flag = 0; + if (sps->r->sps_mrl_enabled_flag && ((y0 % sps->ctb_size_y) > 0)) + cu->intra_luma_ref_idx = ff_vvc_intra_luma_ref_idx(lc); + if (sps->r->sps_isp_enabled_flag && !cu->intra_luma_ref_idx && + (cb_width <= sps->max_tb_size_y && cb_height <= sps->max_tb_size_y) && + (cb_width * cb_height > MIN_TU_SIZE * MIN_TU_SIZE) && + !cu->act_enabled_flag) + intra_subpartitions_mode_flag = ff_vvc_intra_subpartitions_mode_flag(lc); + if (!(x0 & 63) && !(y0 & 63)) + TAB_ISPMF(fc, x0, y0) = intra_subpartitions_mode_flag; + cu->isp_split_type = ff_vvc_isp_split_type(lc, intra_subpartitions_mode_flag); + cu->num_intra_subpartitions = get_num_intra_subpartitions(cu->isp_split_type, cb_width, cb_height); + cu->intra_pred_mode_y = luma_intra_pred_mode(lc, intra_subpartitions_mode_flag); + } + } + set_cb_tab(lc, fc->tab.ipm, cu->intra_pred_mode_y); +} + +static void intra_chroma_pred_modes(VVCLocalContext *lc) +{ + const VVCSPS *sps = lc->fc->ps.sps; + CodingUnit *cu = lc->cu; + const int hs = sps->hshift[CHROMA]; + const int vs = sps->vshift[CHROMA]; + + cu->mip_chroma_direct_flag = 0; + if (sps->r->sps_bdpcm_enabled_flag && + (cu->cb_width >> hs) <= sps->max_ts_size && + (cu->cb_height >> vs) <= sps->max_ts_size) { + cu->bdpcm_flag[CB] = cu->bdpcm_flag[CR] = ff_vvc_intra_bdpcm_chroma_flag(lc); + } + if (cu->bdpcm_flag[CHROMA]) { + cu->intra_pred_mode_c = ff_vvc_intra_bdpcm_chroma_dir_flag(lc) ? INTRA_VERT : INTRA_HORZ; + } else { + const int cclm_enabled = get_cclm_enabled(lc, cu->x0, cu->y0); + int cclm_mode_flag = 0; + int cclm_mode_idx = 0; + int intra_chroma_pred_mode = 0; + + if (cclm_enabled) + cclm_mode_flag = ff_vvc_cclm_mode_flag(lc); + + if (cclm_mode_flag) + cclm_mode_idx = ff_vvc_cclm_mode_idx(lc); + else + intra_chroma_pred_mode = ff_vvc_intra_chroma_pred_mode(lc); + derive_chroma_intra_pred_mode(lc, cclm_mode_flag, cclm_mode_idx, intra_chroma_pred_mode); + } +} + +static PredMode pred_mode_decode(VVCLocalContext *lc, + const VVCTreeType tree_type, + const VVCModeType mode_type) +{ + const VVCFrameContext *fc = lc->fc; + CodingUnit *cu = lc->cu; + const VVCSPS *sps = fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int ch_type = tree_type == DUAL_TREE_CHROMA ? 1 : 0; + const int is_4x4 = cu->cb_width == 4 && cu->cb_height == 4; + int pred_mode_flag; + int pred_mode_ibc_flag; + PredMode pred_mode; + + cu->skip_flag = 0; + if (!IS_I(rsh) || sps->r->sps_ibc_enabled_flag) { + const int is_128 = cu->cb_width == 128 || cu->cb_height == 128; + if (tree_type != DUAL_TREE_CHROMA && + ((!is_4x4 && mode_type != MODE_TYPE_INTRA) || + (sps->r->sps_ibc_enabled_flag && !is_128))) { + cu->skip_flag = ff_vvc_cu_skip_flag(lc, fc->tab.skip); + } + + if (is_4x4 || mode_type == MODE_TYPE_INTRA || IS_I(rsh)) { + pred_mode_flag = 1; + } else if (mode_type == MODE_TYPE_INTER || cu->skip_flag) { + pred_mode_flag = 0; + } else { + pred_mode_flag = ff_vvc_pred_mode_flag(lc, ch_type); + } + pred_mode = pred_mode_flag ? MODE_INTRA : MODE_INTER; + + if (((IS_I(rsh) && !cu->skip_flag) || + (!IS_I(rsh) && (pred_mode != MODE_INTRA || + ((is_4x4 || mode_type == MODE_TYPE_INTRA) && !cu->skip_flag)))) && + !is_128 && mode_type != MODE_TYPE_INTER && sps->r->sps_ibc_enabled_flag && + tree_type != DUAL_TREE_CHROMA) { + pred_mode_ibc_flag = ff_vvc_pred_mode_ibc_flag(lc, ch_type); + } else if (cu->skip_flag && (is_4x4 || mode_type == MODE_TYPE_INTRA)) { + pred_mode_ibc_flag = 1; + } else if (is_128 || mode_type == MODE_TYPE_INTER || tree_type == DUAL_TREE_CHROMA) { + pred_mode_ibc_flag = 0; + } else { + pred_mode_ibc_flag = (IS_I(rsh)) ? sps->r->sps_ibc_enabled_flag : 0; + } + if (pred_mode_ibc_flag) + pred_mode = MODE_IBC; + } else { + pred_mode_flag = is_4x4 || mode_type == MODE_TYPE_INTRA || + mode_type != MODE_TYPE_INTER || IS_I(rsh); + pred_mode = pred_mode_flag ? MODE_INTRA : MODE_INTER; + } + return pred_mode; +} + +static void sbt_info(VVCLocalContext *lc, const VVCSPS *sps) +{ + CodingUnit *cu = lc->cu; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + + if (cu->pred_mode == MODE_INTER && sps->r->sps_sbt_enabled_flag && !cu->ciip_flag + && cb_width <= sps->max_tb_size_y && cb_height <= sps->max_tb_size_y) { + const int sbt_ver_h = cb_width >= 8; + const int sbt_hor_h = cb_height >= 8; + cu->sbt_flag = 0; + if (sbt_ver_h || sbt_hor_h) + cu->sbt_flag = ff_vvc_sbt_flag(lc); + if (cu->sbt_flag) { + const int sbt_ver_q = cb_width >= 16; + const int sbt_hor_q = cb_height >= 16; + int cu_sbt_quad_flag = 0; + + if ((sbt_ver_h || sbt_hor_h) && (sbt_ver_q || sbt_hor_q)) + cu_sbt_quad_flag = ff_vvc_sbt_quad_flag(lc); + if (cu_sbt_quad_flag) { + cu->sbt_horizontal_flag = sbt_hor_q; + if (sbt_ver_q && sbt_hor_q) + cu->sbt_horizontal_flag = ff_vvc_sbt_horizontal_flag(lc); + } else { + cu->sbt_horizontal_flag = sbt_hor_h; + if (sbt_ver_h && sbt_hor_h) + cu->sbt_horizontal_flag = ff_vvc_sbt_horizontal_flag(lc); + } + cu->sbt_pos_flag = ff_vvc_sbt_pos_flag(lc); + + { + const int sbt_min = cu_sbt_quad_flag ? 1 : 2; + lc->parse.sbt_num_fourths_tb0 = cu->sbt_pos_flag ? (4 - sbt_min) : sbt_min; + } + } + } +} + +static int skipped_transform_tree_unit(VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + int ret; + + set_qp_y(lc, cu->x0, cu->y0, 0); + set_qp_c(lc); + ret = skipped_transform_tree(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + if (ret < 0) + return ret; + return 0; +} + +static void set_cb_pos(const VVCFrameContext *fc, const CodingUnit *cu) +{ + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int log2_min_cb_size = sps->min_cb_log2_size_y; + const int x_cb = cu->x0 >> log2_min_cb_size; + const int y_cb = cu->y0 >> log2_min_cb_size; + const int ch_type = cu->ch_type; + int x, y; + + x = y_cb * pps->min_cb_width + x_cb; + for (y = 0; y < (cu->cb_height >> log2_min_cb_size); y++) { + const int width = cu->cb_width >> log2_min_cb_size; + + for (int i = 0; i < width; i++) { + fc->tab.cb_pos_x[ch_type][x + i] = cu->x0; + fc->tab.cb_pos_y[ch_type][x + i] = cu->y0; + } + memset(&fc->tab.cb_width[ch_type][x], cu->cb_width, width); + memset(&fc->tab.cb_height[ch_type][x], cu->cb_height, width); + memset(&fc->tab.cqt_depth[ch_type][x], cu->cqt_depth, width); + + x += pps->min_cb_width; + } +} + +static CodingUnit* alloc_cu(VVCLocalContext *lc, const int x0, const int y0) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int rx = x0 >> sps->ctb_log2_size_y; + const int ry = y0 >> sps->ctb_log2_size_y; + CTU *ctu = fc->tab.ctus + ry * pps->ctb_width + rx; + CodingUnit *cu = ff_refstruct_pool_get(fc->cu_pool); + + if (!cu) + return NULL; + cu->next = NULL; + + if (lc->cu) + lc->cu->next = cu; + else + ctu->cus = cu; + lc->cu = cu; + + return cu; +} + +static CodingUnit* add_cu(VVCLocalContext *lc, const int x0, const int y0, + const int cb_width, const int cb_height, const int cqt_depth, const VVCTreeType tree_type) +{ + VVCFrameContext *fc = lc->fc; + const int ch_type = tree_type == DUAL_TREE_CHROMA ? 1 : 0; + CodingUnit *cu = alloc_cu(lc, x0, y0); + + if (!cu) + return NULL; + + memset(&cu->pu, 0, sizeof(cu->pu)); + + lc->parse.prev_tu_cbf_y = 0; + + cu->sbt_flag = 0; + cu->act_enabled_flag = 0; + + cu->tree_type = tree_type; + cu->x0 = x0; + cu->y0 = y0; + cu->cb_width = cb_width; + cu->cb_height = cb_height; + cu->ch_type = ch_type; + cu->cqt_depth = cqt_depth; + cu->tus.head = cu->tus.tail = NULL; + cu->bdpcm_flag[LUMA] = cu->bdpcm_flag[CB] = cu->bdpcm_flag[CR] = 0; + cu->isp_split_type = ISP_NO_SPLIT; + cu->intra_mip_flag = 0; + cu->ciip_flag = 0; + cu->coded_flag = 1; + cu->num_intra_subpartitions = 1; + + set_cb_pos(fc, cu); + return cu; +} + +static void set_cu_tabs(const VVCLocalContext *lc, const CodingUnit *cu) +{ + const VVCFrameContext *fc = lc->fc; + const TransformUnit *tu = cu->tus.head; + + set_cb_tab(lc, fc->tab.cpm[cu->ch_type], cu->pred_mode); + if (cu->tree_type != DUAL_TREE_CHROMA) + set_cb_tab(lc, fc->tab.skip, cu->skip_flag); + + while (tu) { + for (int j = 0; j < tu->nb_tbs; j++) { + const TransformBlock *tb = tu->tbs + j; + if (tb->c_idx != LUMA) + set_qp_c_tab(lc, tu, tb); + if (tb->c_idx != CR && cu->bdpcm_flag[tb->c_idx]) + set_tb_tab(fc->tab.pcmf[tb->c_idx], 1, fc, tb); + } + tu = tu->next; + } +} + +//8.5.2.7 Derivation process for merge motion vector difference +static void derive_mmvd(const VVCLocalContext *lc, MvField *mvf, const Mv *mmvd_offset) +{ + const SliceContext *sc = lc->sc; + Mv mmvd[2]; + + if (mvf->pred_flag == PF_BI) { + const RefPicList *rpl = sc->rpl; + const int poc = lc->fc->ps.ph.poc; + const int diff[] = { + poc - rpl[0].list[mvf->ref_idx[0]], + poc - rpl[1].list[mvf->ref_idx[1]] + }; + const int sign = FFSIGN(diff[0]) != FFSIGN(diff[1]); + + if (diff[0] == diff[1]) { + mmvd[1] = mmvd[0] = *mmvd_offset; + } + else { + const int i = FFABS(diff[0]) < FFABS(diff[1]); + const int o = !i; + mmvd[i] = *mmvd_offset; + if (!rpl[0].isLongTerm[mvf->ref_idx[0]] && !rpl[1].isLongTerm[mvf->ref_idx[1]]) { + ff_vvc_mv_scale(&mmvd[o], mmvd_offset, diff[i], diff[o]); + } + else { + mmvd[o].x = sign ? -mmvd[i].x : mmvd[i].x; + mmvd[o].y = sign ? -mmvd[i].y : mmvd[i].y; + } + } + mvf->mv[0].x += mmvd[0].x; + mvf->mv[0].y += mmvd[0].y; + mvf->mv[1].x += mmvd[1].x; + mvf->mv[1].y += mmvd[1].y; + } else { + const int idx = mvf->pred_flag - PF_L0; + mvf->mv[idx].x += mmvd_offset->x; + mvf->mv[idx].y += mmvd_offset->y; + } + +} + +static void mvf_to_mi(const MvField *mvf, MotionInfo *mi) +{ + mi->pred_flag = mvf->pred_flag; + mi->bcw_idx = mvf->bcw_idx; + mi->hpel_if_idx = mvf->hpel_if_idx; + for (int i = 0; i < 2; i++) { + const PredFlag mask = i + 1; + if (mvf->pred_flag & mask) { + mi->mv[i][0] = mvf->mv[i]; + mi->ref_idx[i] = mvf->ref_idx[i]; + } + } +} + +static void mv_merge_refine_pred_flag(MvField *mvf, const int width, const int height) +{ + if (mvf->pred_flag == PF_BI && (width + height) == 12) { + mvf->pred_flag = PF_L0; + mvf->bcw_idx = 0; + } +} + +// subblock-based inter prediction data +static void merge_data_subblock(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPH *ph = &fc->ps.ph; + CodingUnit* cu = lc->cu; + PredictionUnit *pu = &cu->pu; + int merge_subblock_idx = 0; + + set_cb_tab(lc, fc->tab.msf, pu->merge_subblock_flag); + if (ph->max_num_subblock_merge_cand > 1) { + merge_subblock_idx = ff_vvc_merge_subblock_idx(lc, ph->max_num_subblock_merge_cand); + } + ff_vvc_sb_mv_merge_mode(lc, merge_subblock_idx, pu); +} + +static void merge_data_regular(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPH *ph = &fc->ps.ph; + const CodingUnit* cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + int merge_idx = 0; + Mv mmvd_offset; + MvField mvf; + + if (sps->r->sps_mmvd_enabled_flag) + pu->mmvd_merge_flag = ff_vvc_mmvd_merge_flag(lc); + if (pu->mmvd_merge_flag) { + int mmvd_cand_flag = 0; + if (sps->max_num_merge_cand > 1) + mmvd_cand_flag = ff_vvc_mmvd_cand_flag(lc); + ff_vvc_mmvd_offset_coding(lc, &mmvd_offset, ph->r->ph_mmvd_fullpel_only_flag); + merge_idx = mmvd_cand_flag; + } else if (sps->max_num_merge_cand > 1) { + merge_idx = ff_vvc_merge_idx(lc); + } + ff_vvc_luma_mv_merge_mode(lc, merge_idx, 0, &mvf); + if (pu->mmvd_merge_flag) + derive_mmvd(lc, &mvf, &mmvd_offset); + mv_merge_refine_pred_flag(&mvf, cu->cb_width, cu->cb_height); + ff_vvc_store_mvf(lc, &mvf); + mvf_to_mi(&mvf, &pu->mi); +} + +static int ciip_flag_decode(VVCLocalContext *lc, const int ciip_avaiable, const int gpm_avaiable, const int is_128) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const CodingUnit *cu = lc->cu; + + if (ciip_avaiable && gpm_avaiable) + return ff_vvc_ciip_flag(lc); + return sps->r->sps_ciip_enabled_flag && !cu->skip_flag && + !is_128 && (cu->cb_width * cu->cb_height >= 64); +} + +static void merge_data_gpm(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + PredictionUnit *pu = &lc->cu->pu; + int merge_gpm_idx[2]; + + pu->merge_gpm_flag = 1; + pu->gpm_partition_idx = ff_vvc_merge_gpm_partition_idx(lc); + merge_gpm_idx[0] = ff_vvc_merge_gpm_idx(lc, 0); + merge_gpm_idx[1] = 0; + if (sps->max_num_gpm_merge_cand > 2) + merge_gpm_idx[1] = ff_vvc_merge_gpm_idx(lc, 1); + + ff_vvc_luma_mv_merge_gpm(lc, merge_gpm_idx, pu->gpm_mv); + ff_vvc_store_gpm_mvf(lc, pu); +} + +static void merge_data_ciip(VVCLocalContext *lc) +{ + const VVCFrameContext* fc = lc->fc; + const VVCSPS* sps = fc->ps.sps; + CodingUnit *cu = lc->cu; + MotionInfo *mi = &cu->pu.mi; + int merge_idx = 0; + MvField mvf; + + if (sps->max_num_merge_cand > 1) + merge_idx = ff_vvc_merge_idx(lc); + ff_vvc_luma_mv_merge_mode(lc, merge_idx, 1, &mvf); + mv_merge_refine_pred_flag(&mvf, cu->cb_width, cu->cb_height); + ff_vvc_store_mvf(lc, &mvf); + mvf_to_mi(&mvf, mi); + cu->intra_pred_mode_y = cu->intra_pred_mode_c = INTRA_PLANAR; + cu->intra_luma_ref_idx = 0; + cu->intra_mip_flag = 0; +} + +// block-based inter prediction data +static void merge_data_block(VVCLocalContext *lc) +{ + const VVCFrameContext* fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + CodingUnit *cu = lc->cu; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + const int is_128 = cb_width == 128 || cb_height == 128; + const int ciip_avaiable = sps->r->sps_ciip_enabled_flag && + !cu->skip_flag && (cb_width * cb_height >= 64); + const int gpm_avaiable = sps->r->sps_gpm_enabled_flag && IS_B(rsh) && + (cb_width >= 8) && (cb_height >=8) && + (cb_width < 8 * cb_height) && (cb_height < 8 *cb_width); + + int regular_merge_flag = 1; + + if (!is_128 && (ciip_avaiable || gpm_avaiable)) + regular_merge_flag = ff_vvc_regular_merge_flag(lc, cu->skip_flag); + if (regular_merge_flag) { + merge_data_regular(lc); + } else { + cu->ciip_flag = ciip_flag_decode(lc, ciip_avaiable, gpm_avaiable, is_128); + if (cu->ciip_flag) + merge_data_ciip(lc); + else + merge_data_gpm(lc); + } +} + +static int hls_merge_data(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPH *ph = &fc->ps.ph; + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + + pu->merge_gpm_flag = 0; + pu->mi.num_sb_x = pu->mi.num_sb_y = 1; + if (cu->pred_mode == MODE_IBC) { + avpriv_report_missing_feature(lc->fc->avctx, "Intra Block Copy"); + return AVERROR_PATCHWELCOME; + } else { + if (ph->max_num_subblock_merge_cand > 0 && cu->cb_width >= 8 && cu->cb_height >= 8) + pu->merge_subblock_flag = ff_vvc_merge_subblock_flag(lc); + if (pu->merge_subblock_flag) + merge_data_subblock(lc); + else + merge_data_block(lc); + } + return 0; +} + +static void hls_mvd_coding(VVCLocalContext *lc, Mv* mvd) +{ + int16_t mv[2]; + int i; + + for (i = 0; i < 2; i++) { + mv[i] = ff_vvc_abs_mvd_greater0_flag(lc); + } + for (i = 0; i < 2; i++) { + if (mv[i]) + mv[i] += ff_vvc_abs_mvd_greater1_flag(lc); + } + for (i = 0; i < 2; i++) { + if (mv[i] > 0) { + if (mv[i] == 2) + mv[i] += ff_vvc_abs_mvd_minus2(lc); + mv[i] = (1 - 2 * ff_vvc_mvd_sign_flag(lc)) * mv[i]; + } + } + mvd->x = mv[0]; + mvd->y = mv[1]; +} + +static int bcw_idx_decode(VVCLocalContext *lc, const MotionInfo *mi, const int cb_width, const int cb_height) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const VVCPH *ph = &fc->ps.ph; + const VVCSH *sh = &lc->sc->sh; + const PredWeightTable *w = pps->r->pps_wp_info_in_ph_flag ? &ph->pwt : &sh->pwt; + int bcw_idx = 0; + + if (sps->r->sps_bcw_enabled_flag && mi->pred_flag == PF_BI && + !w->weight_flag[L0][LUMA][mi->ref_idx[0]] && + !w->weight_flag[L1][LUMA][mi->ref_idx[1]] && + !w->weight_flag[L0][CHROMA][mi->ref_idx[0]] && + !w->weight_flag[L1][CHROMA][mi->ref_idx[1]] && + cb_width * cb_height >= 256) { + bcw_idx = ff_vvc_bcw_idx(lc, ff_vvc_no_backward_pred_flag(lc)); + } + return bcw_idx; +} + +static int8_t ref_idx_decode(VVCLocalContext *lc, const VVCSH *sh, const int sym_mvd_flag, const int lx) +{ + const H266RawSliceHeader *rsh = sh->r; + int ref_idx = 0; + + if (rsh->num_ref_idx_active[lx] > 1 && !sym_mvd_flag) + ref_idx = ff_vvc_ref_idx_lx(lc, rsh->num_ref_idx_active[lx]); + else if (sym_mvd_flag) + ref_idx = sh->ref_idx_sym[lx]; + return ref_idx; +} + +static int mvds_decode(VVCLocalContext *lc, Mv mvds[2][MAX_CONTROL_POINTS], + const int num_cp_mv, const int lx) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPH *ph = &fc->ps.ph; + const PredictionUnit *pu = &lc->cu->pu; + const MotionInfo *mi = &pu->mi; + int has_no_zero_mvd = 0; + + if (lx == L1 && ph->r->ph_mvd_l1_zero_flag && mi->pred_flag == PF_BI) { + for (int j = 0; j < num_cp_mv; j++) + AV_ZERO64(&mvds[lx][j]); + } else { + Mv *mvd0 = &mvds[lx][0]; + if (lx == L1 && pu->sym_mvd_flag) { + mvd0->x = -mvds[L0][0].x; + mvd0->y = -mvds[L0][0].y; + } else { + hls_mvd_coding(lc, mvd0); + } + has_no_zero_mvd |= (mvd0->x || mvd0->y); + for (int j = 1; j < num_cp_mv; j++) { + Mv *mvd = &mvds[lx][j]; + hls_mvd_coding(lc, mvd); + mvd->x += mvd0->x; + mvd->y += mvd0->y; + has_no_zero_mvd |= (mvd->x || mvd->y); + } + } + return has_no_zero_mvd; +} + +static void mvp_add_difference(MotionInfo *mi, const int num_cp_mv, + const Mv mvds[2][MAX_CONTROL_POINTS], const int amvr_shift) +{ + for (int i = 0; i < 2; i++) { + const PredFlag mask = i + PF_L0; + if (mi->pred_flag & mask) { + for (int j = 0; j < num_cp_mv; j++) { + const Mv *mvd = &mvds[i][j]; + mi->mv[i][j].x += mvd->x << amvr_shift; + mi->mv[i][j].y += mvd->y << amvr_shift; + } + } + } +} + +static int mvp_data(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + const VVCSPS *sps = fc->ps.sps; + const VVCPH *ph = &fc->ps.ph; + const VVCSH *sh = &lc->sc->sh; + const H266RawSliceHeader *rsh = sh->r; + MotionInfo *mi = &pu->mi; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + + int mvp_lx_flag[2] = {0}; + int cu_affine_type_flag = 0; + int num_cp_mv; + int amvr_enabled, has_no_zero_mvd = 0, amvr_shift; + Mv mvds[2][MAX_CONTROL_POINTS]; + + mi->pred_flag = ff_vvc_pred_flag(lc, IS_B(rsh)); + if (sps->r->sps_affine_enabled_flag && cb_width >= 16 && cb_height >= 16) { + pu->inter_affine_flag = ff_vvc_inter_affine_flag(lc); + set_cb_tab(lc, fc->tab.iaf, pu->inter_affine_flag); + if (sps->r->sps_6param_affine_enabled_flag && pu->inter_affine_flag) + cu_affine_type_flag = ff_vvc_cu_affine_type_flag(lc); + } + mi->motion_model_idc = pu->inter_affine_flag + cu_affine_type_flag; + num_cp_mv = mi->motion_model_idc + 1; + + if (sps->r->sps_smvd_enabled_flag && !ph->r->ph_mvd_l1_zero_flag && + mi->pred_flag == PF_BI && !pu->inter_affine_flag && + sh->ref_idx_sym[0] > -1 && sh->ref_idx_sym[1] > -1) + pu->sym_mvd_flag = ff_vvc_sym_mvd_flag(lc); + + for (int i = L0; i <= L1; i++) { + const PredFlag pred_flag = PF_L0 + !i; + if (mi->pred_flag != pred_flag) { + mi->ref_idx[i] = ref_idx_decode(lc, sh, pu->sym_mvd_flag, i); + has_no_zero_mvd |= mvds_decode(lc, mvds, num_cp_mv, i); + mvp_lx_flag[i] = ff_vvc_mvp_lx_flag(lc); + } + } + + amvr_enabled = mi->motion_model_idc == MOTION_TRANSLATION ? + sps->r->sps_amvr_enabled_flag : sps->r->sps_affine_amvr_enabled_flag; + amvr_enabled &= has_no_zero_mvd; + + amvr_shift = ff_vvc_amvr_shift(lc, pu->inter_affine_flag, cu->pred_mode, amvr_enabled); + + mi->hpel_if_idx = amvr_shift == 3; + mi->bcw_idx = bcw_idx_decode(lc, mi, cb_width, cb_height); + + if (mi->motion_model_idc) + ff_vvc_affine_mvp(lc, mvp_lx_flag, amvr_shift, mi); + else + ff_vvc_mvp(lc, mvp_lx_flag, amvr_shift, mi); + + mvp_add_difference(mi, num_cp_mv, mvds, amvr_shift); + + if (mi->motion_model_idc) + ff_vvc_store_sb_mvs(lc, pu); + else + ff_vvc_store_mv(lc, &pu->mi); + + return 0; +} + +// derive bdofFlag from 8.5.6 Decoding process for inter blocks +// derive dmvr from 8.5.1 General decoding process for coding units coded in inter prediction mode +static void derive_dmvr_bdof_flag(const VVCLocalContext *lc, PredictionUnit *pu) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const VVCPH *ph = &fc->ps.ph; + const VVCSH *sh = &lc->sc->sh; + const int poc = ph->poc; + const RefPicList *rpl0 = lc->sc->rpl + L0; + const RefPicList *rpl1 = lc->sc->rpl + L1; + const int8_t *ref_idx = pu->mi.ref_idx; + const MotionInfo *mi = &pu->mi; + const CodingUnit *cu = lc->cu; + const PredWeightTable *w = pps->r->pps_wp_info_in_ph_flag ? &fc->ps.ph.pwt : &sh->pwt; + + pu->dmvr_flag = 0; + pu->bdof_flag = 0; + + if (mi->pred_flag == PF_BI && + (poc - rpl0->list[ref_idx[L0]] == rpl1->list[ref_idx[L1]] - poc) && + !rpl0->isLongTerm[ref_idx[L0]] && !rpl1->isLongTerm[ref_idx[L1]] && + !cu->ciip_flag && + !mi->bcw_idx && + !w->weight_flag[L0][LUMA][mi->ref_idx[L0]] && !w->weight_flag[L1][LUMA][mi->ref_idx[L1]] && + !w->weight_flag[L0][CHROMA][mi->ref_idx[L0]] && !w->weight_flag[L1][CHROMA][mi->ref_idx[L1]] && + cu->cb_width >= 8 && cu->cb_height >= 8 && + (cu->cb_width * cu->cb_height >= 128)) { + // fixme: for RprConstraintsActiveFlag + if (!ph->r->ph_bdof_disabled_flag && + mi->motion_model_idc == MOTION_TRANSLATION && + !pu->merge_subblock_flag && + !pu->sym_mvd_flag) + pu->bdof_flag = 1; + if (!ph->r->ph_dmvr_disabled_flag && + pu->general_merge_flag && + !pu->mmvd_merge_flag) + pu->dmvr_flag = 1; + } +} + +// part of 8.5.1 General decoding process for coding units coded in inter prediction mode +static void refine_regular_subblock(const VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + + derive_dmvr_bdof_flag(lc, pu); + if (pu->dmvr_flag || pu->bdof_flag) { + pu->mi.num_sb_x = (cu->cb_width > 16) ? (cu->cb_width >> 4) : 1; + pu->mi.num_sb_y = (cu->cb_height > 16) ? (cu->cb_height >> 4) : 1; + } +} + +static int vvc_inter_data(VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + const MotionInfo *mi = &pu->mi; + int ret = 0; + + pu->general_merge_flag = 1; + if (!cu->skip_flag) + pu->general_merge_flag = ff_vvc_general_merge_flag(lc); + + if (pu->general_merge_flag) { + hls_merge_data(lc); + } else if (cu->pred_mode == MODE_IBC){ + avpriv_report_missing_feature(lc->fc->avctx, "Intra Block Copy"); + return AVERROR_PATCHWELCOME; + } else { + ret = mvp_data(lc); + } + if (!pu->merge_gpm_flag && !pu->inter_affine_flag && !pu->merge_subblock_flag) { + refine_regular_subblock(lc); + ff_vvc_update_hmvp(lc, mi); + } + return ret; +} + +static int hls_coding_unit(VVCLocalContext *lc, int x0, int y0, int cb_width, int cb_height, + int cqt_depth, const VVCTreeType tree_type, VVCModeType mode_type) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int hs = sps->hshift[CHROMA]; + const int vs = sps->vshift[CHROMA]; + const int is_128 = cb_width > 64 || cb_height > 64; + int pred_mode_plt_flag = 0; + int ret; + + CodingUnit *cu = add_cu(lc, x0, y0, cb_width, cb_height, cqt_depth, tree_type); + + if (!cu) + return AVERROR(ENOMEM); + + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + + if (IS_I(rsh) && is_128) + mode_type = MODE_TYPE_INTRA; + cu->pred_mode = pred_mode_decode(lc, tree_type, mode_type); + + if (cu->pred_mode == MODE_INTRA && sps->r->sps_palette_enabled_flag && !is_128 && !cu->skip_flag && + mode_type != MODE_TYPE_INTER && ((cb_width * cb_height) > + (tree_type != DUAL_TREE_CHROMA ? 16 : (16 << hs << vs))) && + (mode_type != MODE_TYPE_INTRA || tree_type != DUAL_TREE_CHROMA)) { + pred_mode_plt_flag = ff_vvc_pred_mode_plt_flag(lc); + if (pred_mode_plt_flag) { + avpriv_report_missing_feature(lc->fc->avctx, "Palette"); + return AVERROR_PATCHWELCOME; + } + } + if (cu->pred_mode == MODE_INTRA && sps->r->sps_act_enabled_flag && tree_type == SINGLE_TREE) { + avpriv_report_missing_feature(fc->avctx, "Adaptive Color Transform"); + return AVERROR_PATCHWELCOME; + } + if (cu->pred_mode == MODE_INTRA || cu->pred_mode == MODE_PLT) { + if (tree_type == SINGLE_TREE || tree_type == DUAL_TREE_LUMA) { + if (pred_mode_plt_flag) { + avpriv_report_missing_feature(lc->fc->avctx, "Palette"); + return AVERROR_PATCHWELCOME; + } else { + intra_luma_pred_modes(lc); + } + ff_vvc_set_intra_mvf(lc); + } + if ((tree_type == SINGLE_TREE || tree_type == DUAL_TREE_CHROMA) && sps->r->sps_chroma_format_idc) { + if (pred_mode_plt_flag && tree_type == DUAL_TREE_CHROMA) { + avpriv_report_missing_feature(lc->fc->avctx, "Palette"); + return AVERROR_PATCHWELCOME; + } else if (!pred_mode_plt_flag) { + if (!cu->act_enabled_flag) + intra_chroma_pred_modes(lc); + } + } + } else if (tree_type != DUAL_TREE_CHROMA) { /* MODE_INTER or MODE_IBC */ + if ((ret = vvc_inter_data(lc)) < 0) + return ret; + } + if (cu->pred_mode != MODE_INTRA && !pred_mode_plt_flag && !lc->cu->pu.general_merge_flag) + cu->coded_flag = ff_vvc_cu_coded_flag(lc); + else + cu->coded_flag = !(cu->skip_flag || pred_mode_plt_flag); + + if (cu->coded_flag) { + sbt_info(lc, sps); + if (sps->r->sps_act_enabled_flag && cu->pred_mode != MODE_INTRA && tree_type == SINGLE_TREE) { + avpriv_report_missing_feature(fc->avctx, "Adaptive Color Transform"); + return AVERROR_PATCHWELCOME; + } + lc->parse.lfnst_dc_only = 1; + lc->parse.lfnst_zero_out_sig_coeff_flag = 1; + lc->parse.mts_dc_only = 1; + lc->parse.mts_zero_out_sig_coeff_flag = 1; + ret = hls_transform_tree(lc, x0, y0, cb_width, cb_height, cu->ch_type); + if (ret < 0) + return ret; + cu->lfnst_idx = lfnst_idx_decode(lc); + cu->mts_idx = mts_idx_decode(lc); + set_qp_c(lc); + if (ret < 0) + return ret; + } else { + av_assert0(tree_type == SINGLE_TREE); + ret = skipped_transform_tree_unit(lc); + if (ret < 0) + return ret; + } + set_cu_tabs(lc, cu); + + return 0; +} + +static int derive_mode_type_condition(const VVCLocalContext *lc, + const VVCSplitMode split, const int cb_width, const int cb_height, const VVCModeType mode_type_curr) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const VVCSPS *sps = lc->fc->ps.sps; + const int area = cb_width * cb_height; + + if ((IS_I(rsh) && sps->r->sps_qtbtt_dual_tree_intra_flag) || + mode_type_curr != MODE_TYPE_ALL || !sps->r->sps_chroma_format_idc || + sps->r->sps_chroma_format_idc == CHROMA_FORMAT_444) + return 0; + if ((area == 64 && (split == SPLIT_QT || split == SPLIT_TT_HOR || split == SPLIT_TT_VER)) || + (area == 32 && (split == SPLIT_BT_HOR || split == SPLIT_BT_VER))) + return 1; + if ((area == 64 && (split == SPLIT_BT_HOR || split == SPLIT_BT_VER) && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_420) || + (area == 128 && (split == SPLIT_TT_HOR || split == SPLIT_TT_VER) && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_420) || + (cb_width == 8 && split == SPLIT_BT_VER) || (cb_width == 16 && split == SPLIT_TT_VER)) + return 1 + !IS_I(rsh); + + return 0; +} + +static VVCModeType mode_type_decode(VVCLocalContext *lc, const int x0, const int y0, + const int cb_width, const int cb_height, const VVCSplitMode split, const int ch_type, + const VVCModeType mode_type_curr) +{ + VVCModeType mode_type; + const int mode_type_condition = derive_mode_type_condition(lc, split, cb_width, cb_height, mode_type_curr); + + if (mode_type_condition == 1) + mode_type = MODE_TYPE_INTRA; + else if (mode_type_condition == 2) { + mode_type = ff_vvc_non_inter_flag(lc, x0, y0, ch_type) ? MODE_TYPE_INTRA : MODE_TYPE_INTER; + } else { + mode_type = mode_type_curr; + } + + return mode_type; +} + +static int hls_coding_tree(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, int part_idx, + VVCSplitMode last_split_mode, VVCTreeType tree_type_curr, VVCModeType mode_type_curr); + +static int coding_tree_btv(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(x, idx) do { \ + ret = hls_coding_tree(lc, x, y0, cb_width / 2, cb_height, \ + qg_on_y, qg_on_c, cb_sub_div + 1, cqt_depth, mtt_depth + 1, \ + depth_offset, idx, SPLIT_BT_VER, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ +} while (0); + + const VVCPPS *pps = lc->fc->ps.pps; + const int x1 = x0 + cb_width / 2; + int ret = 0; + + depth_offset += (x0 + cb_width > pps->width) ? 1 : 0; + CODING_TREE(x0, 0); + if (x1 < pps->width) + CODING_TREE(x1, 1); + + return 0; + +#undef CODING_TREE +} + +static int coding_tree_bth(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(y, idx) do { \ + ret = hls_coding_tree(lc, x0, y, cb_width , cb_height / 2, \ + qg_on_y, qg_on_c, cb_sub_div + 1, cqt_depth, mtt_depth + 1, \ + depth_offset, idx, SPLIT_BT_HOR, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ + } while (0); + + const VVCPPS *pps = lc->fc->ps.pps; + const int y1 = y0 + (cb_height / 2); + int ret = 0; + + depth_offset += (y0 + cb_height > pps->height) ? 1 : 0; + CODING_TREE(y0, 0); + if (y1 < pps->height) + CODING_TREE(y1, 1); + + return 0; + +#undef CODING_TREE +} + +static int coding_tree_ttv(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(x, w, sub_div, idx) do { \ + ret = hls_coding_tree(lc, x, y0, w, cb_height, \ + qg_on_y, qg_on_c, sub_div, cqt_depth, mtt_depth + 1, \ + depth_offset, idx, SPLIT_TT_VER, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ + } while (0); + + const VVCSH *sh = &lc->sc->sh; + const int x1 = x0 + cb_width / 4; + const int x2 = x0 + cb_width * 3 / 4; + int ret; + + qg_on_y = qg_on_y && (cb_sub_div + 2 <= sh->cu_qp_delta_subdiv); + qg_on_c = qg_on_c && (cb_sub_div + 2 <= sh->cu_chroma_qp_offset_subdiv); + + CODING_TREE(x0, cb_width / 4, cb_sub_div + 2, 0); + CODING_TREE(x1, cb_width / 2, cb_sub_div + 1, 1); + CODING_TREE(x2, cb_width / 4, cb_sub_div + 2, 2); + + return 0; + +#undef CODING_TREE +} + +static int coding_tree_tth(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(y, h, sub_div, idx) do { \ + ret = hls_coding_tree(lc, x0, y, cb_width, h, \ + qg_on_y, qg_on_c, sub_div, cqt_depth, mtt_depth + 1, \ + depth_offset, idx, SPLIT_TT_HOR, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ + } while (0); + + const VVCSH *sh = &lc->sc->sh; + const int y1 = y0 + (cb_height / 4); + const int y2 = y0 + (3 * cb_height / 4); + int ret; + + qg_on_y = qg_on_y && (cb_sub_div + 2 <= sh->cu_qp_delta_subdiv); + qg_on_c = qg_on_c && (cb_sub_div + 2 <= sh->cu_chroma_qp_offset_subdiv); + + CODING_TREE(y0, cb_height / 4, cb_sub_div + 2, 0); + CODING_TREE(y1, cb_height / 2, cb_sub_div + 1, 1); + CODING_TREE(y2, cb_height / 4, cb_sub_div + 2, 2); + + return 0; + +#undef CODING_TREE +} + +static int coding_tree_qt(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(x, y, idx) do { \ + ret = hls_coding_tree(lc, x, y, cb_width / 2, cb_height / 2, \ + qg_on_y, qg_on_c, cb_sub_div + 2, cqt_depth + 1, 0, 0, \ + idx, SPLIT_QT, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ + } while (0); + + const VVCPPS *pps = lc->fc->ps.pps; + const int x1 = x0 + cb_width / 2; + const int y1 = y0 + cb_height / 2; + int ret = 0; + + CODING_TREE(x0, y0, 0); + if (x1 < pps->width) + CODING_TREE(x1, y0, 1); + if (y1 < pps->height) + CODING_TREE(x0, y1, 2); + if (x1 < pps->width && + y1 < pps->height) + CODING_TREE(x1, y1, 3); + + return 0; + +#undef CODING_TREE +} + +typedef int (*coding_tree_fn)(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type); + +const static coding_tree_fn coding_tree[] = { + coding_tree_tth, + coding_tree_bth, + coding_tree_ttv, + coding_tree_btv, + coding_tree_qt, +}; + +static int hls_coding_tree(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, int part_idx, + VVCSplitMode last_split_mode, VVCTreeType tree_type_curr, VVCModeType mode_type_curr) +{ + VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const VVCSH *sh = &lc->sc->sh; + const H266RawSliceHeader *rsh = sh->r; + const int ch_type = tree_type_curr == DUAL_TREE_CHROMA; + int ret; + VVCAllowedSplit allowed; + + if (pps->r->pps_cu_qp_delta_enabled_flag && qg_on_y && cb_sub_div <= sh->cu_qp_delta_subdiv) { + lc->parse.is_cu_qp_delta_coded = 0; + lc->parse.cu_qg_top_left_x = x0; + lc->parse.cu_qg_top_left_y = y0; + } + if (rsh->sh_cu_chroma_qp_offset_enabled_flag && qg_on_c && + cb_sub_div <= sh->cu_chroma_qp_offset_subdiv) { + lc->parse.is_cu_chroma_qp_offset_coded = 0; + memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset)); + } + + can_split(lc, x0, y0, cb_width, cb_height, mtt_depth, depth_offset, part_idx, + last_split_mode, tree_type_curr, mode_type_curr, &allowed); + if (ff_vvc_split_cu_flag(lc, x0, y0, cb_width, cb_height, ch_type, &allowed)) { + VVCSplitMode split = ff_vvc_split_mode(lc, x0, y0, cb_width, cb_height, cqt_depth, mtt_depth, ch_type, &allowed); + VVCModeType mode_type = mode_type_decode(lc, x0, y0, cb_width, cb_height, split, ch_type, mode_type_curr); + + VVCTreeType tree_type = (mode_type == MODE_TYPE_INTRA) ? DUAL_TREE_LUMA : tree_type_curr; + + if (split != SPLIT_QT) { + if (!(x0 & 31) && !(y0 & 31) && mtt_depth <= 1) + TAB_MSM(fc, mtt_depth, x0, y0) = split; + } + ret = coding_tree[split - 1](lc, x0, y0, cb_width, cb_height, qg_on_y, qg_on_c, + cb_sub_div, cqt_depth, mtt_depth, depth_offset, tree_type, mode_type); + if (ret < 0) + return ret; + if (mode_type_curr == MODE_TYPE_ALL && mode_type == MODE_TYPE_INTRA) { + ret = hls_coding_tree(lc, x0, y0, cb_width, cb_height, 0, qg_on_c, cb_sub_div, + cqt_depth, mtt_depth, 0, 0, split, DUAL_TREE_CHROMA, mode_type); + if (ret < 0) + return ret; + } + } else { + ret = hls_coding_unit(lc, x0, y0, cb_width, cb_height, cqt_depth, tree_type_curr, mode_type_curr); + if (ret < 0) + return ret; + } + + return 0; +} + +static int dual_tree_implicit_qt_split(VVCLocalContext *lc, + const int x0, const int y0, const int cb_size, const int cqt_depth) +{ + const VVCSH *sh = &lc->sc->sh; + const H266RawSliceHeader *rsh = sh->r; + const VVCPPS *pps = lc->fc->ps.pps; + const int cb_subdiv = 2 * cqt_depth; + int ret; + + if (cb_size > 64) { + #define DUAL_TREE(x, y) do { \ + ret = dual_tree_implicit_qt_split(lc, x, y, cb_size / 2, cqt_depth + 1); \ + if (ret < 0) \ + return ret; \ + } while (0) + + const int x1 = x0 + (cb_size / 2); + const int y1 = y0 + (cb_size / 2); + if (pps->r->pps_cu_qp_delta_enabled_flag && cb_subdiv <= sh->cu_qp_delta_subdiv) { + lc->parse.is_cu_qp_delta_coded = 0; + lc->parse.cu_qg_top_left_x = x0; + lc->parse.cu_qg_top_left_y = y0; + } + if (rsh->sh_cu_chroma_qp_offset_enabled_flag && cb_subdiv <= sh->cu_chroma_qp_offset_subdiv) { + lc->parse.is_cu_chroma_qp_offset_coded = 0; + memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset)); + } + DUAL_TREE(x0, y0); + if (x1 < pps->width) + DUAL_TREE(x1, y0); + if (y1 < pps->height) + DUAL_TREE(x0, y1); + if (x1 < pps->width && y1 < pps->height) + DUAL_TREE(x1, y1); + #undef DUAL_TREE + } else { + #define CODING_TREE(tree_type) do { \ + const int qg_on_y = tree_type == DUAL_TREE_LUMA; \ + ret = hls_coding_tree(lc, x0, y0, cb_size, cb_size, qg_on_y, !qg_on_y, \ + cb_subdiv, cqt_depth, 0, 0, 0, SPLIT_NONE, tree_type, MODE_TYPE_ALL); \ + if (ret < 0) \ + return ret; \ + } while (0) + CODING_TREE(DUAL_TREE_LUMA); + CODING_TREE(DUAL_TREE_CHROMA); + #undef CODING_TREE + } + return 0; +} + +#define SET_SAO(elem, value) \ +do { \ + if (!sao_merge_up_flag && !sao_merge_left_flag) \ + sao->elem = value; \ + else if (sao_merge_left_flag) \ + sao->elem = CTB(fc->tab.sao, rx-1, ry).elem; \ + else if (sao_merge_up_flag) \ + sao->elem = CTB(fc->tab.sao, rx, ry-1).elem; \ + else \ + sao->elem = 0; \ +} while (0) + +static void hls_sao(VVCLocalContext *lc, const int rx, const int ry) +{ + VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + int sao_merge_left_flag = 0; + int sao_merge_up_flag = 0; + SAOParams *sao = &CTB(fc->tab.sao, rx, ry); + int c_idx, i; + + if (rsh->sh_sao_luma_used_flag || rsh->sh_sao_chroma_used_flag) { + if (rx > 0) { + if (lc->ctb_left_flag) + sao_merge_left_flag = ff_vvc_sao_merge_flag_decode(lc); + } + if (ry > 0 && !sao_merge_left_flag) { + if (lc->ctb_up_flag) + sao_merge_up_flag = ff_vvc_sao_merge_flag_decode(lc); + } + } + + for (c_idx = 0; c_idx < (fc->ps.sps->r->sps_chroma_format_idc ? 3 : 1); c_idx++) { + const int sao_used_flag = !c_idx ? rsh->sh_sao_luma_used_flag : rsh->sh_sao_chroma_used_flag; + if (!sao_used_flag) { + sao->type_idx[c_idx] = SAO_NOT_APPLIED; + continue; + } + + if (c_idx == 2) { + sao->type_idx[2] = sao->type_idx[1]; + sao->eo_class[2] = sao->eo_class[1]; + } else { + SET_SAO(type_idx[c_idx], ff_vvc_sao_type_idx_decode(lc)); + } + + if (sao->type_idx[c_idx] == SAO_NOT_APPLIED) + continue; + + for (i = 0; i < 4; i++) + SET_SAO(offset_abs[c_idx][i], ff_vvc_sao_offset_abs_decode(lc)); + + if (sao->type_idx[c_idx] == SAO_BAND) { + for (i = 0; i < 4; i++) { + if (sao->offset_abs[c_idx][i]) { + SET_SAO(offset_sign[c_idx][i], + ff_vvc_sao_offset_sign_decode(lc)); + } else { + sao->offset_sign[c_idx][i] = 0; + } + } + SET_SAO(band_position[c_idx], ff_vvc_sao_band_position_decode(lc)); + } else if (c_idx != 2) { + SET_SAO(eo_class[c_idx], ff_vvc_sao_eo_class_decode(lc)); + } + + // Inferred parameters + sao->offset_val[c_idx][0] = 0; + for (i = 0; i < 4; i++) { + sao->offset_val[c_idx][i + 1] = sao->offset_abs[c_idx][i]; + if (sao->type_idx[c_idx] == SAO_EDGE) { + if (i > 1) + sao->offset_val[c_idx][i + 1] = -sao->offset_val[c_idx][i + 1]; + } else if (sao->offset_sign[c_idx][i]) { + sao->offset_val[c_idx][i + 1] = -sao->offset_val[c_idx][i + 1]; + } + sao->offset_val[c_idx][i + 1] *= 1 << (fc->ps.sps->bit_depth - FFMIN(10, fc->ps.sps->bit_depth)); + } + } +} + +static void alf_params(VVCLocalContext *lc, const int rx, const int ry) +{ + const VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *sh = lc->sc->sh.r; + ALFParams *alf = &CTB(fc->tab.alf, rx, ry); + + alf->ctb_flag[LUMA] = alf->ctb_flag[CB] = alf->ctb_flag[CR] = 0; + if (sh->sh_alf_enabled_flag) { + alf->ctb_flag[LUMA] = ff_vvc_alf_ctb_flag(lc, rx, ry, LUMA); + if (alf->ctb_flag[LUMA]) { + uint8_t alf_use_aps_flag = 0; + if (sh->sh_num_alf_aps_ids_luma > 0) + alf_use_aps_flag = ff_vvc_alf_use_aps_flag(lc); + if (alf_use_aps_flag) { + alf->ctb_filt_set_idx_y = 16; + if (sh->sh_num_alf_aps_ids_luma > 1) + alf->ctb_filt_set_idx_y += ff_vvc_alf_luma_prev_filter_idx(lc); + } else { + alf->ctb_filt_set_idx_y = ff_vvc_alf_luma_fixed_filter_idx(lc); + } + } + for (int c_idx = CB; c_idx <= CR; c_idx++) { + const uint8_t alf_enabled_flag = + c_idx == CB ? sh->sh_alf_cb_enabled_flag : sh->sh_alf_cr_enabled_flag; + if (alf_enabled_flag) { + const VVCALF *aps = fc->ps.alf_list[sh->sh_alf_aps_id_chroma]; + alf->ctb_flag[c_idx] = ff_vvc_alf_ctb_flag(lc, rx, ry, c_idx); + alf->alf_ctb_filter_alt_idx[c_idx - 1] = 0; + if (alf->ctb_flag[c_idx] && aps->num_chroma_filters > 1) + alf->alf_ctb_filter_alt_idx[c_idx - 1] = ff_vvc_alf_ctb_filter_alt_idx(lc, c_idx, aps->num_chroma_filters); + } + } + } + if (fc->ps.sps->r->sps_ccalf_enabled_flag) { + const uint8_t cc_enabled[] = { sh->sh_alf_cc_cb_enabled_flag, sh->sh_alf_cc_cr_enabled_flag }; + const uint8_t cc_aps_id[] = { sh->sh_alf_cc_cb_aps_id, sh->sh_alf_cc_cr_aps_id }; + for (int i = 0; i < 2; i++) { + alf->ctb_cc_idc[i] = 0; + if (cc_enabled[i]) { + const VVCALF *aps = fc->ps.alf_list[cc_aps_id[i]]; + alf->ctb_cc_idc[i] = ff_vvc_alf_ctb_cc_idc(lc, rx, ry, i, aps->num_cc_filters[i]); + } + } + } +} + +static void deblock_params(VVCLocalContext *lc, const int rx, const int ry) +{ + VVCFrameContext *fc = lc->fc; + const VVCSH *sh = &lc->sc->sh; + CTB(fc->tab.deblock, rx, ry) = sh->deblock; +} + +static int hls_coding_tree_unit(VVCLocalContext *lc, + const int x0, const int y0, const int ctu_idx, const int rx, const int ry) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const VVCSH *sh = &lc->sc->sh; + const H266RawSliceHeader *rsh = sh->r; + const unsigned int ctb_size = sps->ctb_size_y; + int ret = 0; + + memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset)); + + hls_sao(lc, x0 >> sps->ctb_log2_size_y, y0 >> sps->ctb_log2_size_y); + alf_params(lc, x0 >> sps->ctb_log2_size_y, y0 >> sps->ctb_log2_size_y); + deblock_params(lc, x0 >> sps->ctb_log2_size_y, y0 >> sps->ctb_log2_size_y); + + if (IS_I(rsh) && sps->r->sps_qtbtt_dual_tree_intra_flag) + ret = dual_tree_implicit_qt_split(lc, x0, y0, ctb_size, 0); + else + ret = hls_coding_tree(lc, x0, y0, ctb_size, ctb_size, + 1, 1, 0, 0, 0, 0, 0, SPLIT_NONE, SINGLE_TREE, MODE_TYPE_ALL); + if (ret < 0) + return ret; + + if (rx == pps->ctb_to_col_bd[rx + 1] - 1) { + if (ctu_idx == sh->num_ctus_in_curr_slice - 1) { + const int end_of_slice_one_bit = ff_vvc_end_of_slice_flag_decode(lc); + if (!end_of_slice_one_bit) + return AVERROR_INVALIDDATA; + } else { + if (ry == pps->ctb_to_row_bd[ry + 1] - 1) { + const int end_of_tile_one_bit = ff_vvc_end_of_tile_one_bit(lc); + if (!end_of_tile_one_bit) + return AVERROR_INVALIDDATA; + } else { + if (fc->ps.sps->r->sps_entropy_coding_sync_enabled_flag) { + const int end_of_subset_one_bit = ff_vvc_end_of_subset_one_bit(lc); + if (!end_of_subset_one_bit) + return AVERROR_INVALIDDATA; + } + } + } + } + + return 0; +} + +static int has_inter_luma(const CodingUnit *cu) +{ + return cu->pred_mode != MODE_INTRA && cu->pred_mode != MODE_PLT && cu->tree_type != DUAL_TREE_CHROMA; +} + +static int pred_get_y(const int y0, const Mv *mv, const int height) +{ + return FFMAX(0, y0 + (mv->y >> 4) + height); +} + +static void cu_get_max_y(const CodingUnit *cu, int max_y[2][VVC_MAX_REF_ENTRIES], const VVCFrameContext *fc) +{ + const PredictionUnit *pu = &cu->pu; + + if (pu->merge_gpm_flag) { + for (int i = 0; i < FF_ARRAY_ELEMS(pu->gpm_mv); i++) { + const MvField *mvf = pu->gpm_mv + i; + const int lx = mvf->pred_flag - PF_L0; + const int idx = mvf->ref_idx[lx]; + const int y = pred_get_y(cu->y0, mvf->mv + lx, cu->cb_height); + + max_y[lx][idx] = FFMAX(max_y[lx][idx], y); + } + } else { + const MotionInfo *mi = &pu->mi; + const int max_dmvr_off = (!pu->inter_affine_flag && pu->dmvr_flag) ? 2 : 0; + const int sbw = cu->cb_width / mi->num_sb_x; + const int sbh = cu->cb_height / mi->num_sb_y; + for (int sby = 0; sby < mi->num_sb_y; sby++) { + for (int sbx = 0; sbx < mi->num_sb_x; sbx++) { + const int x0 = cu->x0 + sbx * sbw; + const int y0 = cu->y0 + sby * sbh; + const MvField *mvf = ff_vvc_get_mvf(fc, x0, y0); + for (int lx = 0; lx < 2; lx++) { + const PredFlag mask = 1 << lx; + if (mvf->pred_flag & mask) { + const int idx = mvf->ref_idx[lx]; + const int y = pred_get_y(y0, mvf->mv + lx, sbh); + + max_y[lx][idx] = FFMAX(max_y[lx][idx], y + max_dmvr_off); + } + } + } + } + } +} + +static void pred_get_max_y(VVCLocalContext *lc, const int rs) +{ + const VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + CTU *ctu = fc->tab.ctus + rs; + const CodingUnit *cu = ctu->cus; + + if (IS_I(rsh)) + return; + + for (int lx = 0; lx < 2; lx++) + memset(ctu->max_y[lx], -1, sizeof(ctu->max_y[0][0]) * rsh->num_ref_idx_active[lx]); + + while (cu) { + if (has_inter_luma(cu)) + cu_get_max_y(cu, ctu->max_y, fc); + cu = cu->next; + } + ctu->max_y_idx[0] = ctu->max_y_idx[1] = 0; +} + +int ff_vvc_coding_tree_unit(VVCLocalContext *lc, + const int ctu_idx, const int rs, const int rx, const int ry) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int x_ctb = rx << sps->ctb_log2_size_y; + const int y_ctb = ry << sps->ctb_log2_size_y; + const int ctb_size = 1 << sps->ctb_log2_size_y << sps->ctb_log2_size_y; + EntryPoint* ep = lc->ep; + int ret; + + if (rx == pps->ctb_to_col_bd[rx]) { + //fix me for ibc + ep->num_hmvp = 0; + ep->is_first_qg = ry == pps->ctb_to_row_bd[ry] || !ctu_idx; + } + + lc->coeffs = fc->tab.coeffs + rs * ctb_size * VVC_MAX_SAMPLE_ARRAYS; + lc->cu = NULL; + + ff_vvc_cabac_init(lc, ctu_idx, rx, ry); + ff_vvc_decode_neighbour(lc, x_ctb, y_ctb, rx, ry, rs); + ret = hls_coding_tree_unit(lc, x_ctb, y_ctb, ctu_idx, rx, ry); + if (ret < 0) + return ret; + pred_get_max_y(lc, rs); + + return 0; +} + void ff_vvc_decode_neighbour(VVCLocalContext *lc, const int x_ctb, const int y_ctb, const int rx, const int ry, const int rs) { @@ -90,6 +2454,14 @@ void ff_vvc_ctu_free_cus(CTU *ctu) } } +int ff_vvc_get_qPy(const VVCFrameContext *fc, const int xc, const int yc) +{ + const int min_cb_log2_size_y = fc->ps.sps->min_cb_log2_size_y; + const int x = xc >> min_cb_log2_size_y; + const int y = yc >> min_cb_log2_size_y; + return fc->tab.qp[LUMA][x + y * fc->ps.pps->min_cb_width]; +} + void ff_vvc_ep_init_stat_coeff(EntryPoint *ep, const int bit_depth, const int persistent_rice_adaptation_enabled_flag) { diff --git a/libavcodec/vvc/vvc_ctu.h b/libavcodec/vvc/vvc_ctu.h index 3f5e677fad..642cf6a0c2 100644 --- a/libavcodec/vvc/vvc_ctu.h +++ b/libavcodec/vvc/vvc_ctu.h @@ -458,6 +458,17 @@ typedef struct ALFParams { uint8_t applied[3]; } ALFParams; +/** + * parse a CTU + * @param lc local context for CTU + * @param ctb_addr CTB(CTU) address in the current slice + * @param rs raster order for the CTU. + * @param rx raster order x for the CTU. + * @param ry raster order y for the CTU. + * @return AVERROR + */ +int ff_vvc_coding_tree_unit(VVCLocalContext *lc, int ctu_idx, int rs, int rx, int ry); + //utils void ff_vvc_set_neighbour_available(VVCLocalContext *lc, int x0, int y0, int w, int h); void ff_vvc_decode_neighbour(VVCLocalContext *lc, int x_ctb, int y_ctb, int rx, int ry, int rs); From patchwork Sun Nov 12 10:35:25 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 44630 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:92a5:b0:181:818d:5e7f with SMTP id q37csp728630pzg; Sun, 12 Nov 2023 02:38:08 -0800 (PST) X-Google-Smtp-Source: AGHT+IHxjP97Rh2vEvdxhnbi6bGJ8CCH5JCAHgqQPRky82UP89pO023KoARjIlX9FFo9q61uqBXw X-Received: by 2002:a05:6402:520b:b0:543:5144:1779 with SMTP id s11-20020a056402520b00b0054351441779mr6834191edd.11.1699785488399; Sun, 12 Nov 2023 02:38:08 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id h20-20020a05640250d400b0053e158b5873si1650288edb.352.2023.11.12.02.38.08; Sun, 12 Nov 2023 02:38:08 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@outlook.com header.s=selector1 header.b=MnE2hV0p; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 04D8168CCB0; Sun, 12 Nov 2023 12:36:28 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from APC01-SG2-obe.outbound.protection.outlook.com (mail-sgaapc01olkn2035.outbound.protection.outlook.com [40.92.53.35]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 439F368CC6F for ; Sun, 12 Nov 2023 12:36:20 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=UzxUWvNSiU0D2ZneOCtSXeUsIwQel2awHULAbTIlH6JFrUVQBMQ4dA1/InPn9YEfI9Mw1YbWJ234H4aR6tlrqB9O4oFKxeAdJX166GV0IW3N8W1ITW2IHiNgeG29cjOigrfdCbxIV1pl+DeO1RTINr39w8mbHvN2XvDR30DR9IZc8B/nGo2pLR5DB3TPlk6zfdA/XSrd7c+1U3MvjdJn+6e55A3HdA5o0H5gQ8ZCNb5sLLukGK+Du2k1HuMc/MlNWxP+eQS63Wammq3Y5kBG/Tefehxq5YJzZ0lBV53iAh3ma6Z53D5yGi8ZtuQgaNjg/2gH3nSVbPFKa8dEdNxKNQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=y1jwUO8/SFWcDSkYTUxKsSkTOqJIAD06dZ3JwwEPK7M=; b=IGfQD9BUQsJyvY0YOgY8FKQUCQ6lTa8vDqffKisrMova7Cg61UCRyN5Y/awjyJh7V7NmISdBnd75nhIVd6cH0FHFEBoM3Ub8G+E0j1KWYpbUcOAguJcjh4+okByW0yVES8VGXuGf1u0lmeobevDK2P90DlnYnY1E1pyGLwWAaTwVUwZweVYdzsXAva2moKGZyAwB07IVN0t8tYu2MOS6/hbdpallQrhacX+eNGhcVCnz/jiNdHSP3BEMIVpfTXTGJPbdygTjPX861bAJ3qGXPwD+p89SyLxRzYnCnzFSfqUz0OEZspn7bmBuAs5C6z/AthbZWTkoEK5oCcsQa62yOA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=outlook.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=y1jwUO8/SFWcDSkYTUxKsSkTOqJIAD06dZ3JwwEPK7M=; b=MnE2hV0pUw4PELtbyH5HtcbLBSPdvOIJa6o/t0lZ0DpW4DYZ/tdJBzxHM2/Yt6tf1UdiNi7+qEVIYnfci8h95EqS5LlJ/U5Y9YKRWFk9yTjezd056+6EWtpBm5MXeNoY3AOnk1sU2iIpOb6U2NmFkWk4yjf279U7slsGw7G44sBqVCu0XvPEQnjKETBnCLOIKZMc6mC1MrORP0oA0Gp/uIbl1ossGMTq37WnK/FflfPLXwFMk59HB7i+6/4EqEUHEdNcnrzVUwm0hsWFcj7b4/BlpduzeDCDQuV0McBLLbRWo/gKhtdmqR7vc9bKMZ31XC6mYuWFAhQb5vQcLdV3Kw== Received: from TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) by TYSPR06MB6768.apcprd06.prod.outlook.com (2603:1096:400:475::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6954.29; Sun, 12 Nov 2023 10:36:04 +0000 Received: from TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f]) by TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f%4]) with mapi id 15.20.6954.028; Sun, 12 Nov 2023 10:36:04 +0000 From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Nov 2023 18:35:25 +0800 Message-ID: X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231112103526.11245-1-nuomi2021@gmail.com> References: <20231112103526.11245-1-nuomi2021@gmail.com> X-TMN: [jrJqDAdgrIk44havbObQvqiWslMw9iTC] X-ClientProxiedBy: TYWP286CA0030.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:262::20) To TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) X-Microsoft-Original-Message-ID: <20231112103526.11245-13-nuomi2021@gmail.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 2 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: TYSPR06MB6433:EE_|TYSPR06MB6768:EE_ X-MS-Office365-Filtering-Correlation-Id: 9927d459-8e42-4581-0b88-08dbe36b2b60 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: nTNMuhDRR6N2z3vyiKf3E2BwZvDGgB5bx4A9/ppiHeWV/s+XIzh4EA35b/ckiN9hBdulbH07p45+9H4EDlGxEppMb4tRmwbkNRLdwouUAbZHG7R1Dj043OAHDyBqtiNFFXFNH/sgflNOG5X5TzDFJLmbfzyHrHJBVoGzhAG5Um4Z2rCjpGenUlWlU+/PWmed3wOQNHL3DVay0A3QdjrgAnRFln5xi7jFcKJFBfXk5ceWpUiFvJAY42djXLlw4cAXLhEHg0BqMOeETEyB4X9865JzUDpFQtHDoo1m61o/ZMITZV/eVOgNodnczoYA5YnPyJXZXBL4qJycBGJaUWutr+fDllCAuJU6aKVx+wsi6D7QxRoarNktWdiQOd2G3TgVBztjGDfyUFeY6aqPriTeNAXz0BG68E7gVktRYOZ9Gn4211Axx4egc1y692MqbZfG7NHNIKorrvvuuAI/Zxr1nqgtWVCBcxteOKizjxs3A0rFnV6XO48DbUE+vfPRT/OuHxIAc34F4jQKx9AgCGSNs7li77Iu4pgFtSWy4jsjdoX28eGLIB+l8DjbucAGOmSJs/30xSWz//4UYTZ7e/XKbEedXflzc1K/Qu+1LR2K/eSczD7J9GbZTtPDFtC/4lOC X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: sOlRfu7xDYZIHs2QEP9TnAEEmKqvZsURQVv/AxYtWkuhOCypdXJ6yWN5awlIhLlJD30rak1FFWiYF9BCHb393xsvD9C+5W/ThDVMtye5ytHRf4I+0dlfjvxkFTC+Wilw7ys0H3W6VX4/YFDJ3ZYLtc0htDWSAcR0/MTnjWwmK94IoP+zkoHMw8JaFXEk6wxWigCVmOd+3bNskanm2u8BrjSWQp7068syAI/S3ePFTCTWX3sLZPB3yCwbb3oO1wKUqR4/YlO312yJhFlgnIL6b1xNjotSIt1T4aVfCEuhCO/hXaYGzmgiUjX41eigUaHEFgdqzxvS/oO2XRzBYlw3tbuf9hIfZUD2s0xa++W8ep/6VCAucPoUp/yJN0RpoTjuFsCzDBcFNJ5yL2Kxwr57dp5BRyQE6cLAkz4MuQDpusy63wOFdL2BIAIou19LTFDdCzNPRaqUNQeRXZFR64bnEyCLpEn+CBH7j3X6oZyulo/eYVFo5L3gSd8RuZiwmuP9iUSpiiMrC8g30y/us53FnVFKsJ+daBCBq3loDFHPbq9jCpVA7xlOJfIVDof1pCh0hrJ/qC713Jojn1MDMOKej8zmPHPDvrtwI0h5otNxKWaPZ8J3t9OdnbZjjhJfCPnmSS7H05laVTN7MX53q+L5LdNV3g1ODdxAJtks8D5KD4X/Rkgz5T3SZM88QjWOaf0+IN6iQYmJPIVyjdlPDiiebvELC3DZlmp8vONKDM1ZXMSojhSMqDtRB3VWqFdu0w6ByA6Nmqm32Vm0cNNQqI/KMNK07zGQ7wlOb34xK44QoV4nkPYAwkZriS+cTwmJG943ouxBJ9ngMiaKYDJBB0OXwzCQlnT8Jc6zOHhTZCaqLXLM//n1BqNzfcttgpSIuPZb866UyXyy5UIp3afUp+dGHOdiNBBKos1GDb3o/UHQeHc8MwKbprTBChqXApCoUw3KRwZ3AB4q5RuRGo6LzZf2/PnVpFkFw7bge1o82wWFa9j9YNLvo+EBvmTKPiYU2jFrgPm/47MwOUbDXuwd67zyZ0LhXFO84MbVNkMOv65G6TLvq2+VrTRFYT9J1zJM9n67Kpi7NDM60woRUonJBVrKzPOroJVV6itHqbE7NJGXP2htcm7FvYdZfc36XVs/BbrRQcd1YZnZDA7sqUFxGISC1HsAl5Bd9w8w35Fsejk6bM+KxZGQbYRMgZ1Sfcn02UYMyVYBBM7d+D5bt8MaHjXUzCFXhlbJQh00sOF1jogsBbcTvxP2u12qMwgbdV0LkB1S X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 9927d459-8e42-4581-0b88-08dbe36b2b60 X-MS-Exchange-CrossTenant-AuthSource: TYSPR06MB6433.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Nov 2023 10:36:03.4210 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYSPR06MB6768 Subject: [FFmpeg-devel] [PATCH v5 13/14] vvcdec: add CTU thread logical X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Nuo Mi Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: slc+dGHgtJBZ This is the main entry point for the CTU (Coding Tree Unit) decoder. The code will divide the CTU decoder into several stages. It will check the stage dependencies and run the stage decoder. --- libavcodec/vvc/Makefile | 1 + libavcodec/vvc/vvc_thread.c | 799 ++++++++++++++++++++++++++++++++++++ libavcodec/vvc/vvc_thread.h | 36 ++ 3 files changed, 836 insertions(+) create mode 100644 libavcodec/vvc/vvc_thread.c create mode 100644 libavcodec/vvc/vvc_thread.h diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile index bc663a5b50..dc484e5fb9 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -13,3 +13,4 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ vvc/vvc_mvs.o \ vvc/vvc_ps.o \ vvc/vvc_refs.o \ + vvc/vvc_thread.o \ diff --git a/libavcodec/vvc/vvc_thread.c b/libavcodec/vvc/vvc_thread.c new file mode 100644 index 0000000000..244ae2187a --- /dev/null +++ b/libavcodec/vvc/vvc_thread.c @@ -0,0 +1,799 @@ +/* + * VVC thread logic + * + * Copyright (C) 2023 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "libavutil/executor.h" +#include "libavutil/thread.h" + +#include "vvc_thread.h" +#include "vvc_ctu.h" +#include "vvc_filter.h" +#include "vvc_inter.h" +#include "vvc_intra.h" +#include "vvc_refs.h" + +typedef struct ProgressListener { + VVCProgressListener l; + struct VVCTask *task; + VVCContext *s; +} ProgressListener; + +typedef enum VVCTaskType { + VVC_TASK_TYPE_INIT, + VVC_TASK_TYPE_PARSE, + VVC_TASK_TYPE_INTER, + VVC_TASK_TYPE_RECON, + VVC_TASK_TYPE_LMCS, + VVC_TASK_TYPE_DEBLOCK_V, + VVC_TASK_TYPE_DEBLOCK_H, + VVC_TASK_TYPE_SAO, + VVC_TASK_TYPE_ALF, + VVC_TASK_TYPE_LAST +} VVCTaskType; + +typedef struct VVCTask { + union { + struct VVCTask *next; //for executor debug only + AVTask task; + }; + + VVCTaskType type; + + // ctu x, y in raster order + int rx, ry; + VVCFrameContext *fc; + + ProgressListener col_listener; + ProgressListener listener[2][VVC_MAX_REF_ENTRIES]; + + // for parse task only + SliceContext *sc; + EntryPoint *ep; + int ctu_idx; //ctu idx in the current slice + + // tasks with target scores met are ready for scheduling + atomic_uchar score[VVC_TASK_TYPE_LAST - VVC_TASK_TYPE_PARSE]; + atomic_uchar target_inter_score; +} VVCTask; + +typedef struct VVCRowThread { + atomic_int progress[VVC_PROGRESS_LAST]; +} VVCRowThread; + +typedef struct VVCFrameThread { + // error return for tasks + atomic_int ret; + + VVCRowThread *rows; + VVCTask *tasks; + + int ctu_size; + int ctu_width; + int ctu_height; + int ctu_count; + + //protected by lock + atomic_int nb_scheduled_tasks; + atomic_int nb_scheduled_listeners; + + int row_progress[VVC_PROGRESS_LAST]; + + AVMutex lock; + AVCond cond; +} VVCFrameThread; + +static void add_task(VVCContext *s, VVCTask *t) +{ + VVCFrameThread *ft = t->fc->ft; + + atomic_fetch_add(&ft->nb_scheduled_tasks, 1); + + av_executor_execute(s->executor, &t->task); +} + +static void task_init(VVCTask *t, VVCTaskType type, VVCFrameContext *fc, const int rx, const int ry) +{ + memset(t, 0, sizeof(*t)); + t->type = type; + t->fc = fc; + t->rx = rx; + t->ry = ry; + for (int i = 0; i < FF_ARRAY_ELEMS(t->score); i++) + atomic_store(t->score + i, 0); + atomic_store(&t->target_inter_score, 0); +} + +static void task_init_parse(VVCTask *t, SliceContext *sc, EntryPoint *ep, const int ctu_idx) +{ + t->sc = sc; + t->ep = ep; + t->ctu_idx = ctu_idx; +} + +static uint8_t task_add_score(VVCTask *t, const VVCTaskType type, const uint8_t count) +{ + return atomic_fetch_add(&t->score[type - VVC_TASK_TYPE_PARSE], count); +} + +//first row in tile or slice +static int is_first_row(const VVCFrameContext *fc, const int rx, const int ry) +{ + const VVCFrameThread *ft = fc->ft; + const VVCPPS *pps = fc->ps.pps; + + if (ry != pps->ctb_to_row_bd[ry]) { + const int rs = ry * ft->ctu_width + rx; + return fc->tab.slice_idx[rs] != fc->tab.slice_idx[rs - ft->ctu_width]; + } + return 1; +} + +static int task_has_target_score(VVCTask *t, const VVCTaskType type) +{ + // l:left, r:right, t: top, b: bottom + static const uint8_t target_score[] = + { + 2, //VVC_TASK_TYPE_RECON, need l + rt recon + 3, //VVC_TASK_TYPE_LMCS, need r + b + rb recon + 2, //VVC_TASK_TYPE_DEBLOCK_V, need r lmcs + l deblock v + 2, //VVC_TASK_TYPE_DEBLOCK_H, need r deblock v + t deblock h + 5, //VVC_TASK_TYPE_SAO, need l + r + lb + b + rb deblock h + 8, //VVC_TASK_TYPE_ALF, need sao around the ctu + }; + const uint8_t score = task_add_score(t, type, 1); + uint8_t target = 0; + VVCFrameContext *fc = t->fc; + + if (type == VVC_TASK_TYPE_PARSE) { + const H266RawSPS *rsps = fc->ps.sps->r; + const int wpp = rsps->sps_entropy_coding_sync_enabled_flag && !is_first_row(fc, t->rx, t->ry); + target = 2 + wpp; //left parse + colocation + wpp + } else if (type == VVC_TASK_TYPE_INTER) { + target = atomic_load(&t->target_inter_score); + } else { + target = target_score[type - VVC_TASK_TYPE_RECON]; + } + + av_assert0(score <= target); + + return score == target; +} + +static void frame_thread_add_score(VVCContext *s, VVCFrameThread *ft, + const int rx, const int ry, const VVCTaskType type) +{ + VVCTask *t = ft->tasks + ft->ctu_width * ry + rx; + + if (rx < 0 || rx >= ft->ctu_width || ry < 0 || ry >= ft->ctu_height) + return; + + if (task_has_target_score(t, type)) { + av_assert0(s); + av_assert0(type == t->type + 1); + t->type++; + add_task(s, t); + } +} + +static void sheduled_done(VVCFrameThread *ft, atomic_int *scheduled) +{ + if (atomic_fetch_sub(scheduled, 1) == 1) { + ff_mutex_lock(&ft->lock); + ff_cond_signal(&ft->cond); + ff_mutex_unlock(&ft->lock); + } +} + +static void progress_done(VVCProgressListener *_l, const int type) +{ + const ProgressListener *l = (ProgressListener *)_l; + const VVCTask *t = l->task; + VVCFrameThread *ft = t->fc->ft; + + frame_thread_add_score(l->s, ft, t->rx, t->ry, type); + sheduled_done(ft, &ft->nb_scheduled_listeners); +} + +static void pixel_done(VVCProgressListener *l) +{ + progress_done(l, VVC_TASK_TYPE_INTER); +} + +static void mv_done(VVCProgressListener *l) +{ + progress_done(l, VVC_TASK_TYPE_PARSE); +} + +static void listener_init(ProgressListener *l, VVCTask *t, VVCContext *s, const VVCProgress vp, const int y) +{ + const int is_inter = vp == VVC_PROGRESS_PIXEL; + + l->task = t; + l->s = s; + l->l.vp = vp; + l->l.y = y; + l->l.progress_done = is_inter ? pixel_done : mv_done; + if (is_inter) + atomic_fetch_add(&t->target_inter_score, 1); +} + +static void add_progress_listener(VVCFrame *ref, ProgressListener *l, + VVCTask *t, VVCContext *s, const VVCProgress vp, const int y) +{ + VVCFrameThread *ft = t->fc->ft; + + atomic_fetch_add(&ft->nb_scheduled_listeners, 1); + listener_init(l, t, s, vp, y); + ff_vvc_add_progress_listener(ref, (VVCProgressListener*)l); +} + +static void schedule_next_parse(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc, VVCTask *t) +{ + VVCFrameThread *ft = fc->ft; + EntryPoint *ep = t->ep; + const VVCSPS *sps = fc->ps.sps; + + if (sps->r->sps_entropy_coding_sync_enabled_flag) { + if (t->rx == fc->ps.pps->ctb_to_col_bd[t->rx]) { + EntryPoint *next = ep + 1; + if (next < sc->eps + sc->nb_eps && !is_first_row(fc, t->rx, t->ry + 1)) { + memcpy(next->cabac_state, ep->cabac_state, sizeof(next->cabac_state)); + ff_vvc_ep_init_stat_coeff(next, sps->bit_depth, sps->r->sps_persistent_rice_adaptation_enabled_flag); + } + } + if (t->ry + 1 < ft->ctu_height && !is_first_row(fc, t->rx, t->ry + 1)) + frame_thread_add_score(s, ft, t->rx, t->ry + 1, VVC_TASK_TYPE_PARSE); + } + + if (t->ctu_idx + 1 < t->ep->ctu_end) { + const int next_rs = sc->sh.ctb_addr_in_curr_slice[t->ctu_idx + 1]; + const int next_rx = next_rs % ft->ctu_width; + const int next_ry = next_rs / ft->ctu_width; + frame_thread_add_score(s, ft, next_rx, next_ry, VVC_TASK_TYPE_PARSE); + } +} + +static void schedule_inter(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc, VVCTask *t, const int rs) +{ + const VVCSH *sh = &sc->sh; + + if (!IS_I(sh->r)) { + CTU *ctu = fc->tab.ctus + rs; + for (int lx = 0; lx < 2; lx++) { + for (int i = 0; i < sh->r->num_ref_idx_active[lx]; i++) { + const int y = ctu->max_y[lx][i]; + VVCFrame *ref = sc->rpl[lx].ref[i]; + if (ref && y >= 0) + add_progress_listener(ref, &t->listener[lx][i], t, s, VVC_PROGRESS_PIXEL, y + LUMA_EXTRA_AFTER); + } + } + } +} + +static void parse_task_done(VVCContext *s, VVCFrameContext *fc, const int rx, const int ry) +{ + VVCFrameThread *ft = fc->ft; + const int rs = ry * ft->ctu_width + rx; + const int slice_idx = fc->tab.slice_idx[rs]; + VVCTask *t = ft->tasks + rs; + const SliceContext *sc = fc->slices[slice_idx]; + + schedule_next_parse(s, fc, sc, t); + schedule_inter(s, fc, sc, t, rs); +} + +static void task_state_done(const VVCTask *task, VVCContext *s) +{ + VVCFrameContext *fc = task->fc; + VVCFrameThread *ft = fc->ft; + const int rx = task->rx; + const int ry = task->ry; + const int type = task->type; + +#define ADD(dx, dy, type) frame_thread_add_score(s, ft, rx + (dx), ry + (dy), type) + + //this is a reserve map of ready_score, ordered by zigzag + if (type == VVC_TASK_TYPE_PARSE) { + parse_task_done(s, fc, task->rx, task->ry); + } else if (type == VVC_TASK_TYPE_RECON) { + ADD(-1, 1, VVC_TASK_TYPE_RECON); + ADD( 1, 0, VVC_TASK_TYPE_RECON); + ADD(-1, -1, VVC_TASK_TYPE_LMCS); + ADD( 0, -1, VVC_TASK_TYPE_LMCS); + ADD(-1, 0, VVC_TASK_TYPE_LMCS); + } else if (type == VVC_TASK_TYPE_LMCS) { + ADD(-1, 0, VVC_TASK_TYPE_DEBLOCK_V); + } else if (type == VVC_TASK_TYPE_DEBLOCK_V) { + ADD( 1, 0, VVC_TASK_TYPE_DEBLOCK_V); + ADD(-1, 0, VVC_TASK_TYPE_DEBLOCK_H); + } else if (type == VVC_TASK_TYPE_DEBLOCK_H) { + ADD( 0, 1, VVC_TASK_TYPE_DEBLOCK_H); + ADD(-1, -1, VVC_TASK_TYPE_SAO); + ADD( 0, -1, VVC_TASK_TYPE_SAO); + ADD(-1, 0, VVC_TASK_TYPE_SAO); + ADD( 1, -1, VVC_TASK_TYPE_SAO); + ADD( 1, 0, VVC_TASK_TYPE_SAO); + } else if (type == VVC_TASK_TYPE_SAO) { + ADD(-1, -1, VVC_TASK_TYPE_ALF); + ADD( 0, -1, VVC_TASK_TYPE_ALF); + ADD(-1, 0, VVC_TASK_TYPE_ALF); + ADD( 1, -1, VVC_TASK_TYPE_ALF); + ADD(-1, 1, VVC_TASK_TYPE_ALF); + ADD( 1, 0, VVC_TASK_TYPE_ALF); + ADD( 0, 1, VVC_TASK_TYPE_ALF); + ADD( 1, 1, VVC_TASK_TYPE_ALF); + } + if (type < VVC_TASK_TYPE_ALF) + ADD(0, 0, type + 1); +} + +static int task_ready(const AVTask *_t, void *user_data) +{ + return 1; +} + +#define CHECK(a, b) \ + do { \ + if ((a) != (b)) \ + return (a) < (b); \ + } while (0) + +static int task_priority_higher(const AVTask *_a, const AVTask *_b) +{ + const VVCTask *a = (const VVCTask*)_a; + const VVCTask *b = (const VVCTask*)_b; + + CHECK(a->fc->decode_order, b->fc->decode_order); //decode order + + if (a->type == VVC_TASK_TYPE_PARSE || b->type == VVC_TASK_TYPE_PARSE) { + CHECK(a->type, b->type); + CHECK(a->ry, b->ry); + return a->rx < b->rx; + } + + CHECK(a->rx + a->ry + a->type, b->rx + b->ry + b->type); //zigzag with type + CHECK(a->rx + a->ry, b->rx + b->ry); //zigzag + return a->ry < b->ry; +} + +static int run_parse(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + int ret, rs; + + lc->sc = t->sc; + lc->ep = t->ep; + + rs = t->sc->sh.ctb_addr_in_curr_slice[t->ctu_idx]; + ret = ff_vvc_coding_tree_unit(lc, t->ctu_idx, rs, t->rx, t->ry); + if (ret < 0) + return ret; + + return 0; +} + +static void report_frame_progress(VVCFrameContext *fc, + const int ry, const VVCTaskType type) +{ + VVCFrameThread *ft = fc->ft; + const int ctu_size = ft->ctu_size; + const int idx = type == VVC_TASK_TYPE_INTER ? VVC_PROGRESS_MV : VVC_PROGRESS_PIXEL; + int old; + + if (atomic_fetch_add(&ft->rows[ry].progress[idx], 1) == ft->ctu_width - 1) { + int y; + ff_mutex_lock(&ft->lock); + y = old = ft->row_progress[idx]; + while (y < ft->ctu_height && atomic_load(&ft->rows[y].progress[idx]) == ft->ctu_width) + y++; + if (old != y) { + const int progress = y == ft->ctu_height ? INT_MAX : y * ctu_size; + ft->row_progress[idx] = y; + ff_vvc_report_progress(fc->ref, idx, progress); + } + ff_mutex_unlock(&ft->lock); + } +} + +static int run_inter(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->ft; + const int rs = t->ry * ft->ctu_width + t->rx; + const int slice_idx = fc->tab.slice_idx[rs]; + + if (slice_idx != -1) { + lc->sc = fc->slices[slice_idx]; + ff_vvc_predict_inter(lc, rs); + } + report_frame_progress(fc, t->ry, VVC_TASK_TYPE_INTER); + + return 0; +} + +static int run_recon(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->ft; + const int rs = t->ry * ft->ctu_width + t->rx; + const int slice_idx = fc->tab.slice_idx[rs]; + + if (slice_idx != -1) { + lc->sc = fc->slices[slice_idx]; + ff_vvc_reconstruct(lc, rs, t->rx, t->ry); + } + + return 0; +} + +static int run_lmcs(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->ft; + const int ctu_size = ft->ctu_size; + const int x0 = t->rx * ctu_size; + const int y0 = t->ry * ctu_size; + const int rs = t->ry * ft->ctu_width + t->rx; + const int slice_idx = fc->tab.slice_idx[rs]; + + if (slice_idx != -1) { + lc->sc = fc->slices[slice_idx]; + ff_vvc_lmcs_filter(lc, x0, y0); + } + + return 0; +} + +static int run_deblock_v(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->ft; + const int rs = t->ry * ft->ctu_width + t->rx; + const int ctb_size = ft->ctu_size; + const int x0 = t->rx * ctb_size; + const int y0 = t->ry * ctb_size; + const int slice_idx = fc->tab.slice_idx[rs]; + + if (slice_idx != -1) { + lc->sc = fc->slices[slice_idx]; + if (!lc->sc->sh.r->sh_deblocking_filter_disabled_flag) { + ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs); + ff_vvc_deblock_vertical(lc, x0, y0); + } + } + + return 0; +} + +static int run_deblock_h(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->ft; + const int ctb_size = ft->ctu_size; + const int rs = t->ry * ft->ctu_width + t->rx; + const int x0 = t->rx * ctb_size; + const int y0 = t->ry * ctb_size; + const int slice_idx = fc->tab.slice_idx[rs]; + + if (slice_idx != -1) { + lc->sc = fc->slices[slice_idx]; + if (!lc->sc->sh.r->sh_deblocking_filter_disabled_flag) { + ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs); + ff_vvc_deblock_horizontal(lc, x0, y0); + } + if (fc->ps.sps->r->sps_sao_enabled_flag) + ff_vvc_sao_copy_ctb_to_hv(lc, t->rx, t->ry, t->ry == ft->ctu_height - 1); + } + + return 0; +} + +static int run_sao(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->ft; + const int rs = t->ry * fc->ps.pps->ctb_width + t->rx; + const int ctb_size = ft->ctu_size; + const int x0 = t->rx * ctb_size; + const int y0 = t->ry * ctb_size; + + if (fc->ps.sps->r->sps_sao_enabled_flag) { + ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs); + ff_vvc_sao_filter(lc, x0, y0); + } + + if (fc->ps.sps->r->sps_alf_enabled_flag) + ff_vvc_alf_copy_ctu_to_hv(lc, x0, y0); + + return 0; +} + +static int run_alf(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->ft; + const int ctu_size = ft->ctu_size; + const int x0 = t->rx * ctu_size; + const int y0 = t->ry * ctu_size; + + if (fc->ps.sps->r->sps_alf_enabled_flag) { + const int slice_idx = CTB(fc->tab.slice_idx, t->rx, t->ry); + if (slice_idx != -1) { + const int rs = t->ry * fc->ps.pps->ctb_width + t->rx; + lc->sc = fc->slices[slice_idx]; + ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs); + ff_vvc_alf_filter(lc, x0, y0); + } + } + report_frame_progress(fc, t->ry, VVC_TASK_TYPE_ALF); + + return 0; +} + +#define VVC_THREAD_DEBUG +#ifdef VVC_THREAD_DEBUG +const static char* task_name[] = { + "P", + "I", + "R", + "L", + "V", + "H", + "S", + "A" +}; +#endif + +typedef int (*run_func)(VVCContext *s, VVCLocalContext *lc, VVCTask *t); + +static int task_run(AVTask *_t, void *local_context, void *user_data) +{ + VVCTask *t = (VVCTask*)_t; + VVCContext *s = (VVCContext *)user_data; + VVCLocalContext *lc = local_context; + VVCFrameContext *fc = t->fc; + VVCFrameThread *ft = fc->ft; + const VVCTaskType type = t->type; + const int idx = type - VVC_TASK_TYPE_PARSE; + int ret = 0; + run_func run[] = { + run_parse, + run_inter, + run_recon, + run_lmcs, + run_deblock_v, + run_deblock_h, + run_sao, + run_alf, + }; + + lc->fc = t->fc; + +#ifdef VVC_THREAD_DEBUG + av_log(s->avctx, AV_LOG_DEBUG, "frame %5d, %s(%3d, %3d)\r\n", (int)t->fc->decode_order, task_name[idx], t->rx, t->ry); +#endif + + if (!atomic_load(&ft->ret)) { + if ((ret = run[idx](s, lc, t)) < 0) { +#ifdef COMPAT_ATOMICS_WIN32_STDATOMIC_H + intptr_t zero = 0; +#else + int zero = 0; +#endif + atomic_compare_exchange_strong(&ft->ret, &zero, ret); + av_log(s->avctx, AV_LOG_ERROR, + "frame %5d, %s(%3d, %3d) failed with %d\r\n", + (int)t->fc->decode_order, task_name[idx], t->rx, t->ry, ret); + } + } + + task_state_done(t, s); + sheduled_done(ft, &ft->nb_scheduled_tasks); + + return ret; +} + +AVExecutor* ff_vvc_executor_alloc(VVCContext *s, int thread_count) +{ + AVTaskCallbacks callbacks = { + s, + sizeof(VVCLocalContext), + task_priority_higher, + task_ready, + task_run, + }; + return av_executor_alloc(&callbacks, s->nb_fcs); +} + +void ff_vvc_executor_free(AVExecutor **e) +{ + av_executor_free(e); +} + +void ff_vvc_frame_thread_free(VVCFrameContext *fc) +{ + VVCFrameThread *ft = fc->ft; + + if (!ft) + return; + + ff_mutex_destroy(&ft->lock); + ff_cond_destroy(&ft->cond); + av_freep(&ft->rows); + av_freep(&ft->tasks); + av_freep(&ft); +} + +static void frame_thread_init_score(VVCFrameContext *fc) +{ + const VVCFrameThread *ft = fc->ft; + VVCTask task; + + task_init(&task, VVC_TASK_TYPE_RECON, fc, 0, 0); + + for (int i = VVC_TASK_TYPE_RECON; i < VVC_TASK_TYPE_LAST; i++) { + task.type = i; + + for (task.rx = -1; task.rx <= ft->ctu_width; task.rx++) { + task.ry = -1; //top + task_state_done(&task, NULL); + task.ry = ft->ctu_height; //bottom + task_state_done(&task, NULL); + } + + for (task.ry = 0; task.ry < ft->ctu_height; task.ry++) { + task.rx = -1; //left + task_state_done(&task, NULL); + task.rx = ft->ctu_width; //right + task_state_done(&task, NULL); + } + } +} + +int ff_vvc_frame_thread_init(VVCFrameContext *fc) +{ + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + VVCFrameThread *ft = fc->ft; + int ret; + + if (!ft || ft->ctu_width != pps->ctb_width || + ft->ctu_height != pps->ctb_height || + ft->ctu_size != sps->ctb_size_y) { + + ff_vvc_frame_thread_free(fc); + ft = av_calloc(1, sizeof(*fc->ft)); + if (!ft) + return AVERROR(ENOMEM); + + ft->ctu_width = fc->ps.pps->ctb_width; + ft->ctu_height = fc->ps.pps->ctb_height; + ft->ctu_count = fc->ps.pps->ctb_count; + ft->ctu_size = fc->ps.sps->ctb_size_y; + + ft->rows = av_calloc(ft->ctu_height, sizeof(*ft->rows)); + if (!ft->rows) + goto fail; + + ft->tasks = av_malloc(ft->ctu_count * sizeof(*ft->tasks)); + if (!ft->tasks) + goto fail; + + if ((ret = ff_cond_init(&ft->cond, NULL))) + goto fail; + + if ((ret = ff_mutex_init(&ft->lock, NULL))) { + ff_cond_destroy(&ft->cond); + goto fail; + } + } + + ft->ret = 0; + for (int y = 0; y < ft->ctu_height; y++) { + VVCRowThread *row = ft->rows + y; + memset(row->progress, 0, sizeof(row->progress)); + } + + for (int rs = 0; rs < ft->ctu_count; rs++) { + VVCTask *t = ft->tasks + rs; + task_init(t, VVC_TASK_TYPE_INIT, fc, rs % ft->ctu_width, rs / ft->ctu_width); + } + + memset(&ft->row_progress[0], 0, sizeof(ft->row_progress)); + + fc->ft = ft; + frame_thread_init_score(fc); + + return 0; + +fail: + if (ft) { + av_freep(&ft->rows); + av_freep(&ft->tasks); + av_freep(&ft); + } + + return AVERROR(ENOMEM); +} + +static void check_colocation(VVCContext *s, VVCTask *t) +{ + const VVCFrameContext *fc = t->fc; + + if (fc->ps.ph.r->ph_temporal_mvp_enabled_flag || fc->ps.sps->r->sps_sbtmvp_enabled_flag) { + VVCFrame *col = fc->ref->collocated_ref; + if (col) { + //we depend on bottom and right boundary, do not - 1 for y + const int y = (t->ry << fc->ps.sps->ctb_log2_size_y); + add_progress_listener(col, &t->col_listener, t, s, VVC_PROGRESS_MV, y); + return; + } + } + frame_thread_add_score(s, fc->ft, t->rx, t->ry, VVC_TASK_TYPE_PARSE); +} + +static void submit_entry_point(VVCContext *s, VVCFrameThread *ft, SliceContext *sc, EntryPoint *ep) +{ + const int rs = sc->sh.ctb_addr_in_curr_slice[ep->ctu_start]; + VVCTask *t = ft->tasks + rs; + + frame_thread_add_score(s, ft, t->rx, t->ry, VVC_TASK_TYPE_PARSE); +} + +void ff_vvc_frame_submit(VVCContext *s, VVCFrameContext *fc) +{ + VVCFrameThread *ft = fc->ft; + + for (int i = 0; i < fc->nb_slices; i++) { + SliceContext *sc = fc->slices[i]; + for (int j = 0; j < sc->nb_eps; j++) { + EntryPoint *ep = sc->eps + j; + for (int k = ep->ctu_start; k < ep->ctu_end; k++) { + const int rs = sc->sh.ctb_addr_in_curr_slice[k]; + VVCTask *t = ft->tasks + rs; + + task_init_parse(t, sc, ep, k); + check_colocation(s, t); + task_state_done(t, s); + } + submit_entry_point(s, ft, sc, ep); + } + } +} + +int ff_vvc_frame_wait(VVCContext *s, VVCFrameContext *fc) +{ + VVCFrameThread *ft = fc->ft; + + ff_mutex_lock(&ft->lock); + + while (atomic_load(&ft->nb_scheduled_tasks) || atomic_load(&ft->nb_scheduled_listeners)) + ff_cond_wait(&ft->cond, &ft->lock); + + ff_mutex_unlock(&ft->lock); + ff_vvc_report_frame_finished(fc->ref); + +#ifdef VVC_THREAD_DEBUG + av_log(s->avctx, AV_LOG_DEBUG, "frame %5d done\r\n", (int)fc->decode_order); +#endif + return ft->ret; +} diff --git a/libavcodec/vvc/vvc_thread.h b/libavcodec/vvc/vvc_thread.h new file mode 100644 index 0000000000..acea1ba8e8 --- /dev/null +++ b/libavcodec/vvc/vvc_thread.h @@ -0,0 +1,36 @@ +/* + * VVC thread logic + * + * Copyright (C) 2023 Nuo Mi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VVC_THREAD_H +#define AVCODEC_VVC_THREAD_H + +#include "vvcdec.h" + +struct AVExecutor* ff_vvc_executor_alloc(VVCContext *s, int thread_count); +void ff_vvc_executor_free(struct AVExecutor **e); + +int ff_vvc_frame_thread_init(VVCFrameContext *fc); +void ff_vvc_frame_thread_free(VVCFrameContext *fc); +void ff_vvc_frame_submit(VVCContext *s, VVCFrameContext *fc); +int ff_vvc_frame_wait(VVCContext *s, VVCFrameContext *fc); + +#endif // AVCODEC_VVC_THREAD_H From patchwork Sun Nov 12 10:35:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 44633 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:92a5:b0:181:818d:5e7f with SMTP id q37csp728781pzg; Sun, 12 Nov 2023 02:38:40 -0800 (PST) X-Google-Smtp-Source: AGHT+IG1iAMbq53Uuk4dS4nUzPsSdt4EC4VIzdXMfWnE1Mfar3WPc7OlIlLJbSKxrb1jfu66sPZs X-Received: by 2002:a17:906:a84a:b0:9b2:babb:5fe9 with SMTP id dx10-20020a170906a84a00b009b2babb5fe9mr2495872ejb.23.1699785520202; Sun, 12 Nov 2023 02:38:40 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id r25-20020a170906c29900b009d3f3aa3622si1448026ejz.946.2023.11.12.02.38.39; Sun, 12 Nov 2023 02:38:40 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@outlook.com header.s=selector1 header.b=nQgJFwH0; arc=fail (body hash mismatch); spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 6100168CCC8; Sun, 12 Nov 2023 12:36:31 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from APC01-PSA-obe.outbound.protection.outlook.com (mail-psaapc01olkn2055.outbound.protection.outlook.com [40.92.52.55]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id D7D6268CC59 for ; Sun, 12 Nov 2023 12:36:20 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=VXDAnDrhCpSCWJwk65u9+r0f1vg3V1EOcJYSVZma7OtMXzeTUHdW1et8iegHrmvFDqh4IOdeGMHZcjsVhYL0FoWs0mV2HGEBFhdeSap1get89jvuwyXdu647V2pFC6Va1fmKnS9rUDY9Cp12syE8GaShRwlbMmtA4kGRVT8511iSlxBd96HYmf04p5nyNv3CS/z8qCglcwVp+2KNf6O0hk5Y2au9RDa2QmKzXMRvAujBRNYKKgJuZlgFGKhh3jxFpjKPe8NCi/naaHctTUPYTwM/1nEg17MQkaAmQg/8ns/lbqGguRRiKu245nHDx7ml7NkpsX7vn4v/sgvLPiXA5A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=bLBGixA9qaN7TNWdY8sFd02eSdhwhhHEL7kc2TyzVtM=; b=iUcPlQGHdirZuMdII0zOKgsHCj1yRD07hfG3juSYuJ1dm4PRKQzjugbsDMOujpR2axEi+rjrPRBySwYesUzByOFHjZz98KMR1x1ON43cE/a0j9X7HAeEnxGE6WlwAtPjL54uaCmK/GN1bj+R7zk01DJeSq+l7UmSspHTYRGm1/hiQ6EHC8SXpDdT8qSUcFEGMAtut6s58ig0qQ7A8Uw4NhOsqqESO7EXprCFy16PWihZAFN3wytIYqs8DhMW4/6VQm8+D3opAr1yViR0VTKahAR/EqcJ3WFdOZB94nnk16nWWiY+dXVFKWdli3EVmE465TGrx7p57XA7kfe5R9XH2w== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=outlook.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=bLBGixA9qaN7TNWdY8sFd02eSdhwhhHEL7kc2TyzVtM=; b=nQgJFwH0aVmeZHhyK+4gtDGZs2PcrZKaFhIGIZNNc1Gs7p3/jvOuZf3+TubH86aU+ZP4b8W/bLEJHqQ+zSY8q2zGiLlF7orL6HvjY6VCf239PJORF7FJSndP1rCpts6lnMqzRs/kc3feSfLseMQ/A38mL2xpbLTm2nmd04Gev0EkQCZ9rRvxnAtZTNOxYBWxrRK6NxtZutssBDK+HlDQBb9jHjeBQf3Vnnyl65Km/PEyJvJAtPw9v5HgVmpDXhbOVGEyF8JDq5MBp4Z2xQhXLqzskatzXcwdD0monvDNH63kamZZWk+WXOQoYmapG7/39L2GcZikex2aTPPkMKlSgg== Received: from TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) by TYSPR06MB6768.apcprd06.prod.outlook.com (2603:1096:400:475::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6954.29; Sun, 12 Nov 2023 10:36:04 +0000 Received: from TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f]) by TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f%4]) with mapi id 15.20.6954.028; Sun, 12 Nov 2023 10:36:04 +0000 From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Nov 2023 18:35:26 +0800 Message-ID: X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231112103526.11245-1-nuomi2021@gmail.com> References: <20231112103526.11245-1-nuomi2021@gmail.com> X-TMN: [ag7/ApAWNbwJ0tP80erapzgWleWbkvn3] X-ClientProxiedBy: TYWP286CA0030.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:262::20) To TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) X-Microsoft-Original-Message-ID: <20231112103526.11245-14-nuomi2021@gmail.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 2 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: TYSPR06MB6433:EE_|TYSPR06MB6768:EE_ X-MS-Office365-Filtering-Correlation-Id: c7b52c88-c07c-480a-f600-08dbe36b2bc8 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: MRR0pjCRgkj4rD7VrXag3OcS74jNCvu4XIFONGtj2BCf19soX4i2etFq1JZHTenU2cPUQVjmRzdOULRd7WakC/dO1dAgmOITETerMYfcTa/nOZZQXOgNsSPESVlqIwFQFxWIDhBVZC7TdrjoRlDhHNfSJG9zxsZcmg99yke2bcqTFF3Gm+sQEQUU0UOnQulleSFTOKDoYDlQ70tqryT6hLAah+c2GVLI/hM8ilJ5RGzqs3buO+XqOl0+yCxqeUMZXAftey7igMvl2eXHu++H2CD+sxsWolSSY2wrYBfwyL3jGvvKkBz/ZnT97Fji9lBlo6fMqnSKfAp5BUGtvMMesJ/nvkvRHTGm0ZRNZYkEE91Uinbuf7h1g8VkFQo20iIBCPBXG51ljvnw8s+o/Tn3nu4IktyvKEaUPxgDX1DCMHfzWvQaloxcGW7sCKvrtzg89mw+CagRi3vc5loYs57s1XBbazBpcOGcwkMrwP5W2I7bjuCHxlRHEYVfY7gyiGNEXxPsDAhjxM5ca7AdsxXUM6Oaywv8lEUHiXfjKLO4nDHYUHYUsZi3OfCPj86zQNccNDlfazUXOBQ8bzEmB4GAo8CfZStO+4JtCmJUinHC2BAQG2Y/jwie6C5ooQgfGLbw X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: EDhQbnWTbldabB9AFGHTP5WokfS+rMth4CvjvjnMpIqgjJzmEPxecKblLTp7IYgMHf32PcdjGVmS95V3xTqtAdkqTDGZrpdBpjXnP+CIfInPYQ1utaqFScr/kKjljHMmGQQ0JIfPYG6mxAbzfymRRKEOY8f+qiuKormEngiW2EcL+3Iu7a+6CFlQ1kyMRkwPkAk2yR+dVAIxBT5dt1ECDuQWLNVebU6v2lDxll1P3YZzH/cXnSyQxoR+4BZH/sXzvFHymcfTpaC6JS3H21vR4o3/8jDJ6kdnx76WYOAbVILIDacX9ZAnX7SzPI1AQiJnRGwXNkoWSzrL3Bx242dHi/+aX2e/MV3uCqvjqh1UDVjvJ4hF7us/gEXAQVo7OhFnKLVZC6xKSyv+04ybC3EP3f1p6t9reXAdJm+bMCTljMVat5WKz+YV0GK2e78+2YB/KrXky+TyB6piLen50zFiUjGJy1o9CX4Qx1IaF1MsG+D6xbVuaLspwArwmwAcvkVVVtQKKuPO7thiUSjoQE24gPJSuodHGUNl5G/y2ZbtYDhQi/+8t/iEC5k9cyWQbsxL+P+X3pWT6CAZXdEgJ1V5Zul+NLznv/jJ7E/0sijEoCJdWTbLyUrsqVjRpGEEcuZonjMQWBEtb0W8QjMsNFeuzHtZJz7IzdGnS4fBvz58EbYU62TAtH2ooF8E9EJjP4E9ho5XEDC8PJBkunUE3f8v+WFIDIgyGPOGHyEMHLCqynDBilrvFctGppd8BhTZ2mPlIII1XlEzWgahMpgliIbbn3OBQx5aqyyKYMFHIsAxv6Zo7zkRhN4nC7FPvJRpzuJeVjQNjRFcSXY33RzTkZXtKv/FYEbtSzAXuU7UQFRIydZah2WjH3Ime1zwqE7wMZN1z6eeiQstKNLBGBoBYYYKHnVMg7gud4b+O3bCmW0W8b9iijrbwLdbrrJMpT7jf4zdPpcSDCpwbMB2+K9mPWny+q01OnF4veg44VEOWuNsCl62HUwDSM706CidRoa3A4IVa/BpDmbx2yeFXtT7pIB2dzmHMgPOuktpy0TklpktBhsreTm1gPkcRkuJJN8VzmS054K/cwEMPj9gCuQIP0BFdIHLgcqV6+ICZOClyf9eg2wbTYUSDM/vNZXtY4nR4Nsx0hQi8O0peip6XArddRXgaFS0fcyPxwe8/Tnu+9YIFUkuIQSrbStk/RcT0TwuTrffT2WnhHA7f5tueJR3rtkzPRpzYA1u4nVPKtCnvBGGHfmIQVBNniuCbDvwect+wRUo X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: c7b52c88-c07c-480a-f600-08dbe36b2bc8 X-MS-Exchange-CrossTenant-AuthSource: TYSPR06MB6433.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Nov 2023 10:36:04.1429 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYSPR06MB6768 Subject: [FFmpeg-devel] [PATCH v5 14/14] vvcdec: add full vvc decoder X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Nuo Mi Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 3nH+sCTOGC7C vvc decoder plug-in to avcodec. split frames into slices/tiles and send them to vvc_thread for further decoding reorder and wait for the frame decoding to be done and output the frame Features: + Support I, P, B frames + Support 8/10/12 bits, chroma 400, 420, 422, and 444 and range extension + Support VVC new tools like MIP, CCLM, AFFINE, GPM, DMVR, PROF, BDOF, LMCS, ALF + 295 conformace clips passed - Not support RPR, IBC, PALETTE, and other minor features yet Performance: C code FPS on i7-12700 (x86): BQTerrace_1920x1080_60_10_420_22_RA.vvc 93.0 Chimera_8bit_1080P_1000_frames.vvc 184.3 NovosobornayaSquare_1920x1080.bin 191.3 RitualDance_1920x1080_60_10_420_32_LD.266 150.7 RitualDance_1920x1080_60_10_420_37_RA.266 170.0 Tango2_3840x2160_60_10_420_27_LD.266 33.7 C code FPS on M1 Mac Pro (ARM): BQTerrace_1920x1080_60_10_420_22_RA.vvc 58.7 Chimera_8bit_1080P_1000_frames.vvc 153.3 NovosobornayaSquare_1920x1080.bin 150.3 RitualDance_1920x1080_60_10_420_32_LD.266 105.0 RitualDance_1920x1080_60_10_420_37_RA.266 133.0 Tango2_3840x2160_60_10_420_27_LD.266 21.7 Asm optimizations still working in progress. please check https://github.com/ffvvc/FFmpeg/wiki#performance-data for the latest Contributors(based on code merge order): Nuo Mi Xu Mu frankplow Shaun Loo --- libavcodec/vvc/vvcdec.c | 1007 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 1007 insertions(+) diff --git a/libavcodec/vvc/vvcdec.c b/libavcodec/vvc/vvcdec.c index 3c591ce875..e40eb7339f 100644 --- a/libavcodec/vvc/vvcdec.c +++ b/libavcodec/vvc/vvcdec.c @@ -21,28 +21,1035 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavcodec/codec_internal.h" +#include "libavcodec/decode.h" #include "libavcodec/profiles.h" +#include "libavcodec/refstruct.h" +#include "libavutil/cpu.h" #include "vvcdec.h" +#include "vvc_ctu.h" +#include "vvc_data.h" +#include "vvc_refs.h" +#include "vvc_thread.h" + +static int vvc_frame_start(VVCContext *s, VVCFrameContext *fc, SliceContext *sc) +{ + const VVCPH *ph = &fc->ps.ph; + const H266RawSliceHeader *rsh = sc->sh.r; + int ret; + + // 8.3.1 Decoding process for picture order count + if (!s->temporal_id && !ph->r->ph_non_ref_pic_flag && !(IS_RASL(s) || IS_RADL(s))) + s->poc_tid0 = ph->poc; + + if ((ret = ff_vvc_set_new_ref(s, fc, &fc->frame)) < 0) + goto fail; + + if (!IS_IDR(s)) + ff_vvc_bump_frame(s, fc); + + av_frame_unref(fc->output_frame); + + if ((ret = ff_vvc_output_frame(s, fc, fc->output_frame,rsh->sh_no_output_of_prior_pics_flag, 0)) < 0) + goto fail; + + if ((ret = ff_vvc_frame_rpl(s, fc, sc)) < 0) + goto fail; + + if ((ret = ff_vvc_frame_thread_init(fc)) < 0) + goto fail; + return 0; +fail: + if (fc->ref) + ff_vvc_unref_frame(fc, fc->ref, ~0); + fc->ref = NULL; + return ret; +} + +static void ctb_arrays_free(VVCFrameContext *fc) +{ + av_freep(&fc->tab.deblock); + av_freep(&fc->tab.sao); + av_freep(&fc->tab.alf); + av_freep(&fc->tab.slice_idx); + av_freep(&fc->tab.coeffs); + if (fc->tab.ctus) { + for (int i = 0; i < fc->tab.ctu_count; i++) + ff_vvc_ctu_free_cus(fc->tab.ctus + i); + av_freep(&fc->tab.ctus); + } + ff_refstruct_pool_uninit(&fc->rpl_tab_pool); +} + +static int ctb_arrays_init(VVCFrameContext *fc, const int ctu_count, const int ctu_size) +{ + if (fc->tab.ctu_count != ctu_count || fc->tab.ctu_size != ctu_size) { + ctb_arrays_free(fc); + fc->tab.deblock = av_calloc(ctu_count, sizeof(*fc->tab.deblock)); + fc->tab.sao = av_calloc(ctu_count, sizeof(*fc->tab.sao)); + fc->tab.alf = av_calloc(ctu_count, sizeof(*fc->tab.alf)); + fc->tab.ctus = av_calloc(ctu_count, sizeof(*fc->tab.ctus)); + fc->tab.slice_idx = av_malloc(ctu_count * sizeof(*fc->tab.slice_idx)); + if (!fc->tab.deblock || !fc->tab.sao || !fc->tab.alf || !fc->tab.ctus || !fc->tab.slice_idx ) + return AVERROR(ENOMEM); + fc->tab.coeffs = av_malloc(ctu_count * sizeof(*fc->tab.coeffs) * ctu_size * VVC_MAX_SAMPLE_ARRAYS); + if (!fc->tab.coeffs) + return AVERROR(ENOMEM); + fc->rpl_tab_pool = ff_refstruct_pool_alloc(ctu_count * sizeof(RefPicListTab), 0); + if (!fc->rpl_tab_pool) + return AVERROR(ENOMEM); + } else { + memset(fc->tab.deblock, 0, ctu_count * sizeof(*fc->tab.deblock)); + memset(fc->tab.sao, 0, ctu_count * sizeof(*fc->tab.sao)); + memset(fc->tab.alf, 0, ctu_count * sizeof(*fc->tab.alf)); + for (int i = 0; i < fc->tab.ctu_count; i++) + ff_vvc_ctu_free_cus(fc->tab.ctus + i); + memset(fc->tab.ctus, 0, ctu_count * sizeof(*fc->tab.ctus)); + } + memset(fc->tab.slice_idx, -1, ctu_count * sizeof(*fc->tab.slice_idx)); + + return 0; +} + +static void min_cb_arrays_free(VVCFrameContext *fc) +{ + for (int i = LUMA; i <= CHROMA; i++) { + av_freep(&fc->tab.cb_pos_x[i]); + av_freep(&fc->tab.cb_pos_y[i]); + av_freep(&fc->tab.cb_width[i]); + av_freep(&fc->tab.cb_height[i]); + av_freep(&fc->tab.cqt_depth[i]); + av_freep(&fc->tab.cpm[i]); + av_freep(&fc->tab.cp_mv[i]); + } + + av_freep(&fc->tab.ipm); + av_freep(&fc->tab.imf); + av_freep(&fc->tab.imtf); + av_freep(&fc->tab.imm); + av_freep(&fc->tab.skip); +} + +static int min_cb_arrays_init(VVCFrameContext *fc, const int pic_size_in_min_cb) +{ + if (fc->tab.pic_size_in_min_cb != pic_size_in_min_cb) { + min_cb_arrays_free(fc); + for (int i = LUMA; i <= CHROMA; i++) { + fc->tab.cb_pos_x[i] = av_mallocz(pic_size_in_min_cb * sizeof(int)); + fc->tab.cb_pos_y[i] = av_mallocz(pic_size_in_min_cb * sizeof(int)); + fc->tab.cb_width[i] = av_mallocz(pic_size_in_min_cb); + fc->tab.cb_height[i] = av_mallocz(pic_size_in_min_cb); + fc->tab.cqt_depth[i] = av_mallocz(pic_size_in_min_cb); + if (!fc->tab.cb_pos_x[i] || !fc->tab.cb_pos_y[i] || !fc->tab.cb_width[i] || !fc->tab.cb_height[i] || !fc->tab.cqt_depth[i]) + return AVERROR(ENOMEM); + + fc->tab.cpm[i] = av_mallocz(pic_size_in_min_cb); + fc->tab.cp_mv[i] = av_mallocz(pic_size_in_min_cb * sizeof(Mv) * MAX_CONTROL_POINTS); + if (!fc->tab.cpm[i] || !fc->tab.cp_mv[i]) + return AVERROR(ENOMEM); + } + + fc->tab.ipm = av_mallocz(pic_size_in_min_cb); + fc->tab.imf = av_mallocz(pic_size_in_min_cb); + fc->tab.imtf = av_mallocz(pic_size_in_min_cb); + fc->tab.imm = av_mallocz(pic_size_in_min_cb); + fc->tab.skip = av_mallocz(pic_size_in_min_cb); + if (!fc->tab.ipm || !fc->tab.imf || !fc->tab.imtf || !fc->tab.imm || !fc->tab.skip) + return AVERROR(ENOMEM); + } else { + for (int i = LUMA; i <= CHROMA; i++) { + memset(fc->tab.cb_pos_x[i], 0, pic_size_in_min_cb * sizeof(int)); + memset(fc->tab.cb_pos_y[i], 0, pic_size_in_min_cb * sizeof(int)); + memset(fc->tab.cb_width[i], 0, pic_size_in_min_cb); + memset(fc->tab.cb_height[i], 0, pic_size_in_min_cb); + memset(fc->tab.cqt_depth[i], 0, pic_size_in_min_cb); + memset(fc->tab.cpm[i], 0, pic_size_in_min_cb); + memset(fc->tab.cp_mv[i], 0, pic_size_in_min_cb * sizeof(Mv) * MAX_CONTROL_POINTS); + } + + memset(fc->tab.ipm, 0, pic_size_in_min_cb); + memset(fc->tab.imf, 0, pic_size_in_min_cb); + memset(fc->tab.imtf, 0, pic_size_in_min_cb); + memset(fc->tab.imm, 0, pic_size_in_min_cb); + memset(fc->tab.skip, 0, pic_size_in_min_cb); + } + return 0; +} + +static void min_tu_arrays_free(VVCFrameContext *fc) +{ + for (int i = LUMA; i <= CHROMA; i++) { + av_freep(&fc->tab.tb_pos_x0[i]); + av_freep(&fc->tab.tb_pos_y0[i]); + av_freep(&fc->tab.tb_width[i]); + av_freep(&fc->tab.tb_height[i]); + av_freep(&fc->tab.pcmf[i]); + } + + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) { + av_freep(&fc->tab.qp[i]); + av_freep(&fc->tab.tu_coded_flag[i]); + } + + av_freep(&fc->tab.tu_joint_cbcr_residual_flag); +} + +static int min_tu_arrays_init(VVCFrameContext *fc, const int pic_size_in_min_tu) +{ + if (fc->tab.pic_size_in_min_tu != pic_size_in_min_tu) { + min_tu_arrays_free(fc); + for (int i = LUMA; i <= CHROMA; i++) { + fc->tab.tb_pos_x0[i] = av_mallocz(pic_size_in_min_tu * sizeof(*fc->tab.tb_pos_x0[0])); + fc->tab.tb_pos_y0[i] = av_mallocz(pic_size_in_min_tu * sizeof(*fc->tab.tb_pos_y0[0])) ; + fc->tab.tb_width[i] = av_mallocz(pic_size_in_min_tu); + fc->tab.tb_height[i] = av_mallocz(pic_size_in_min_tu); + fc->tab.pcmf[i] = av_mallocz(pic_size_in_min_tu); + if (!fc->tab.tb_pos_x0[i] || !fc->tab.tb_pos_y0[i] || + !fc->tab.tb_width[i] || !fc->tab.tb_height[i] || !fc->tab.pcmf[i]) + return AVERROR(ENOMEM); + } + + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) { + fc->tab.tu_coded_flag[i] = av_mallocz(pic_size_in_min_tu); + if (!fc->tab.tu_coded_flag[i]) + return AVERROR(ENOMEM); + + fc->tab.qp[i] = av_mallocz(pic_size_in_min_tu); + if (!fc->tab.qp[i]) + return AVERROR(ENOMEM); + } + + fc->tab.tu_joint_cbcr_residual_flag = av_mallocz(pic_size_in_min_tu); + if (!fc->tab.tu_joint_cbcr_residual_flag) + return AVERROR(ENOMEM); + } else { + for (int i = LUMA; i <= CHROMA; i++) { + memset(fc->tab.tb_pos_x0[i], 0, pic_size_in_min_tu * sizeof(*fc->tab.tb_pos_x0[0])); + memset(fc->tab.tb_pos_y0[i], 0, pic_size_in_min_tu * sizeof(*fc->tab.tb_pos_y0[0])) ; + memset(fc->tab.tb_width[i], 0, pic_size_in_min_tu); + memset(fc->tab.tb_height[i], 0, pic_size_in_min_tu); + memset(fc->tab.pcmf[i], 0, pic_size_in_min_tu); + } + + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) { + memset(fc->tab.tu_coded_flag[i], 0, pic_size_in_min_tu); + memset(fc->tab.qp[i], 0, pic_size_in_min_tu); + } + memset(fc->tab.tu_joint_cbcr_residual_flag, 0, pic_size_in_min_tu); + } + return 0; +} + +static void min_pu_arrays_free(VVCFrameContext *fc) +{ + av_freep(&fc->tab.mvf); + av_freep(&fc->tab.msf); + av_freep(&fc->tab.iaf); + av_freep(&fc->tab.mmi); + ff_refstruct_pool_uninit(&fc->tab_dmvr_mvf_pool); +} + +static int min_pu_arrays_init(VVCFrameContext *fc, const int pic_size_in_min_pu) +{ + if (fc->tab.pic_size_in_min_pu != pic_size_in_min_pu) { + min_pu_arrays_free(fc); + fc->tab.msf = av_mallocz(pic_size_in_min_pu); + fc->tab.iaf = av_mallocz(pic_size_in_min_pu); + fc->tab.mmi = av_mallocz(pic_size_in_min_pu); + fc->tab.mvf = av_mallocz(pic_size_in_min_pu * sizeof(*fc->tab.mvf)); + if (!fc->tab.msf || !fc->tab.iaf || !fc->tab.mmi || !fc->tab.mvf) + return AVERROR(ENOMEM); + fc->tab_dmvr_mvf_pool = ff_refstruct_pool_alloc(pic_size_in_min_pu * sizeof(MvField), FF_REFSTRUCT_POOL_FLAG_ZERO_EVERY_TIME); + if (!fc->tab_dmvr_mvf_pool) + return AVERROR(ENOMEM); + } else { + memset(fc->tab.msf, 0, pic_size_in_min_pu); + memset(fc->tab.iaf, 0, pic_size_in_min_pu); + memset(fc->tab.mmi, 0, pic_size_in_min_pu); + memset(fc->tab.mvf, 0, pic_size_in_min_pu * sizeof(*fc->tab.mvf)); + } + + return 0; +} + +static void bs_arrays_free(VVCFrameContext *fc) +{ + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) { + av_freep(&fc->tab.horizontal_bs[i]); + av_freep(&fc->tab.vertical_bs[i]); + } + av_freep(&fc->tab.horizontal_q); + av_freep(&fc->tab.horizontal_p); + av_freep(&fc->tab.vertical_p); + av_freep(&fc->tab.vertical_q); +} + +static int bs_arrays_init(VVCFrameContext *fc, const int bs_width, const int bs_height) +{ + if (fc->tab.bs_width != bs_width || fc->tab.bs_height != bs_height) { + bs_arrays_free(fc); + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) { + fc->tab.horizontal_bs[i] = av_calloc(bs_width, bs_height); + fc->tab.vertical_bs[i] = av_calloc(bs_width, bs_height); + if (!fc->tab.horizontal_bs[i] || !fc->tab.vertical_bs[i]) + return AVERROR(ENOMEM); + } + fc->tab.horizontal_q = av_calloc(bs_width, bs_height); + fc->tab.horizontal_p = av_calloc(bs_width, bs_height); + fc->tab.vertical_p = av_calloc(bs_width, bs_height); + fc->tab.vertical_q = av_calloc(bs_width, bs_height); + if (!fc->tab.horizontal_q || !fc->tab.horizontal_p || !fc->tab.vertical_p || !fc->tab.vertical_q) + return AVERROR(ENOMEM); + } else { + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) { + memset(fc->tab.horizontal_bs[i], 0, bs_width * bs_height); + memset(fc->tab.vertical_bs[i], 0, bs_width * bs_height); + } + memset(fc->tab.horizontal_q, 0, bs_width * bs_height); + memset(fc->tab.horizontal_p, 0, bs_width * bs_height); + memset(fc->tab.vertical_p, 0, bs_width * bs_height); + memset(fc->tab.vertical_q, 0, bs_width * bs_height); + } + return 0; +} + +static void pixel_buffer_free(VVCFrameContext *fc) +{ + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) { + av_freep(&fc->tab.sao_pixel_buffer_h[i]); + av_freep(&fc->tab.sao_pixel_buffer_v[i]); + for (int j = 0; j < 2; j++) { + av_freep(&fc->tab.alf_pixel_buffer_h[i][j]); + av_freep(&fc->tab.alf_pixel_buffer_v[i][j]); + } + } +} + +static int pixel_buffer_init(VVCFrameContext *fc, const int width, const int height, + const int ctu_width, const int ctu_height, const int chroma_format_idc, const int ps) +{ + const VVCSPS *sps = fc->ps.sps; + const int c_end = chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; + + if (fc->tab.chroma_format_idc != chroma_format_idc || + fc->tab.width != width || fc->tab.height != height || + fc->tab.ctu_width != ctu_width || fc->tab.ctu_height != ctu_height) { + pixel_buffer_free(fc); + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int w = width >> sps->hshift[c_idx]; + const int h = height >> sps->vshift[c_idx]; + fc->tab.sao_pixel_buffer_h[c_idx] = av_malloc((w * 2 * ctu_height) << ps); + fc->tab.sao_pixel_buffer_v[c_idx] = av_malloc((h * 2 * ctu_width) << ps); + if (!fc->tab.sao_pixel_buffer_h[c_idx] || !fc->tab.sao_pixel_buffer_v[c_idx]) + return AVERROR(ENOMEM); + } + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int w = width >> sps->hshift[c_idx]; + const int h = height >> sps->vshift[c_idx]; + const int border_pixels = c_idx ? ALF_BORDER_CHROMA : ALF_BORDER_LUMA; + for (int i = 0; i < 2; i++) { + fc->tab.alf_pixel_buffer_h[c_idx][i] = av_malloc((w * border_pixels * ctu_height) << ps); + fc->tab.alf_pixel_buffer_v[c_idx][i] = av_malloc(h * ALF_PADDING_SIZE * ctu_width); + if (!fc->tab.alf_pixel_buffer_h[c_idx][i] || !fc->tab.alf_pixel_buffer_v[c_idx][i]) + return AVERROR(ENOMEM); + } + } + } + return 0; +} + +static void pic_arrays_free(VVCFrameContext *fc) +{ + ctb_arrays_free(fc); + min_cb_arrays_free(fc); + min_pu_arrays_free(fc); + min_tu_arrays_free(fc); + bs_arrays_free(fc); + ff_refstruct_pool_uninit(&fc->cu_pool); + ff_refstruct_pool_uninit(&fc->tu_pool); + pixel_buffer_free(fc); + + for (int i = 0; i < 2; i++) + av_freep(&fc->tab.msm[i]); + av_freep(&fc->tab.ispmf); + + fc->tab.ctu_count = 0; + fc->tab.ctu_size = 0; + fc->tab.pic_size_in_min_cb = 0; + fc->tab.pic_size_in_min_pu = 0; + fc->tab.pic_size_in_min_tu = 0; + fc->tab.width = 0; + fc->tab.height = 0; + fc->tab.ctu_width = 0; + fc->tab.ctu_height = 0; + fc->tab.bs_width = 0; + fc->tab.bs_height = 0; +} + +static int pic_arrays_init(VVCContext *s, VVCFrameContext *fc) +{ + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int ctu_size = 1 << sps->ctb_log2_size_y << sps->ctb_log2_size_y; + const int pic_size_in_min_cb = pps->min_cb_width * pps->min_cb_height; + const int pic_size_in_min_pu = pps->min_pu_width * pps->min_pu_height; + const int pic_size_in_min_tu = pps->min_tu_width * pps->min_tu_height; + const int w32 = AV_CEIL_RSHIFT(pps->width, 5); + const int h32 = AV_CEIL_RSHIFT(pps->height, 5); + const int w64 = AV_CEIL_RSHIFT(pps->width, 6); + const int h64 = AV_CEIL_RSHIFT(pps->height, 6); + const int bs_width = (fc->ps.pps->width >> 2) + 1; + const int bs_height = (fc->ps.pps->height >> 2) + 1; + int ret; + + if ((ret = ctb_arrays_init(fc, pps->ctb_count, ctu_size)) < 0) + goto fail; + + if ((ret = min_cb_arrays_init(fc, pic_size_in_min_cb)) < 0) + goto fail; + + if ((ret = min_pu_arrays_init(fc, pic_size_in_min_pu)) < 0) + goto fail; + + if ((ret = min_tu_arrays_init(fc, pic_size_in_min_tu)) < 0) + goto fail; + + if ((ret = bs_arrays_init(fc, bs_width, bs_height)) < 0) + goto fail; + + if ((ret = pixel_buffer_init(fc, pps->width, pps->height, pps->ctb_width, pps->ctb_height, + sps->r->sps_chroma_format_idc, sps->pixel_shift)) < 0) + goto fail; + + if (AV_CEIL_RSHIFT(fc->tab.width, 5) != w32 || AV_CEIL_RSHIFT(fc->tab.height, 5) != h32) { + for (int i = LUMA; i <= CHROMA; i++) { + av_freep(&fc->tab.msm[i]); + fc->tab.msm[i] = av_calloc(w32, h32); + if (!fc->tab.msm[i]) + goto fail; + } + } else { + for (int i = LUMA; i <= CHROMA; i++) + memset(fc->tab.msm[i], 0, w32 * h32); + } + if (AV_CEIL_RSHIFT(fc->tab.width, 6) != w64 || AV_CEIL_RSHIFT(fc->tab.height, 6) != h64) { + av_freep(&fc->tab.ispmf); + fc->tab.ispmf = av_calloc(w64, h64); + if (!fc->tab.ispmf) + goto fail; + } else { + memset(fc->tab.ispmf, 0, w64 * h64); + } + + if (!fc->cu_pool) { + fc->cu_pool = ff_refstruct_pool_alloc(sizeof(CodingUnit), 0); + if (!fc->cu_pool) + goto fail; + } + + if (!fc->tu_pool) { + fc->tu_pool = ff_refstruct_pool_alloc(sizeof(TransformUnit), 0); + if (!fc->tu_pool) + goto fail; + } + + fc->tab.ctu_count = pps->ctb_count; + fc->tab.ctu_size = ctu_size; + fc->tab.pic_size_in_min_cb = pic_size_in_min_cb; + fc->tab.pic_size_in_min_pu = pic_size_in_min_pu; + fc->tab.pic_size_in_min_tu = pic_size_in_min_tu; + fc->tab.width = pps->width; + fc->tab.height = pps->height; + fc->tab.ctu_width = pps->ctb_width; + fc->tab.ctu_height = pps->ctb_height; + fc->tab.chroma_format_idc = sps->r->sps_chroma_format_idc; + fc->tab.pixel_shift = sps->pixel_shift; + fc->tab.bs_width = bs_width; + fc->tab.bs_height = bs_height; + + return 0; +fail: + pic_arrays_free(fc); + return ret; +} + +static int min_positive(const int idx, const int diff, const int min_diff) +{ + return diff > 0 && (idx < 0 || diff < min_diff); +} + +static int max_negtive(const int idx, const int diff, const int max_diff) +{ + return diff < 0 && (idx < 0 || diff > max_diff); +} + +typedef int (*smvd_find_fxn)(const int idx, const int diff, const int old_diff); + +static int8_t smvd_find(const VVCFrameContext *fc, const SliceContext *sc, int lx, smvd_find_fxn find) +{ + const H266RawSliceHeader *rsh = sc->sh.r; + const RefPicList *rpl = sc->rpl + lx; + const int poc = fc->ref->poc; + int8_t idx = -1; + int old_diff = -1; + for (int i = 0; i < rsh->num_ref_idx_active[lx]; i++) { + if (!rpl->isLongTerm[i]) { + int diff = poc - rpl->list[i]; + if (find(idx, diff, old_diff)) { + idx = i; + old_diff = diff; + } + } + } + return idx; +} + +static void vvc_smvd_ref_idx(const VVCFrameContext *fc, SliceContext *sc) +{ + VVCSH *sh = &sc->sh; + if (IS_B(sh->r)) { + sh->ref_idx_sym[0] = smvd_find(fc, sc, 0, min_positive); + sh->ref_idx_sym[1] = smvd_find(fc, sc, 1, max_negtive); + if (sh->ref_idx_sym[0] == -1 || sh->ref_idx_sym[1] == -1) { + sh->ref_idx_sym[0] = smvd_find(fc, sc, 0, max_negtive); + sh->ref_idx_sym[1] = smvd_find(fc, sc, 1, min_positive); + } + } +} + +static void eps_free(SliceContext *slice) +{ + av_freep(&slice->eps); +} + +static void slices_free(VVCFrameContext *fc) +{ + if (fc->slices) { + for (int i = 0; i < fc->nb_slices_allocated; i++) { + SliceContext *slice = fc->slices[i]; + if (slice) { + ff_refstruct_unref(&slice->sh.r); + eps_free(slice); + av_free(slice); + } + } + av_freep(&fc->slices); + } + fc->nb_slices_allocated = 0; + fc->nb_slices = 0; +} + +static int slices_realloc(VVCFrameContext *fc) +{ + void *p; + const int size = (fc->nb_slices_allocated + 1) * 3 / 2; + + if (fc->nb_slices < fc->nb_slices_allocated) + return 0; + + p = av_realloc(fc->slices, size * sizeof(*fc->slices)); + if (!p) + return AVERROR(ENOMEM); + + fc->slices = p; + for (int i = fc->nb_slices_allocated; i < size; i++) { + fc->slices[i] = av_calloc(1, sizeof(*fc->slices[0])); + if (!fc->slices[i]) { + for (int j = fc->nb_slices_allocated; j < i; j++) + av_freep(&fc->slices[j]); + return AVERROR(ENOMEM); + } + fc->slices[i]->slice_idx = i; + } + fc->nb_slices_allocated = size; + return 0; +} + +static void ep_init_cabac_decoder(SliceContext *sc, const int index, const H2645NAL *nal, GetBitContext *gb) +{ + const H266RawSliceHeader *rsh = sc->sh.r; + EntryPoint *ep = sc->eps + index; + int size; + + if (index < rsh->num_entry_points) { + int skipped = 0; + int64_t start = (gb->index >> 3); + int64_t end = start + rsh->sh_entry_point_offset_minus1[index] + 1; + while (skipped < nal->skipped_bytes && nal->skipped_bytes_pos[skipped] <= start) { + skipped++; + } + while (skipped < nal->skipped_bytes && nal->skipped_bytes_pos[skipped] < end) { + end--; + skipped++; + } + size = end - start; + } else { + size = get_bits_left(gb) / 8; + } + ff_init_cabac_decoder (&ep->cc, gb->buffer + get_bits_count(gb) / 8, size); + skip_bits(gb, size * 8); +} + +static int init_slice_context(SliceContext *sc, VVCFrameContext *fc, const H2645NAL *nal, const CodedBitstreamUnit *unit) +{ + const VVCSH *sh = &sc->sh; + const H266RawSlice *slice = (const H266RawSlice *)unit->content; + int nb_eps = sh->r->num_entry_points + 1; + int ctu_addr = 0; + GetBitContext gb; + + if (sc->nb_eps != nb_eps) { + eps_free(sc); + sc->eps = av_calloc(nb_eps, sizeof(*sc->eps)); + if (!sc->eps) + return AVERROR(ENOMEM); + sc->nb_eps = nb_eps; + } + + init_get_bits8(&gb, slice->data, slice->data_size); + for (int i = 0; i < sc->nb_eps; i++) + { + EntryPoint *ep = sc->eps + i; + + ep->ctu_start = ctu_addr; + ep->ctu_end = (i + 1 == sc->nb_eps ? sh->num_ctus_in_curr_slice : sh->entry_point_start_ctu[i]); + + for (int j = ep->ctu_start; j < ep->ctu_end; j++) { + const int rs = sc->sh.ctb_addr_in_curr_slice[j]; + fc->tab.slice_idx[rs] = sc->slice_idx; + } + + ep_init_cabac_decoder(sc, i, nal, &gb); + + if (i + 1 < sc->nb_eps) + ctu_addr = sh->entry_point_start_ctu[i]; + } + + return 0; +} + +static VVCFrameContext* get_frame_context(const VVCContext *s, const VVCFrameContext *fc, const int delta) +{ + const int size = s->nb_fcs; + const int idx = (fc - s->fcs + delta + size) % size; + return s->fcs + idx; +} + +static int vvc_ref_frame(VVCFrameContext *fc, VVCFrame *dst, VVCFrame *src) +{ + int ret; + + ret = av_frame_ref(dst->frame, src->frame); + if (ret < 0) + return ret; + + ff_refstruct_replace(&dst->progress, src->progress); + + ff_refstruct_replace(&dst->tab_dmvr_mvf, src->tab_dmvr_mvf); + + ff_refstruct_replace(&dst->rpl_tab, src->rpl_tab); + ff_refstruct_replace(&dst->rpl, src->rpl); + dst->nb_rpl_elems = src->nb_rpl_elems; + + dst->poc = src->poc; + dst->ctb_count = src->ctb_count; + dst->flags = src->flags; + dst->sequence = src->sequence; + + return 0; +} + +static av_cold void frame_context_free(VVCFrameContext *fc) +{ + slices_free(fc); + + for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + ff_vvc_unref_frame(fc, &fc->DPB[i], ~0); + av_frame_free(&fc->DPB[i].frame); + } + + ff_vvc_frame_thread_free(fc); + pic_arrays_free(fc); + av_frame_free(&fc->output_frame); + ff_vvc_frame_ps_free(&fc->ps); + av_freep(&fc->avctx); +} + +static av_cold int frame_context_init(VVCFrameContext *fc, AVCodecContext *avctx) +{ + + fc->avctx = av_memdup(avctx, sizeof(*avctx)); + if (!fc->avctx) + goto fail; + + fc->output_frame = av_frame_alloc(); + if (!fc->output_frame) + goto fail; + + for (int j = 0; j < FF_ARRAY_ELEMS(fc->DPB); j++) { + fc->DPB[j].frame = av_frame_alloc(); + if (!fc->DPB[j].frame) + goto fail; + } + + return 0; +fail: + return AVERROR(ENOMEM); +} + +static int frame_context_setup(VVCFrameContext *fc, VVCContext *s) +{ + int ret = 0; + + // copy refs from the last frame + if (s->nb_frames && s->nb_fcs > 1) { + VVCFrameContext *prev = get_frame_context(s, fc, -1); + for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + ff_vvc_unref_frame(fc, &fc->DPB[i], ~0); + if (prev->DPB[i].frame->buf[0]) { + ret = vvc_ref_frame(fc, &fc->DPB[i], &prev->DPB[i]); + if (ret < 0) + goto fail; + } + } + } + + if (IS_IDR(s)) { + s->seq_decode = (s->seq_decode + 1) & 0xff; + ff_vvc_clear_refs(fc); + } + + ret = pic_arrays_init(s, fc); + if (ret < 0) + goto fail; + ff_vvc_dsp_init(&fc->vvcdsp, fc->ps.sps->bit_depth); + ff_videodsp_init(&fc->vdsp, fc->ps.sps->bit_depth); + +fail: + return ret; +} + +static void export_frame_params(VVCFrameContext *fc) +{ + AVCodecContext *c = fc->avctx; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + + c->pix_fmt = sps->pix_fmt; + c->coded_width = pps->width; + c->coded_height = pps->height; + c->width = pps->width - pps->r->pps_conf_win_left_offset - pps->r->pps_conf_win_right_offset; + c->height = pps->height - pps->r->pps_conf_win_top_offset - pps->r->pps_conf_win_bottom_offset; +} + +static int decode_slice(VVCContext *s, VVCFrameContext *fc, const H2645NAL *nal, const CodedBitstreamUnit *unit) +{ + int ret = 0; + SliceContext *sc; + VVCSH *sh; + const int is_first_slice = !fc->nb_slices; + + ret = slices_realloc(fc); + if (ret < 0) + return ret; + sc = fc->slices[fc->nb_slices]; + + sh = &sc->sh; + + if (ret < 0) + goto fail; + + s->vcl_unit_type = nal->type; + if (is_first_slice) { + //first slice + ret = ff_vvc_decode_frame_ps(&fc->ps, s); + if (ret < 0) + return ret; + + ret = frame_context_setup(fc, s); + if (ret < 0) + goto fail; + + export_frame_params(fc); + } + + ret = ff_vvc_decode_sh(&sc->sh, &fc->ps, unit); + if (ret < 0) + return ret; + + if (is_first_slice) { + ret = vvc_frame_start(s, fc, sc); + if (ret < 0) + return ret; + } else if (fc->ref) { + if (!IS_I(sh->r)) { + ret = ff_vvc_slice_rpl(s, fc, sc); + if (ret < 0) { + av_log(fc->avctx, AV_LOG_WARNING, + "Error constructing the reference lists for the current slice.\n"); + return ret; + } + } + } else { + av_log(fc->avctx, AV_LOG_ERROR, "First slice in a frame missing.\n"); + return ret; + } + + if (!IS_I(sh->r)) + vvc_smvd_ref_idx(fc, sc); + + ret = init_slice_context(sc, fc, nal, unit); + if (ret < 0) + goto fail; + fc->nb_slices++; + +fail: + return ret; +} + +static int decode_nal_unit(VVCContext *s, VVCFrameContext *fc, const H2645NAL *nal, const CodedBitstreamUnit *unit) +{ + int ret; + + s->temporal_id = nal->temporal_id; + + switch (unit->type) { + case VVC_VPS_NUT: + case VVC_SPS_NUT: + case VVC_PPS_NUT: + /* vps, sps, sps cached by s->cbc */ + break; + case VVC_TRAIL_NUT: + case VVC_STSA_NUT: + case VVC_RADL_NUT: + case VVC_RASL_NUT: + case VVC_IDR_W_RADL: + case VVC_IDR_N_LP: + case VVC_CRA_NUT: + case VVC_GDR_NUT: + ret = decode_slice(s, fc, nal, unit); + if (ret < 0) + goto fail; + break; + case VVC_PREFIX_APS_NUT: + case VVC_SUFFIX_APS_NUT: + ret = ff_vvc_decode_aps(&s->ps, unit); + if (ret < 0) + goto fail; + break; + default: + av_log(s->avctx, AV_LOG_INFO, + "Skipping NAL unit %d\n", unit->type); + } + + return 0; +fail: + return ret; +} + +static int decode_nal_units(VVCContext *s, VVCFrameContext *fc, AVPacket *avpkt) +{ + const CodedBitstreamH266Context *h266 = (const CodedBitstreamH266Context *)s->cbc->priv_data; + CodedBitstreamFragment *frame = &s->current_frame; + int i, ret = 0; + int eos_at_start = 1; + s->last_eos = s->eos; + s->eos = 0; + + ff_cbs_fragment_reset(frame); + ret = ff_cbs_read_packet(s->cbc, frame, avpkt); + if (ret < 0) { + av_log(s->avctx, AV_LOG_ERROR, "Failed to read packet.\n"); + return ret; + } + /* decode the NAL units */ + for (i = 0; i < frame->nb_units; i++) { + const H2645NAL *nal = h266->common.read_packet.nals + i; + const CodedBitstreamUnit *unit = frame->units + i; + + if (unit->type == VVC_EOB_NUT || unit->type == VVC_EOS_NUT) { + if (eos_at_start) + s->last_eos = 1; + else + s->eos = 1; + } else { + ret = decode_nal_unit(s, fc, nal, unit); + if (ret < 0) { + av_log(s->avctx, AV_LOG_WARNING, + "Error parsing NAL unit #%d.\n", i); + goto fail; + } + } + } + return 0; + +fail: + if (fc->ref) + ff_vvc_report_frame_finished(fc->ref); + return ret; +} + +static int set_output_format(const VVCContext *s, const AVFrame *output) +{ + AVCodecContext *c = s->avctx; + int ret; + + if (output->width != c->width || output->height != c->height) { + if ((ret = ff_set_dimensions(c, output->width, output->height)) < 0) + return ret; + } + c->pix_fmt = output->format; + return 0; +} + +static int wait_delayed_frame(VVCContext *s, AVFrame *output, int *got_output) +{ + VVCFrameContext *delayed = get_frame_context(s, s->fcs, s->nb_frames - s->nb_delayed); + int ret = ff_vvc_frame_wait(s, delayed); + + if (!ret && delayed->output_frame->buf[0]) { + av_frame_move_ref(output, delayed->output_frame); + ret = set_output_format(s, output); + if (!ret) + *got_output = 1; + } + s->nb_delayed--; + + return ret; +} + +static int submit_frame(VVCContext *s, VVCFrameContext *fc, AVFrame *output, int *got_output) +{ + int ret; + s->nb_frames++; + s->nb_delayed++; + ff_vvc_frame_submit(s, fc); + if (s->nb_delayed >= s->nb_fcs) { + if ((ret = wait_delayed_frame(s, output, got_output)) < 0) + return ret; + } + return 0; +} static int vvc_decode_frame(AVCodecContext *avctx, AVFrame *output, int *got_output, AVPacket *avpkt) { + VVCContext *s = avctx->priv_data; + VVCFrameContext *fc; + int ret; + + if (!avpkt->size) { + while (s->nb_delayed) { + if ((ret = wait_delayed_frame(s, output, got_output)) < 0) + return ret; + if (*got_output) + return 0; + } + if (s->nb_frames) { + //we still have frames cached in dpb. + VVCFrameContext *last = get_frame_context(s, s->fcs, s->nb_frames - 1); + + ret = ff_vvc_output_frame(s, last, output, 0, 1); + if (ret < 0) + return ret; + if (ret) { + *got_output = ret; + if ((ret = set_output_format(s, output)) < 0) + return ret; + } + } + return 0; + } + + fc = get_frame_context(s, s->fcs, s->nb_frames); + + fc->nb_slices = 0; + fc->decode_order = s->nb_frames; + + ret = decode_nal_units(s, fc, avpkt); + if (ret < 0) + return ret; + + ret = submit_frame(s, fc, output, got_output); + if (ret < 0) + return ret; + return avpkt->size; } static void vvc_decode_flush(AVCodecContext *avctx) { + VVCContext *s = avctx->priv_data; + int got_output; + AVFrame *output = av_frame_alloc(); + + if (output) { + while (s->nb_delayed) { + wait_delayed_frame(s, output, &got_output); + if (got_output) { + av_frame_unref(output); + } + } + av_frame_free(&output); + } } static av_cold int vvc_decode_free(AVCodecContext *avctx) { + VVCContext *s = avctx->priv_data; + int i; + + ff_cbs_fragment_free(&s->current_frame); + vvc_decode_flush(avctx); + ff_vvc_executor_free(&s->executor); + if (s->fcs) { + for (i = 0; i < s->nb_fcs; i++) + frame_context_free(s->fcs + i); + av_free(s->fcs); + } + ff_vvc_ps_uninit(&s->ps); + ff_cbs_close(&s->cbc); + return 0; } +#define VVC_MAX_FRMAE_DELAY 16 static av_cold int vvc_decode_init(AVCodecContext *avctx) { + VVCContext *s = avctx->priv_data; + int ret; + + s->avctx = avctx; + + if (ff_cbs_init(&s->cbc, AV_CODEC_ID_VVC, avctx)) + goto fail; + + s->nb_fcs = (avctx->flags & AV_CODEC_FLAG_LOW_DELAY) ? 1 : FFMIN(av_cpu_count(), VVC_MAX_FRMAE_DELAY); + s->fcs = av_calloc(s->nb_fcs, sizeof(*s->fcs)); + if (!s->fcs) + goto fail; + + for (int i = 0; i < s->nb_fcs; i++) { + VVCFrameContext *fc = s->fcs + i; + ret = frame_context_init(fc, avctx); + if (ret < 0) + goto fail; + } + + s->executor = ff_vvc_executor_alloc(s, s->nb_fcs); + if (!s->executor) + goto fail; + + s->eos = 1; + GDR_SET_RECOVERED(s); + memset(&ff_vvc_default_scale_m, 16, sizeof(ff_vvc_default_scale_m)); + return 0; + +fail: + vvc_decode_free(avctx); + return AVERROR(ENOMEM); } const FFCodec ff_vvc_decoder = {