From patchwork Sun May 21 13:03:06 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 41759 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:ba91:b0:105:feb:71f2 with SMTP id fb17csp1090191pzb; Sun, 21 May 2023 06:03:52 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ45LLXNyuZTq5r7XgQnNAv4HUZzM5WL1xf6Bec2pJzbIve/OSfTA77h8nZoiGU09RmSg2CB X-Received: by 2002:a05:6402:5213:b0:512:7129:1346 with SMTP id s19-20020a056402521300b0051271291346mr4249692edd.16.1684674232036; Sun, 21 May 2023 06:03:52 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684674232; cv=none; d=google.com; s=arc-20160816; b=ZupBnpWPoMAz6k7x3HjkHcaP9oaS5k9+1y0VH6BBf6sHMImF02EuSuzFwVmEuxJT8w 26iKyCkpBGDdOHOKSRNH47MiZE+Ky1hemO1f8y2ABmLBHfuwIXuJKlCPcs5OptJijH/9 Erukie7+Z04XIkLYybE1lJ9FXhHG2bituhDburikolmzhgIsDof3IasLeQUz2J/aO4kZ a1SOPr9yD1M2udgWlM3fxErlNs3CfdxxXdkIdBrCIS7cipANdxaa2aTQG9cCbjg1y1zF qA7XXlnUze/RmhoAYVjP7UlhFOOw1t2SGNmjHg0IuX15pgVZ3nUC/DQFdb/APVGpO4Hu 0ftw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=LQa23GN4mdcVDX7iboSDl3G+otQL2CtUH863LLu/7+U=; b=f39lHyARbEotsS36yHVU70uoljxQJv2hLAMHS9UaypkFgv8zlccr9L8QhMSiCzJvTK Qt9WfUxCHMG0nXGATm2yBEXRZvQlMf59jlea67eAOFiHOvVXRxLBj1HmtghV69IMF+5I t2T12vUyymJmjEKy/0e3LSF62IziVZcTMADtasU3kG6v9pkIZeAMVuIyr9fvn7FapOoY XHJDufaiW42l9xgwuwDYQ3D0Efv1fF/qT9n6NZ5uRZ2l+j1d4TsMQ71v2HfbL27p8vUB gbEoCVH1qq4wxiiHCHGavg6oPSix2EOkn4qmFGoA2O16Oqf3gpHcNO4ZjfNdjQZAzwRa F7GQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20221208 header.b=JGG2eNuk; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id i21-20020a056402055500b005075e52cd9csi2728724edx.47.2023.05.21.06.03.51; Sun, 21 May 2023 06:03:52 -0700 (PDT) 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=@gmail.com header.s=20221208 header.b=JGG2eNuk; 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 50D1968C025; Sun, 21 May 2023 16:03:36 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f171.google.com (mail-pf1-f171.google.com [209.85.210.171]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 765E768B577 for ; Sun, 21 May 2023 16:03:29 +0300 (EEST) Received: by mail-pf1-f171.google.com with SMTP id d2e1a72fcca58-64d30ab1f89so1675364b3a.3 for ; Sun, 21 May 2023 06:03:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1684674207; x=1687266207; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Vb4ac+NHLyHlASD9OyUnjW/HSwQ4BYh1F7RoumtC6A8=; b=JGG2eNuktRGx15X2ivk10caZi3rAk2Y8OGwYRb8iElVUOLp9adcSNxEcwSb8qx0JXh 5Hkdtlw5OOrvldaCmtOKImbY8qw4KNVkx8RjlXllUdqQz3PI51MTZAVkeYWyOCCFPK1P bkuk5O7QoZppPyRFKKuLLSFIBl8ZNMNoMM5nWMMfDlHWPJelT7AuLyXRt2OMH+yGBPXO Fvi8M5Apt/JgNKUXiQj9CnmCfD1vvkUTIHsDW3V2LssLnUnvzNqIWhEDbA2KgTxcB9wV 6pPtEnNVXgIPsO1a+6a8gtacf01tYnXHooshRKCaTJhpxv/ZTsHv6T1k5AA+gd5tUbZS zK9w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684674207; x=1687266207; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Vb4ac+NHLyHlASD9OyUnjW/HSwQ4BYh1F7RoumtC6A8=; b=IZ/MdiIwpT6Scxx4gfM7juSShHE7Vy8OJCKl1V9IMVTXKx6blbcJy6LxrqH7buVwr9 ICxrBCzjkSZg/FGZmY1QJIJxfTmedDwsToUwn04g5nEPFT6asEY8J1t5D/TCQ1Ng2byO mLyiDeMrrSu9aG6mw+BhwJ3BQxSDjohXd95sEOMrSzy15PYLa8CpjrkZgjzObd8GKI6I dQhlS6J6mxyVgJNxOHHBEDnYylSfNtob5ba+PrAL3eFOnkQrqQIArGYRbwcnHlFta1tr SFTiveHaJ9/inkvCZ3AFkAp7/pELoQmhJpK0P07OeQSQ1XF8Vfbelc0RETptiQh0wMg7 k42w== X-Gm-Message-State: AC+VfDyKcYebFSmhBHuOPSlGlwmf6rTpg9DfNO9QtqzGxObh8fjXY6zD AdQrci+0E60L7CkYCL3328iUkk8SK/o4Iw== X-Received: by 2002:a05:6a00:15c7:b0:626:2ce1:263c with SMTP id o7-20020a056a0015c700b006262ce1263cmr10503874pfu.5.1684674207203; Sun, 21 May 2023 06:03:27 -0700 (PDT) Received: from NuoMi.localdomain ([112.64.8.97]) by smtp.gmail.com with ESMTPSA id t9-20020aa79389000000b0064d45eea573sm1671872pfe.41.2023.05.21.06.03.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 21 May 2023 06:03:26 -0700 (PDT) From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 21 May 2023 21:03:06 +0800 Message-Id: <20230521130319.13813-2-nuomi2021@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230521130319.13813-1-nuomi2021@gmail.com> References: <20230521130319.13813-1-nuomi2021@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 01/14] vvcdec: add thread executor 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: pE12tPNiizof The executor design pattern was inroduced by java it also adapted by python Compared to handcrafted thread pool management, it greatly simplifies the thread code. --- libavcodec/Makefile | 1 + libavcodec/vvc/Makefile | 4 + libavcodec/vvc/vvc_executor.c | 193 ++++++++++++++++++++++++++++++++++ libavcodec/vvc/vvc_executor.h | 73 +++++++++++++ 4 files changed, 271 insertions(+) create mode 100644 libavcodec/vvc/Makefile create mode 100644 libavcodec/vvc/vvc_executor.c create mode 100644 libavcodec/vvc/vvc_executor.h diff --git a/libavcodec/Makefile b/libavcodec/Makefile index dab09f483a..b1fcbf71b2 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -62,6 +62,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/vvc/Makefile b/libavcodec/vvc/Makefile new file mode 100644 index 0000000000..c4b93e0389 --- /dev/null +++ b/libavcodec/vvc/Makefile @@ -0,0 +1,4 @@ +clean:: + $(RM) $(CLEANSUFFIXES:%=libavcodec/vvc/%) + +OBJS-$(CONFIG_VVC_DECODER) += vvc/vvc_executor.o diff --git a/libavcodec/vvc/vvc_executor.c b/libavcodec/vvc/vvc_executor.c new file mode 100644 index 0000000000..f2afdf79ae --- /dev/null +++ b/libavcodec/vvc/vvc_executor.c @@ -0,0 +1,193 @@ +/* + * VVC video 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 + */ +#include "libavutil/avutil.h" +#include "libavutil/thread.h" + +#include "vvc_executor.h" + +typedef struct ThreadInfo { + int idx; + VVCExecutor *e; + pthread_t thread; +} ThreadInfo; + +struct VVCExecutor { + VVCTaskCallbacks cb; + ThreadInfo *threads; + uint8_t *local_contexts; + int thread_count; + + pthread_mutex_t lock; + pthread_cond_t cond; + int die; + VVCTasklet *tasks; +}; + +static void remove_task(VVCTasklet **prev, VVCTasklet *t) +{ + *prev = t->next; + t->next = NULL; +} + +static void add_task(VVCTasklet **prev, VVCTasklet *t) +{ + t->next = *prev; + *prev = t; +} + +static void *executor_worker_task(void *data) +{ + ThreadInfo *ti = (ThreadInfo*)data; + VVCExecutor *e = ti->e; + void *lc = e->local_contexts + ti->idx * e->cb.local_context_size; + VVCTasklet **prev; + VVCTaskCallbacks *cb = &e->cb; + + pthread_mutex_lock(&e->lock); + while (1) { + VVCTasklet* t = NULL; + if (e->die) break; + + for (prev = &e->tasks; *prev; prev = &(*prev)->next) { + if (cb->ready(*prev, cb->user_data)) { + t = *prev; + break; + } + } + if (t) { + //found one task + remove_task(prev, t); + pthread_mutex_unlock(&e->lock); + cb->run(t, lc, cb->user_data); + pthread_mutex_lock(&e->lock); + } else { + //no task in one loop + pthread_cond_wait(&e->cond, &e->lock); + } + } + pthread_mutex_unlock(&e->lock); + return NULL; +} + +VVCExecutor* ff_vvc_executor_alloc(const VVCTaskCallbacks *cb, int thread_count) +{ + VVCExecutor *e; + int i, j, ret; + if (!cb || !cb->user_data || !cb->ready || !cb->run || !cb->priority_higher) + return NULL; + e = av_calloc(1, sizeof(*e)); + if (!e) + return NULL; + e->cb = *cb; + + e->local_contexts = av_malloc(thread_count * e->cb.local_context_size); + if (!e->local_contexts) + goto free_executor; + + e->threads = av_calloc(thread_count, sizeof(*e->threads)); + if (!e->threads) + goto free_contexts; + for (i = 0; i < thread_count; i++) { + ThreadInfo *ti = e->threads + i; + ti->e = e; + ti->idx = i; + } + + ret = pthread_mutex_init(&e->lock, NULL); + if (ret) + goto free_threads; + + ret = pthread_cond_init(&e->cond, NULL); + if (ret) + goto destroy_lock; + + for (i = 0; i < thread_count; i++) { + ThreadInfo *ti = e->threads + i; + ret = pthread_create(&ti->thread, NULL, executor_worker_task, ti); + if (ret) + goto join_threads; + } + e->thread_count = thread_count; + return e; + +join_threads: + pthread_mutex_lock(&e->lock); + e->die = 1; + pthread_cond_broadcast(&e->cond); + pthread_mutex_unlock(&e->lock); + for (j = 0; j < i; j++) + pthread_join(e->threads[j].thread, NULL); + pthread_cond_destroy(&e->cond); +destroy_lock: + pthread_mutex_destroy(&e->lock); +free_threads: + av_free(e->threads); +free_contexts: + av_free(e->local_contexts); +free_executor: + free(e); + return NULL; +} + +void ff_vvc_executor_free(VVCExecutor **executor) +{ + VVCExecutor *e; + if (!executor || !*executor) + return; + e = *executor; + + //singal die + pthread_mutex_lock(&e->lock); + e->die = 1; + pthread_cond_broadcast(&e->cond); + pthread_mutex_unlock(&e->lock); + + for (int i = 0; i < e->thread_count; i++) + pthread_join(e->threads[i].thread, NULL); + pthread_cond_destroy(&e->cond); + pthread_mutex_destroy(&e->lock); + + av_free(e->threads); + av_free(e->local_contexts); + + av_freep(executor); +} + +void ff_vvc_executor_execute(VVCExecutor *e, VVCTasklet *t) +{ + VVCTaskCallbacks *cb = &e->cb; + VVCTasklet **prev; + + pthread_mutex_lock(&e->lock); + for (prev = &e->tasks; *prev && cb->priority_higher(*prev, t); prev = &(*prev)->next) + /* nothing */; + add_task(prev, t); + pthread_cond_signal(&e->cond); + pthread_mutex_unlock(&e->lock); +} + +void ff_vvc_executor_wakeup(VVCExecutor *e) +{ + pthread_mutex_lock(&e->lock); + pthread_cond_broadcast(&e->cond); + pthread_mutex_unlock(&e->lock); +} diff --git a/libavcodec/vvc/vvc_executor.h b/libavcodec/vvc/vvc_executor.h new file mode 100644 index 0000000000..79c00d58ca --- /dev/null +++ b/libavcodec/vvc/vvc_executor.h @@ -0,0 +1,73 @@ +/* + * 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_EXECUTOR_H +#define AVCODEC_VVC_EXECUTOR_H + +typedef struct VVCExecutor VVCExecutor; +typedef struct VVCTasklet VVCTasklet; + +struct VVCTasklet { + VVCTasklet *next; +}; + +typedef struct VVCTaskCallbacks { + void *user_data; + + int local_context_size; + + // return 1 if a's priority > b's priority + int (*priority_higher)(const VVCTasklet *a, const VVCTasklet *b); + + // task is ready for run + int (*ready)(const VVCTasklet *t, void *user_data); + + // run the task + int (*run)(VVCTasklet *t, void *local_context, void *user_data); +} VVCTaskCallbacks; + +/** + * Alloc executor + * @param callbacks callback strucutre for executor + * @param thread_count worker thread number + * @return return the executor + */ +VVCExecutor* ff_vvc_executor_alloc(const VVCTaskCallbacks *callbacks, int thread_count); + +/** + * Free executor + * @param e pointer to executor + */ +void ff_vvc_executor_free(VVCExecutor **e); + +/** + * Add task to executor + * @param e pointer to executor + * @param t pointer to task + */ +void ff_vvc_executor_execute(VVCExecutor *e, VVCTasklet *t); + +/** + * Wakeup all threads + * @param e pointer to executor + */ +void ff_vvc_executor_wakeup(VVCExecutor *e); + +#endif //AVCODEC_VVC_EXECUTOR_H From patchwork Sun May 21 13:03:07 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 41760 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:ba91:b0:105:feb:71f2 with SMTP id fb17csp1090351pzb; Sun, 21 May 2023 06:04:04 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ52hvareYZUw5nHmXpGmaXHMWl5oXBIkMpe7G7Ngxt07iadzeKVOi2xFTRwnGhq1rt7OYK3 X-Received: by 2002:a17:907:784:b0:966:5035:6973 with SMTP id xd4-20020a170907078400b0096650356973mr7515283ejb.50.1684674244191; Sun, 21 May 2023 06:04:04 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684674244; cv=none; d=google.com; s=arc-20160816; b=IAnBxgT9tBPqqMMd8vxnVzoGUzKIm2NMU7fqcWTrsqvHLGeN5via0nqqx02ZOk39zJ io0temPUpg+JXQkBrektD39i6JZKVUoSVtJJqHcZzjHnQqNQg7R4m+E9haTKMhGfTITL yZHOxp7M/12DQcgfJCjGmILI0AAWHgPbJ4Ji7zMweBazH+jAoBfmmahFVFa35pWWH8HY HKVblXO+kfW4CqGyXTgNd1BlXtpFcxJjpHpLFjq6rqOiGu3wfAd/DDiBgxK0evEb4KdT YOhy59LQEFdTHimEYXA7YdPGhxPsT1owCfN3d9AXNeeL9pEAj8C0B9t6TA40RqL4cnxv NfwQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=qu1/DlHFNIiylqU5KllQjNGVBrCrNIsM9ixpR6MWn/E=; b=DIt7OXGWhfd62YI+ZXDHjYy+3h35FKb2xxgQje1oNeCD5AzHxTSBvJD+RhLj4Tod3q UBJ/G4XxTko54mwg0903DHg7qMHZWI+FNk8fvofWZNmgBVsSafMUm/Dgdn7ittgAxLlu /IQqjcDTb9vTPiArCHjDhsefL+WDSMttrSHxrOi70e+ZHkIEPF0vDpurEZb1rHXYCN5A oNU4nGKKGPChOsRNO9b7+GC2LJ/vtnuXPPwu9/1/lyqFIuhxWA+Y5WEkuVfTRRE3h8DK uYcJWJmidLvIt6gm80eVUDeOPbZqAnsNZpuRvcqizg+U/yCA+7WxnYzt+j2CMhPdgjRy UxHA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20221208 header.b=DSK7fx3O; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id qu28-20020a170907111c00b0094f34ed62d2si1420623ejb.366.2023.05.21.06.04.03; Sun, 21 May 2023 06:04:04 -0700 (PDT) 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=@gmail.com header.s=20221208 header.b=DSK7fx3O; 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 8159E68C11B; Sun, 21 May 2023 16:03:38 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f177.google.com (mail-pf1-f177.google.com [209.85.210.177]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 1458668C025 for ; Sun, 21 May 2023 16:03:31 +0300 (EEST) Received: by mail-pf1-f177.google.com with SMTP id d2e1a72fcca58-64d3fdcadb8so1271011b3a.3 for ; Sun, 21 May 2023 06:03:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1684674209; x=1687266209; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=JjuMB4SC/J84J3tkmFEtRV5VD0oabMKksHce9VY/oP8=; b=DSK7fx3O+oBq++JNmgorr6jI8yWPZKKYReIsiIKgg7qe5m3bKqyOFZOCdFiSWwpJjH K55anLp0NRDktf7s/bxIQyjo2HyZl+1C4SDW4mpoSDvkZLZMFzW+MFG/ffrxuuAwsFmJ nlY4iyYvPoppfrXpuNieCrO0J4a2D1sEPHxtY6Mt83uk8h0BZ3bwpp8h8nRDJPr0lo/a xOfP1ycxOY6syehn6YamA+VY8oahQ4dwhIbnkkiLOBzB9Gp8wBcRoEUEFf62QmLqG3IY 8PSDF9UeCqOwQVy5HdOEDBLe+z280Pmcv6pI88QYPSpg8uxYnFV//VSgaVU9IH/cjXsX jYsA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684674209; x=1687266209; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=JjuMB4SC/J84J3tkmFEtRV5VD0oabMKksHce9VY/oP8=; b=XCpSju88rrKibdtflJmjhFG8icCR+OdftHpkIkY5B4+iXA7w/dZzBjmYnL6rFQrE7z I2WRn+Zs9RyMqbzsXb6uS5DDfNBhmcJE/AjCht9IjHsqm8kjC4+XBc6TenWd3biwljFx eB+r3If548hNnPpxyL2N7t64LTnQJpvFfWIDQwI1epieYVLCJZ6b4iZTKRAmnP0yi2TS vaEKJIb+f6woWd3anBQle/vrDI2nuYxQjq70RrJHeYhHyZXxjlgeyMF65JMkNc8b3FeX tUZ/ITQ9n9c34Cs05h3XfLH1ijuVYN0E/cxIWhpojJ1l1HWn0/YpAdia6PRVdLYI4Ryo Hviw== X-Gm-Message-State: AC+VfDxJtukO4wC/vjpm31Dj5MmKOBjBiotpcyTdWhcK1ldeXjKnGAOW mdGQoTKnN1hsn4Cd9pZW6jTWiKe6hKZKgg== X-Received: by 2002:a05:6a00:cc9:b0:64d:22db:1018 with SMTP id b9-20020a056a000cc900b0064d22db1018mr10248113pfv.25.1684674208797; Sun, 21 May 2023 06:03:28 -0700 (PDT) Received: from NuoMi.localdomain ([112.64.8.97]) by smtp.gmail.com with ESMTPSA id t9-20020aa79389000000b0064d45eea573sm1671872pfe.41.2023.05.21.06.03.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 21 May 2023 06:03:28 -0700 (PDT) From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 21 May 2023 21:03:07 +0800 Message-Id: <20230521130319.13813-3-nuomi2021@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230521130319.13813-1-nuomi2021@gmail.com> References: <20230521130319.13813-1-nuomi2021@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 02/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: OYSChUHXSfOE --- configure | 1 + libavcodec/allcodecs.c | 1 + libavcodec/vvc/Makefile | 3 +- libavcodec/vvc/vvcdec.c | 84 +++++++++++ libavcodec/vvc/vvcdec.h | 304 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 392 insertions(+), 1 deletion(-) create mode 100644 libavcodec/vvc/vvcdec.c create mode 100644 libavcodec/vvc/vvcdec.h diff --git a/configure b/configure index c5de9a1114..cc5c75f1c9 100755 --- a/configure +++ b/configure @@ -2968,6 +2968,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/allcodecs.c b/libavcodec/allcodecs.c index b009848a44..16255a5794 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -383,6 +383,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 index c4b93e0389..06d611ca6d 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -1,4 +1,5 @@ clean:: $(RM) $(CLEANSUFFIXES:%=libavcodec/vvc/%) -OBJS-$(CONFIG_VVC_DECODER) += vvc/vvc_executor.o +OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ + vvc/vvc_executor.o diff --git a/libavcodec/vvc/vvcdec.c b/libavcodec/vvc/vvcdec.c new file mode 100644 index 0000000000..8d027af0b9 --- /dev/null +++ b/libavcodec/vvc/vvcdec.c @@ -0,0 +1,84 @@ +/* + * 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 "config_components.h" + +#include "libavcodec/codec_internal.h" +#include "libavcodec/decode.h" +#include "libavcodec/golomb.h" +#include "libavcodec/profiles.h" +#include "libavcodec/vvc.h" + +#include "libavutil/cpu.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; +} + +#define OFFSET(x) offsetof(VVCContext, x) +#define PAR (AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) + +static const AVOption options[] = { + { NULL }, +}; + +static const AVClass vvc_decoder_class = { + .class_name = "vvc decoder", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +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), + .p.priv_class = &vvc_decoder_class, + .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..7097293af8 --- /dev/null +++ b/libavcodec/vvc/vvcdec.h @@ -0,0 +1,304 @@ +/* + * 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/h2645_parse.h" +#include "libavcodec/threadframe.h" +#include "libavcodec/videodsp.h" +#include "libavcodec/vvc.h" + +#include "vvc_executor.h" + +#define LUMA 0 +#define CHROMA 1 +#define CB 1 +#define CR 2 +#define JCBCR 3 + +#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 MIN_PU_SIZE 4 +#define MIN_PU_LOG2 2 + +#define MAX_TB_SIZE 64 +#define MIN_TU_LOG2 2 ///< MinTbLog2SizeY +#define MIN_TU_SIZE 4 +#define MAX_TUS_IN_CU 64 + +#define MAX_PARTS_IN_CTU ((MAX_CTU_SIZE >> MIN_CU_LOG2) * (MAX_CTU_SIZE >> MIN_CU_LOG2)) + +#define MAX_CONTROL_POINTS 3 + +#define MRG_MAX_NUM_CANDS 6 +#define MAX_NUM_HMVP_CANDS 5 + +#define L0 0 +#define L1 1 + +#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_QP 63 +#define DEFAULT_INTRA_TC_OFFSET 2 + +#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 + +#define MAX_PB_SIZE 128 +#define EDGE_EMU_BUFFER_STRIDE (MAX_PB_SIZE + 32) + +#define AFFINE_MIN_BLOCK_SIZE 4 +#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) + +/** + * 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)]) + +typedef struct VVCLocalContext VVCLocalContext; +typedef struct SliceContext SliceContext; +typedef struct VVCFrameContext VVCFrameContext; +typedef struct VVCFrameThread VVCFrameThread; +typedef struct EntryPoint EntryPoint; +typedef struct VVCTask VVCTask; +typedef struct Mv Mv; +typedef struct MvField MvField; +typedef struct DMVRInfo DMVRInfo; +typedef struct CTU CTU; +typedef struct SAOParams SAOParams; +typedef struct ALFParams ALFParams; + +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 { + AVFrame *frame; + ThreadFrame tf; + + MvField *tab_mvf; + RefPicList *refPicList; + RefPicListTab **rpl_tab; + + int ctb_count; + + int poc; + + struct VVCFrame *collocated_ref; + + AVBufferRef *tab_mvf_buf; + AVBufferRef *rpl_tab_buf; + AVBufferRef *rpl_buf; + AVBufferRef *progress_buf; + + /** + * 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; + +struct SliceContext { + int slice_idx; + EntryPoint *eps; + int nb_eps; +}; + +struct VVCFrameContext { + AVCodecContext *avctx; + + // +1 for the current frame + VVCFrame DPB[VVC_MAX_DPB_SIZE + 1]; + + AVFrame *frame; + AVFrame *output_frame; + + SliceContext **slices; + int nb_slices; + int nb_slices_allocated; + + VVCFrame *ref; + + VideoDSPContext vdsp; + + VVCFrameThread *frame_thread; + + uint64_t decode_order; + + AVPacket *avpkt; + H2645Packet pkt; + + AVBufferPool *tab_mvf_pool; + AVBufferPool *rpl_tab_pool; + + AVBufferPool *cu_pool; + + struct { + int16_t *slice_idx; + + DMVRInfo *dmvr; + + 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[][] + Mv *cp_mv[2]; ///< CpMvLX[][][][MAX_CONTROL_POINTS]; + + 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 maxFilterLengthPs for luma + uint8_t *vertical_p; ///< vertical maxFilterLengthQs 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; + 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; +} ; + +typedef struct VVCContext { + const AVClass *c; // needed by private avoptions + AVCodecContext *avctx; + + int temporal_id; ///< temporal_id_plus1 - 1 + int pocTid0; + + 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; + + int is_nalff; ///< this flag is != 0 if bitstream is encapsulated + ///< as a format defined in 14496-15 + + int apply_defdispwin; + int nal_length_size; ///< Number of bytes used for nal length (1, 2 or 4) + + VVCExecutor *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 May 21 13:03:08 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 41761 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:ba91:b0:105:feb:71f2 with SMTP id fb17csp1090512pzb; Sun, 21 May 2023 06:04:19 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ6pSXrG3vuXIRd+b1LSp60krY27VPRz8CpdjabfgV/KZzCZcIPUY1Kh8NTMdq88qPDAjqKb X-Received: by 2002:a17:906:85c1:b0:965:66dd:78f8 with SMTP id i1-20020a17090685c100b0096566dd78f8mr5381646ejy.56.1684674258716; Sun, 21 May 2023 06:04:18 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684674258; cv=none; d=google.com; s=arc-20160816; b=xdiNo/rYn2J8O0fGVkkBUdl/Sb4uJiXLogAfrWZOfKlJPN0ALAj9ObEQE57qSoM6qY bIcv8T4Y7Czw6vEc1LcM27WHO/6Wt0WC59ObXkbfKrE81M7QtXkbz5fD44/CDZRx5fgZ Tv7r/3AxQ2ShNTBPEcKtPBfjBCr0K4h5lnH4BB+WkkytSPIlSRdK+wzqbxH3LHhiiI/w bbEQlh+glxohBPIrD4lY45vslXLM01J2bL83bLJHEIJFkga6cjX3Yw5fhlPI0LA1l4Ya z0lwfLt3qwkzwfbKDypy6Dh8VsINV2OF1/TjewpHI+/YazRC7qvMm7EzuufnjzlZF+y+ 1czw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=PDUdJkRGuWL60XJqu6hGM+QS4IkVncxE8jTQBjZbKBI=; b=cA4zTVwuN/qSLMbwbFR6HIzvlBJCvfq1Mo/+M7P+CM15lvoMa1sXtyawoSLMxYWvMZ 63mzZFUgHe8pzVZfUZx5W5uGaMVx2KHrdyVSBBO031Y6aoeKikBnbAlWD6dogG+BOzsu dri2FYP0GiRHie8Vh84mcoyEXVRe8uFg+XFi2wY1qUtQNemijfJfesO4SBl5DqXQtueG ps0Xg7TW8L4KLB1FDGZ1NCo0JxO8vewlgHQRof+TjYksyuAwbFs/CDKlKZm2TxNBxKSw cAPBELM8IRWb+HFxH83dj/oW6c06MDx1zMYecQtF0xRxxqMo7f9aolgfcudAt/ufYxIY ZEBQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20221208 header.b=aZgJa6qg; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id ez22-20020a1709070bd600b00965f2c24247si698480ejc.950.2023.05.21.06.04.17; Sun, 21 May 2023 06:04:18 -0700 (PDT) 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=@gmail.com header.s=20221208 header.b=aZgJa6qg; 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 B969968C13E; Sun, 21 May 2023 16:03:44 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f182.google.com (mail-pf1-f182.google.com [209.85.210.182]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5C3CB68C122 for ; Sun, 21 May 2023 16:03:37 +0300 (EEST) Received: by mail-pf1-f182.google.com with SMTP id d2e1a72fcca58-64d3fdcadb8so1271048b3a.3 for ; Sun, 21 May 2023 06:03:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1684674215; x=1687266215; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=cP3Tb+fSNb4fbht+JU0aNFCT3ohJNOiJCiPxMOW3LmE=; b=aZgJa6qgFDFxDtoty1/xNlb5roXhHMwe5XMuVTR7TK8UD3jE/dPW/EZEn0/fSKcbRW AS/YeIJXGa8oRo+BC5ZwZeZlg5XwQx+siZyvfL351OqfbbVmkwDPazESx76T8h3ekwRa bxiALgt3JTxI3Cr+UBO3OVp3VK9fbEGFRPQVjqJN+3vxlfY8+/r/qtekF1ETp0rWKM0B 4HjHu6lSatTnFKFHILtHQ5hOH4dPbbm1Bj3Q/iZP/kH0sTJL0KGBnAMUbsOO+tMY5N8Y PNz7iBjhdDn3fptS/Kn7Kf5vGnkK3kTDWK0Yf7Kdtr6zwk5kZiNbB1+7/tfcXBXbCanA CpoQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684674215; x=1687266215; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=cP3Tb+fSNb4fbht+JU0aNFCT3ohJNOiJCiPxMOW3LmE=; b=X+5QbUuROJ0gHTuGubA2jwxDWg7aA8m6yld7aSRYlgUNr+KJEHaN/k4eirQCQFVIBW ymGmPlxw3Vp9zbcTerbKx8jCTZtSE0wraJmOQDBR+ggNIsgVDwgkp7Zrtf50hoJ6Rpm6 XWqda77dLRp7Qmv6JOhybgPQ0Ympd1LNZRaB0U20GHJM95/P2bldidN4qLbPuWr9sCfK 5GzMBJynIzqVVCnTiiKge22ZXUXzdt77Trb8UYPtzu0wjcfLfEMh9VxiJV4tqo0QqJXR JDohVFf4LEgbreJDEzHoYf74wIER/cDb/uVOIJNdjnUjn8dcxvrVDblWHWlX7TaX44ak D8gQ== X-Gm-Message-State: AC+VfDxePgce0X66vS14CRgYGFgwq8dVLLPwjG51E/NqypUthRPXygf3 b+dIF3mPxJNPJgI/4B/oUF/PsYxMhMMBsw== X-Received: by 2002:a05:6a00:2e9a:b0:63d:260d:f9dd with SMTP id fd26-20020a056a002e9a00b0063d260df9ddmr11046621pfb.33.1684674212610; Sun, 21 May 2023 06:03:32 -0700 (PDT) Received: from NuoMi.localdomain ([112.64.8.97]) by smtp.gmail.com with ESMTPSA id t9-20020aa79389000000b0064d45eea573sm1671872pfe.41.2023.05.21.06.03.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 21 May 2023 06:03:31 -0700 (PDT) From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 21 May 2023 21:03:08 +0800 Message-Id: <20230521130319.13813-4-nuomi2021@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230521130319.13813-1-nuomi2021@gmail.com> References: <20230521130319.13813-1-nuomi2021@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 03/14] vvcdec: add sps, pps, sh 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: 6vAWvhnBZmiy --- libavcodec/vvc/Makefile | 4 +- libavcodec/vvc/vvc_data.c | 3295 ++++++++++++++++++++++++++++++++++++ libavcodec/vvc/vvc_data.h | 69 + libavcodec/vvc/vvc_ps.c | 3379 +++++++++++++++++++++++++++++++++++++ libavcodec/vvc/vvc_ps.h | 811 +++++++++ libavcodec/vvc/vvcdec.h | 3 + 6 files changed, 7560 insertions(+), 1 deletion(-) create mode 100644 libavcodec/vvc/vvc_data.c create mode 100644 libavcodec/vvc/vvc_data.h 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 06d611ca6d..3dbfb70407 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -2,4 +2,6 @@ clean:: $(RM) $(CLEANSUFFIXES:%=libavcodec/vvc/%) OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ - vvc/vvc_executor.o + vvc/vvc_executor.o \ + vvc/vvc_ps.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..b44c0fb595 --- /dev/null +++ b/libavcodec/vvc/vvc_data.c @@ -0,0 +1,3295 @@ +/* + * 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 int8_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_filter_c[NUM_INTRA_LUMA_FACTS][NUM_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 }, +}; + +#define FILTER_G(fact) { 16 - (fact >> 1), 32 - (fact >> 1), 16 + (fact >> 1), fact >> 1} +const int8_t ff_vvc_filter_g[NUM_INTRA_LUMA_FACTS][NUM_INTRA_LUMA_TAPS] = { + 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[32] = { + 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..8955498e21 --- /dev/null +++ b/libavcodec/vvc/vvc_data.h @@ -0,0 +1,69 @@ +/* + * 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 NUM_INTRA_LUMA_TAPS 4 +#define NUM_INTRA_LUMA_FACTS 32 +extern const int8_t ff_vvc_filter_c[NUM_INTRA_LUMA_FACTS][NUM_INTRA_LUMA_TAPS]; +extern const int8_t ff_vvc_filter_g[NUM_INTRA_LUMA_FACTS][NUM_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 int8_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 */ diff --git a/libavcodec/vvc/vvc_ps.c b/libavcodec/vvc/vvc_ps.c new file mode 100644 index 0000000000..cf16712b1b --- /dev/null +++ b/libavcodec/vvc/vvc_ps.c @@ -0,0 +1,3379 @@ +/* + * 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/avcodec.h" +#include "libavcodec/golomb.h" + +#include "libavutil/imgutils.h" + +#include "vvc_data.h" +#include "vvc_ps.h" +#include "vvcdec.h" + +#define u(b, f, min, max) do { \ + f = get_bits(gb, b); \ + if ((f < (min)) || (f > (max))) { \ + av_log(log_ctx, AV_LOG_ERROR, \ + #f " %d should in range [%d, %d]", f, (min), (max)); \ + }\ +} while (0) + +#define uep(f, plus, min, max) do { \ + f = get_ue_golomb_long(gb) + plus; \ + if (((f) < (min)) || ((f) > (max))) {\ + av_log(log_ctx, AV_LOG_ERROR, \ + #f " %d should in range [%d, %d]", f, (min), (max)); \ + return AVERROR_INVALIDDATA; \ + } \ +} while (0) + +#define ue(f, max) do { \ + f = get_ue_golomb_long(gb); \ + if ((f) > (max)) {\ + av_log(log_ctx, AV_LOG_ERROR, \ + #f " %d should < %d", f, (max)); \ + return AVERROR_INVALIDDATA; \ + } \ +} while (0) + +#define sep(f, plus, min, max) do {\ + f = get_se_golomb(gb) + plus; \ + if ((f < (min)) || (f > (max))) {\ + av_log(log_ctx, AV_LOG_ERROR, \ + #f " %d should in range [%d, %d]", f, (min), (max)); \ + return AVERROR_INVALIDDATA; \ + } \ +} while (0) + +#define se(f, min, max) do {\ + f = get_se_golomb(gb); \ + if ((f < (min)) || (f > (max))) {\ + av_log(log_ctx, AV_LOG_ERROR, \ + #f " %d should in range [%d, %d]", f, (min), (max)); \ + return AVERROR_INVALIDDATA; \ + } \ +} while (0) + +typedef struct VVCLMCS { + int min_bin_idx; + int max_bin_idx; + int delta_cw[LMCS_MAX_BIN_SIZE]; + int delta_crs; +} VVCLMCS; + +static av_always_inline unsigned int vvc_ceil(unsigned int v, unsigned int align) +{ + return (((v) + (align) - 1) / (align)); +} + +static void remove_pps(VVCParamSets *s, int id) +{ + av_buffer_unref(&s->pps_list[id]); +} + +static void remove_sps(VVCParamSets *s, int id) +{ + if (s->sps_list[id]) { + /* drop all PPS that depend on this SPS */ + for (int i = 0; i < FF_ARRAY_ELEMS(s->pps_list); i++) + if (s->pps_list[i] && ((VVCPPS*)s->pps_list[i]->data)->seq_parameter_set_id == id) + remove_pps(s, i); + } + av_buffer_unref(&s->sps_list[id]); +} + +static int gci_parse(GeneralConstraintsInfo *gci, GetBitContext *gb, void *log_ctx) +{ + int i; + + gci->present_flag = get_bits1(gb); + if (gci->present_flag) { + unsigned int num_reserved_bits; + /* general */ + gci->intra_only_constraint_flag = get_bits1(gb); + gci->all_layers_independent_constraint_flag = get_bits1(gb); + gci->one_au_only_constraint_flag = get_bits1(gb); + + /* picture format */ + gci->sixteen_minus_max_bitdepth_constraint_idc = get_bits(gb, 4); + if (gci->sixteen_minus_max_bitdepth_constraint_idc > 8U) { + av_log(log_ctx, AV_LOG_ERROR, + "sixteen_minus_max_bitdepth_constraint_idc %d is invalid\n", + gci->sixteen_minus_max_bitdepth_constraint_idc); + return AVERROR_INVALIDDATA; + } + gci-> three_minus_max_chroma_format_constraint_idc = get_bits(gb, 2); + + /* NAL unit type related */ + gci->no_mixed_nalu_types_in_pic_constraint_flag = get_bits1(gb); + gci->no_trail_constraint_flag = get_bits1(gb); + gci->no_stsa_constraint_flag = get_bits1(gb); + gci->no_rasl_constraint_flag = get_bits1(gb); + gci->no_radl_constraint_flag = get_bits1(gb); + gci->no_idr_constraint_flag = get_bits1(gb); + gci->no_cra_constraint_flag = get_bits1(gb); + gci->no_gdr_constraint_flag = get_bits1(gb); + gci->no_aps_constraint_flag = get_bits1(gb); + gci->no_idr_rpl_constraint_flag = get_bits1(gb); + + /* tile, slice, subpicture partitioning */ + gci->one_tile_per_pic_constraint_flag = get_bits1(gb); + gci->pic_header_in_slice_header_constraint_flag = get_bits1(gb); + gci->one_slice_per_pic_constraint_flag = get_bits1(gb); + gci->no_rectangular_slice_constraint_flag = get_bits1(gb); + gci->one_slice_per_subpic_constraint_flag = get_bits1(gb); + gci->no_subpic_info_constraint_flag = get_bits1(gb); + + /* CTU and block partitioning */ + gci->three_minus_max_log2_ctu_size_constraint_idc = get_bits(gb, 2); + gci->no_partition_constraints_override_constraint_flag = get_bits1(gb); + gci->no_mtt_constraint_flag = get_bits1(gb); + gci->no_qtbtt_dual_tree_intra_constraint_flag = get_bits1(gb); + + /* intra */ + gci->no_palette_constraint_flag = get_bits1(gb); + gci->no_ibc_constraint_flag = get_bits1(gb); + gci->no_isp_constraint_flag = get_bits1(gb); + gci->no_mrl_constraint_flag = get_bits1(gb); + gci->no_mip_constraint_flag = get_bits1(gb); + gci->no_cclm_constraint_flag = get_bits1(gb); + + /* inter */ + gci->no_ref_pic_resampling_constraint_flag = get_bits1(gb); + gci->no_res_change_in_clvs_constraint_flag = get_bits1(gb); + gci->no_weighted_prediction_constraint_flag = get_bits1(gb); + gci->no_ref_wraparound_constraint_flag = get_bits1(gb); + gci->no_temporal_mvp_constraint_flag = get_bits1(gb); + gci->no_sbtmvp_constraint_flag = get_bits1(gb); + gci->no_amvr_constraint_flag = get_bits1(gb); + gci->no_bdof_constraint_flag = get_bits1(gb); + gci->no_smvd_constraint_flag = get_bits1(gb); + gci->no_dmvr_constraint_flag = get_bits1(gb); + gci->no_mmvd_constraint_flag = get_bits1(gb); + gci->no_affine_motion_constraint_flag = get_bits1(gb); + gci->no_prof_constraint_flag = get_bits1(gb); + gci->no_bcw_constraint_flag = get_bits1(gb); + gci->no_ciip_constraint_flag = get_bits1(gb); + gci->no_gpm_constraint_flag = get_bits1(gb); + + /* transform, quantization, residual */ + gci->no_luma_transform_size_64_constraint_flag = get_bits1(gb); + gci->no_transform_skip_constraint_flag = get_bits1(gb); + gci->no_bdpcm_constraint_flag = get_bits1(gb); + gci->no_mts_constraint_flag = get_bits1(gb); + gci->no_lfnst_constraint_flag = get_bits1(gb); + gci->no_joint_cbcr_constraint_flag = get_bits1(gb); + gci->no_sbt_constraint_flag = get_bits1(gb); + gci->no_act_constraint_flag = get_bits1(gb); + gci->no_explicit_scaling_list_constraint_flag = get_bits1(gb); + gci->no_dep_quant_constraint_flag = get_bits1(gb); + gci->no_sign_data_hiding_constraint_flag = get_bits1(gb); + gci->no_cu_qp_delta_constraint_flag = get_bits1(gb); + gci->no_chroma_qp_offset_constraint_flag = get_bits1(gb); + + /* loop filter */ + gci->no_sao_constraint_flag = get_bits1(gb); + gci->no_alf_constraint_flag = get_bits1(gb); + gci->no_ccalf_constraint_flag = get_bits1(gb); + gci->no_lmcs_constraint_flag = get_bits1(gb); + gci->no_ladf_constraint_flag = get_bits1(gb); + gci->no_virtual_boundaries_constraint_flag = get_bits1(gb); + num_reserved_bits = get_bits(gb, 8); + for (i = 0; i < num_reserved_bits; i++) { + skip_bits1(gb); //reserved_zero_bit[i] + } + } + align_get_bits(gb); + return 0; +} + +static int ptl_parse(PTL *ptl, const int profile_tier_present_flag, + const int max_num_sub_layers_minus1, GetBitContext *gb, void *log_ctx) +{ + int ret, i; + + if (profile_tier_present_flag) { + ptl->general_profile_idc = get_bits(gb, 7); + ptl->general_tier_flag = get_bits1(gb); + } + ptl->general_level_idc = get_bits(gb, 8); + ptl->frame_only_constraint_flag = get_bits1(gb); + ptl->multilayer_enabled_flag = get_bits1(gb); + if (profile_tier_present_flag) { + ret = gci_parse(&ptl->gci, gb, log_ctx); + if (ret < 0) + return ret; + } + + for (i = max_num_sub_layers_minus1 - 1; i >= 0; i--) + ptl->sublayer_level_present_flag[i] = get_bits1(gb); + + align_get_bits(gb); + + for (i = max_num_sub_layers_minus1 - 1; i >= 0; i--) { + if (ptl->sublayer_level_present_flag[i]) + ptl->sublayer_level_idc[i] = get_bits(gb, 8); + } + + if (profile_tier_present_flag) { + ptl->num_sub_profiles = get_bits(gb, 8); + for (i = 0; i < ptl->num_sub_profiles; i++) + ptl->general_sub_profile_idc[i] = get_bits(gb, 32); + } + return 0; +} + +static int map_pixel_format(VVCSPS *sps, void *log_ctx) +{ + const AVPixFmtDescriptor *desc; + switch (sps->bit_depth) { + case 8: + if (sps->chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY8; + if (sps->chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P; + if (sps->chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P; + if (sps->chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P; + break; + case 10: + if (sps->chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY10; + if (sps->chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P10; + if (sps->chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P10; + if (sps->chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P10; + break; + default: + av_log(log_ctx, AV_LOG_ERROR, + "The following bit-depths are currently specified: 8, 10 bits, " + "chroma_format_idc is %d, depth is %d\n", + 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_parse_pic_resampling(VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + sps->ref_pic_resampling_enabled_flag = get_bits1(gb); + if (sps->ref_pic_resampling_enabled_flag) + sps->res_change_in_clvs_allowed_flag = get_bits1(gb); + if (sps->res_change_in_clvs_allowed_flag) { + avpriv_request_sample(log_ctx, "res_change_in_clvs_allowed_flag"); + return AVERROR_PATCHWELCOME; + } + + return 0; +} + +static void win_parse(VVCWindow* win, const VVCSPS *sps, GetBitContext *gb) +{ + win->left_offset = get_ue_golomb_long(gb) << sps->hshift[1]; + win->right_offset = get_ue_golomb_long(gb) << sps->hshift[1]; + win->top_offset = get_ue_golomb_long(gb) << sps->vshift[1]; + win->bottom_offset = get_ue_golomb_long(gb) << sps->vshift[1]; +} + +static void sps_parse_conf_win(VVCSPS *sps, GetBitContext *gb, AVCodecContext *avctx) +{ + if (get_bits1(gb)) { // sps_conformance_window_flag + win_parse(&sps->conf_win, sps, gb); + + if (avctx->flags2 & AV_CODEC_FLAG2_IGNORE_CROP) { + av_log(avctx, AV_LOG_DEBUG, + "discarding sps conformance window, " + "original values are l:%u r:%u t:%u b:%u\n", + sps->conf_win.left_offset, + sps->conf_win.right_offset, + sps->conf_win.top_offset, + sps->conf_win.bottom_offset); + + sps->conf_win.left_offset = + sps->conf_win.right_offset = + sps->conf_win.top_offset = + sps->conf_win.bottom_offset = 0; + } + sps->output_window = sps->conf_win; + } +} + +static void pps_parse_conf_win(VVCPPS *pps, const VVCSPS *sps, GetBitContext *gb) +{ + if (get_bits1(gb)) { // pps_conformance_window_flag + win_parse(&pps->conf_win, sps, gb); + } else if (pps->width == sps->width && pps->height == sps->height) { + pps->conf_win = sps->conf_win; + } +} + +static void scaling_win_parse(VVCWindow* win, const VVCSPS *sps, GetBitContext *gb) +{ + win->left_offset = get_se_golomb(gb) << sps->hshift[1]; + win->right_offset = get_se_golomb(gb) << sps->hshift[1]; + win->top_offset = get_se_golomb(gb) << sps->vshift[1]; + win->bottom_offset = get_se_golomb(gb) << sps->vshift[1]; +} + +static int pps_scaling_win_parse(VVCPPS *pps, const VVCSPS *sps, + GetBitContext *gb, void *log_ctx) +{ + if (get_bits1(gb)) { //pps_scaling_window_explicit_signalling_flag + if (!sps->ref_pic_resampling_enabled_flag) { + av_log(log_ctx, AV_LOG_ERROR, + "Invalid data: sps_ref_pic_resampling_enabled_flag is false, " + "but pps_scaling_window_explicit_signalling_flag is true.\n"); + return AVERROR_INVALIDDATA; + } + scaling_win_parse(&pps->scaling_win, sps, gb); + } else { + pps->scaling_win = pps->conf_win; + } + return 0; +} + +static int sps_parse_subpic(VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + int i; + unsigned int ctb_size_y = sps->ctb_size_y; + unsigned int tmp_width_val = + vvc_ceil(sps->width, ctb_size_y); + unsigned int tmp_height_val = + vvc_ceil(sps->height, ctb_size_y); + + sps->subpic_info_present_flag = get_bits1(gb); + if (sps->subpic_info_present_flag) { + sps->num_subpics = get_ue_golomb_long(gb) + 1; + if (sps->num_subpics > VVC_MAX_SLICES) { + av_log(log_ctx, AV_LOG_ERROR, "num_subpics out of range: %d\n", + sps->num_subpics); + return AVERROR_INVALIDDATA; + } + if (sps->num_subpics > 1) { + const int wlen = av_ceil_log2(tmp_width_val); + const int hlen = av_ceil_log2(tmp_height_val); + int sps_subpic_same_size_flag; + + sps->independent_subpics_flag = get_bits1(gb); + sps_subpic_same_size_flag = get_bits1(gb); + if (sps->width > ctb_size_y) + sps->subpic_width[0] = get_bits(gb, wlen) + 1; + else + sps->subpic_width[0] = tmp_width_val; + if (sps->height > ctb_size_y) + sps->subpic_height[0] = get_bits(gb, hlen) + 1; + else + sps->subpic_height[0] = tmp_height_val; + if (!sps->independent_subpics_flag) { + sps->subpic_treated_as_pic_flag[0] = get_bits1(gb); + sps->loop_filter_across_subpic_enabled_flag[0] = get_bits1(gb); + } else { + sps->subpic_treated_as_pic_flag[0] = 1; + sps->loop_filter_across_subpic_enabled_flag[0] = 1; + } + for (i = 1; i < sps->num_subpics; i++) { + if (!sps_subpic_same_size_flag) { + if (sps->width > ctb_size_y) + sps->subpic_ctu_top_left_x[i] = get_bits(gb, wlen); + if (sps->height > ctb_size_y) + sps->subpic_ctu_top_left_y[i] = get_bits(gb, hlen); + if (i < sps->num_subpics - 1 && + sps->width > ctb_size_y) { + sps->subpic_width[i] = get_bits(gb, wlen) + 1; + } else { + sps->subpic_width[i] = + tmp_width_val - sps->subpic_ctu_top_left_x[i]; + } + if (i < sps->num_subpics - 1 && + sps->height > ctb_size_y) { + sps->subpic_height[i] = get_bits(gb, hlen) + 1; + } else { + sps->subpic_height[i] = + tmp_height_val - sps->subpic_ctu_top_left_y[i]; + } + } else { + int num_subpic_cols = + tmp_width_val / sps->subpic_width[0]; + sps->subpic_ctu_top_left_x[i] = + (i % num_subpic_cols) * sps->subpic_width[0]; + sps->subpic_ctu_top_left_y[i] = + (i / num_subpic_cols) * sps->subpic_height[0]; + sps->subpic_width[i] = sps->subpic_width[0]; + sps->subpic_height[i] = sps->subpic_height[0]; + } + if (!sps->independent_subpics_flag) { + sps->subpic_treated_as_pic_flag[i] = get_bits1(gb); + sps->loop_filter_across_subpic_enabled_flag[i] = get_bits1(gb); + } else { + sps->subpic_treated_as_pic_flag[i] = 1; + } + } + sps->subpic_id_len = get_ue_golomb_long(gb) + 1; + + if (sps->subpic_id_len > 16U || + ((1 << sps->subpic_id_len) < sps->num_subpics)) { + av_log(log_ctx, AV_LOG_ERROR, + "subpic_id_len(%d) is invalid\n", + sps->subpic_id_len); + return AVERROR_INVALIDDATA; + } + sps->subpic_id_mapping_explicitly_signalled_flag = get_bits1(gb); + if (sps->subpic_id_mapping_explicitly_signalled_flag) { + sps->subpic_id_mapping_present_flag = get_bits1(gb); + if (sps->subpic_id_mapping_present_flag) { + for (i = 0; i < sps->num_subpics; i++) { + sps->subpic_id[i] = get_bits(gb, sps->subpic_id_len); + } + } + } + } else { + sps->subpic_ctu_top_left_x[0] = 0; + sps->subpic_ctu_top_left_y[0] = 0; + sps->subpic_width[0] = tmp_width_val; + sps->subpic_height[0] = tmp_height_val; + } + } else { + sps->num_subpics = 1; + sps->independent_subpics_flag = 1; + sps->subpic_width[0] = tmp_width_val; + sps->subpic_height[0] = tmp_height_val; + } + + return 0; +} + +static int sps_parse_bit_depth(VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + sps->bit_depth = get_ue_golomb_long(gb) + 8; + if (sps->bit_depth > 10) { + avpriv_report_missing_feature(log_ctx, "%d bits", sps->bit_depth); + return AVERROR_PATCHWELCOME; + } + + sps->qp_bd_offset = 6 * (sps->bit_depth - 8); + return map_pixel_format(sps, log_ctx); +} + +static int sps_parse_poc(VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + sps->log2_max_pic_order_cnt_lsb = get_bits(gb, 4) + 4; + if (sps->log2_max_pic_order_cnt_lsb > 16U) { + av_log(log_ctx, AV_LOG_ERROR, + "log2_max_pic_order_cnt_lsb %d > 16 is invalid", + sps->log2_max_pic_order_cnt_lsb); + return AVERROR_INVALIDDATA; + } + sps->max_pic_order_cnt_lsb = 1 << sps->log2_max_pic_order_cnt_lsb; + + sps->poc_msb_cycle_flag = get_bits1(gb); + if (sps->poc_msb_cycle_flag) + uep(sps->poc_msb_cycle_len, 1, 1, 32 - sps->log2_max_pic_order_cnt_lsb); + + return 0; +} + +static int sps_parse_extra_bytes(VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + u(2, sps->num_extra_ph_bytes, 0, 2); + for (int i = 0; i < (sps->num_extra_ph_bytes * 8); i++) + sps->extra_ph_bit_present_flag[i] = get_bits1(gb); + + u(2, sps->num_extra_sh_bytes, 0, 2); + for (int i = 0; i < (sps->num_extra_sh_bytes * 8); i++) + sps->extra_sh_bit_present_flag[i] = get_bits1(gb); + + return 0; +} + +static void dpb_parameters_parse(DpbParameters *dpb, const uint8_t max_sublayers_minus1, + const uint8_t sublayer_info_flag, GetBitContext *gb) +{ + int i = (sublayer_info_flag ? 0 : max_sublayers_minus1); + for (/* nothing */; i <= max_sublayers_minus1; i++) { + dpb->max_dec_pic_buffering[i] = get_ue_golomb_long(gb) + 1; + dpb->max_num_reorder_pics[i] = get_ue_golomb_long(gb); + dpb->max_latency_increase[i] = get_ue_golomb_long(gb) - 1; + } +} + +static int partition_constraints_parse(PartitionConstraints *pc, const VVCSPS *sps, + GetBitContext *gb, void *log_ctx) +{ + const int max = FFMIN(6, sps->ctb_log2_size_y); + + ue(pc->log2_diff_min_qt_min_cb, max - sps->min_cb_log2_size_y); + ue(pc->max_mtt_hierarchy_depth, 2 * (sps->ctb_log2_size_y - sps->min_cb_log2_size_y)); + if (pc->max_mtt_hierarchy_depth) { + const int min_qt_log2_size = pc->log2_diff_min_qt_min_cb + sps->min_cb_log2_size_y; + ue(pc->log2_diff_max_bt_min_qt, sps->ctb_log2_size_y - min_qt_log2_size); + ue(pc->log2_diff_max_tt_min_qt, FFMIN(6, sps->ctb_log2_size_y) - min_qt_log2_size); + } + + return 0; +} + +static int sps_parse_partition_constraints(VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + int ret = 0; + + sps->partition_constraints_override_enabled_flag = get_bits1(gb); + + ret = partition_constraints_parse(&sps->intra_slice_luma, sps, gb, log_ctx); + if (ret < 0) + return ret; + + if (sps->chroma_format_idc != 0) { + sps->qtbtt_dual_tree_intra_flag = get_bits1(gb); + if (sps->qtbtt_dual_tree_intra_flag) { + ret = partition_constraints_parse(&sps->intra_slice_chroma, sps, gb, log_ctx); + if (ret < 0) + return ret; + } + } + + ret = partition_constraints_parse(&sps->inter_slice, sps, gb, log_ctx); + if (ret < 0) + return ret; + + return ret; +} + +static int rpls_parse(VVCRefPicListStruct *rpls, GetBitContext *gb, + const uint8_t list_idx, const uint8_t rpls_idx, const VVCSPS *sps, void *log_ctx) +{ + int i, j; + + rpls->num_ltrp_entries = 0; + ue(rpls->num_ref_entries, VVC_MAX_REF_ENTRIES); + if (sps->long_term_ref_pics_flag && + rpls_idx < sps->num_ref_pic_lists[list_idx] && + rpls->num_ref_entries > 0) + rpls->ltrp_in_header_flag = get_bits1(gb); + if (sps->long_term_ref_pics_flag && + rpls_idx == sps->num_ref_pic_lists[list_idx]) + rpls->ltrp_in_header_flag = 1; + for (i = 0, j = 0; i < rpls->num_ref_entries; i++) { + VVCRefPicListStructEntry *entry = &rpls->entries[i]; + if (sps->inter_layer_prediction_enabled_flag) + entry->inter_layer_ref_pic_flag = get_bits1(gb); + + if (!entry->inter_layer_ref_pic_flag) { + if (sps->long_term_ref_pics_flag) + entry->st_ref_pic_flag = get_bits1(gb); + else + entry->st_ref_pic_flag = 1; + if (entry->st_ref_pic_flag) { + int abs_delta_poc_st, strp_entry_sign_flag = 0; + ue(abs_delta_poc_st, (1<<15) - 1); + if (!((sps->weighted_pred_flag || + sps->weighted_bipred_flag) && i != 0)) + abs_delta_poc_st++; + if (abs_delta_poc_st > 0) + strp_entry_sign_flag = get_bits1(gb); + entry->delta_poc_val_st = (1 - 2 * strp_entry_sign_flag) * abs_delta_poc_st; + } else { + if (!rpls->ltrp_in_header_flag) { + uint8_t bits = sps->log2_max_pic_order_cnt_lsb; + entry->lt_poc = get_bits(gb, bits); + j++; + } + rpls->num_ltrp_entries++; + } + } else { + avpriv_request_sample(log_ctx, "Inter layer ref"); + return AVERROR_PATCHWELCOME; + } + } + + return 0; +} + +static int sps_parse_chroma_qp_table(VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + const int sps_same_qp_table_for_chroma_flag = get_bits1(gb); + const int num_qp_tables = sps_same_qp_table_for_chroma_flag ? + 1 : (sps->joint_cbcr_enabled_flag ? 3 : 2); + + for (int i = 0; i < num_qp_tables; i++) { + int qp_table_start_minus26, num_points_in_qp_table_minus1; + int num_points_in_qp_table; + int 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; + + se(qp_table_start_minus26, -26 - sps->qp_bd_offset, 36); + ue(num_points_in_qp_table_minus1, 36 - qp_table_start_minus26); + num_points_in_qp_table = num_points_in_qp_table_minus1 + 1; + + qp_out[0] = qp_in[0] = qp_table_start_minus26 + 26; + for (int j = 0; j < num_points_in_qp_table; j++ ) { + const uint8_t max = 0xff; + unsigned int delta_qp_diff_val, delta_qp_in_val_minus1; + + ue(delta_qp_in_val_minus1, max); + ue(delta_qp_diff_val, max); + delta_qp_in[j] = delta_qp_in_val_minus1 + 1; + qp_in[j+1] = qp_in[j] + delta_qp_in[j]; + qp_out[j+1] = qp_out[j] + (delta_qp_in_val_minus1 ^ delta_qp_diff_val); + } + 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 (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 int sps_parse_transform(VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + int ret = 0; + + sps->transform_skip_enabled_flag = get_bits1(gb); + if (sps->transform_skip_enabled_flag) { + sps->max_ts_size = 1 << (get_ue_golomb_long(gb) + 2); + if (sps->max_ts_size > 32) { + av_log(log_ctx, AV_LOG_ERROR, + "sps_log2_transform_skip_max_size_minus2 > 3 is invalid"); + return AVERROR_INVALIDDATA; + } + sps->bdpcm_enabled_flag = get_bits1(gb); + } + + sps->mts_enabled_flag = get_bits1(gb); + if (sps->mts_enabled_flag) { + sps->explicit_mts_intra_enabled_flag = get_bits1(gb); + sps->explicit_mts_inter_enabled_flag = get_bits1(gb); + } + + sps->lfnst_enabled_flag = get_bits1(gb); + + if (sps->chroma_format_idc != 0) { + sps->joint_cbcr_enabled_flag = get_bits1(gb); + ret = sps_parse_chroma_qp_table(sps, gb, log_ctx); + if (ret < 0) + return ret; + } + + return ret; +} + +static void sps_parse_filter(VVCSPS *sps, GetBitContext *gb) +{ + sps->sao_enabled_flag = get_bits1(gb); + + sps->alf_enabled_flag = get_bits1(gb); + if (sps->alf_enabled_flag && sps->chroma_format_idc) + sps->ccalf_enabled_flag = get_bits1(gb); + + sps->lmcs_enabled_flag = get_bits1(gb); +} + +static int sps_parse_rpls(VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + int sps_rpl1_same_as_rpl0_flag; + + sps->idr_rpl_present_flag = get_bits1(gb); + sps_rpl1_same_as_rpl0_flag = get_bits1(gb); + for (int i = 0; i < (sps_rpl1_same_as_rpl0_flag ? 1 : 2); i++) { + ue(sps->num_ref_pic_lists[i], VVC_MAX_REF_PIC_LISTS); + for (int j = 0; j < sps->num_ref_pic_lists[i]; j++) { + int ret = rpls_parse(&sps->ref_pic_list_struct[i][j], gb, i, j, sps, log_ctx); + if (ret < 0) + return ret; + } + } + + if (sps_rpl1_same_as_rpl0_flag) { + sps->num_ref_pic_lists[1] = sps->num_ref_pic_lists[0]; + for (int j = 0; j < sps->num_ref_pic_lists[0]; j++) + memcpy(&sps->ref_pic_list_struct[1][j], + &sps->ref_pic_list_struct[0][j], + sizeof(sps->ref_pic_list_struct[0][j])); + } + + return 0; +} + +static int sps_parse_max_num_gpm_merge_cand(VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + sps->max_num_gpm_merge_cand = 0; + if (sps->max_num_merge_cand >= 2) { + sps->gpm_enabled_flag = get_bits1(gb); + if (sps->gpm_enabled_flag) { + sps->max_num_gpm_merge_cand = 2; + if (sps->max_num_merge_cand >= 3) { + uint8_t sps_max_num_merge_cand_minus_max_num_gpm_cand; + ue(sps_max_num_merge_cand_minus_max_num_gpm_cand, sps->max_num_merge_cand - 2); + sps->max_num_gpm_merge_cand = sps->max_num_merge_cand - sps_max_num_merge_cand_minus_max_num_gpm_cand; + } + } + } + + return 0; +} + +static int sps_parse_inter(VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + int ret, sps_six_minus_max_num_merge_cand; + + sps->weighted_pred_flag = get_bits1(gb); + sps->weighted_bipred_flag = get_bits1(gb); + sps->long_term_ref_pics_flag = get_bits1(gb); + if (sps->video_parameter_set_id > 0) + sps->inter_layer_prediction_enabled_flag = get_bits1(gb); + + ret = sps_parse_rpls(sps, gb, log_ctx); + if (ret < 0) + return ret; + + sps->ref_wraparound_enabled_flag = get_bits1(gb); + + sps->temporal_mvp_enabled_flag = get_bits1(gb); + if (sps->temporal_mvp_enabled_flag) + sps->sbtmvp_enabled_flag = get_bits1(gb); + + sps->amvr_enabled_flag = get_bits1(gb); + sps->bdof_enabled_flag = get_bits1(gb); + if (sps->bdof_enabled_flag) + sps->bdof_control_present_in_ph_flag = get_bits1(gb); + + sps->smvd_enabled_flag = get_bits1(gb); + sps->dmvr_enabled_flag = get_bits1(gb); + if (sps->dmvr_enabled_flag) + sps->dmvr_control_present_in_ph_flag = get_bits1(gb); + + sps->mmvd_enabled_flag = get_bits1(gb); + if (sps->mmvd_enabled_flag) + sps->mmvd_fullpel_only_enabled_flag = get_bits1(gb); + + ue(sps_six_minus_max_num_merge_cand, 5); + sps->max_num_merge_cand = 6 - sps_six_minus_max_num_merge_cand; + + sps->sbt_enabled_flag = get_bits1(gb); + + sps->affine_enabled_flag = get_bits1(gb); + if (sps->affine_enabled_flag) { + ue(sps->five_minus_max_num_subblock_merge_cand, + 5 - sps->sbtmvp_enabled_flag); + sps->six_param_affine_enabled_flag = get_bits1(gb); + if (sps->amvr_enabled_flag) + sps->affine_amvr_enabled_flag = get_bits1(gb); + sps->affine_prof_enabled_flag = get_bits1(gb); + if (sps->affine_prof_enabled_flag) + sps->prof_control_present_in_ph_flag = get_bits1(gb); + } + + sps->bcw_enabled_flag = get_bits1(gb); + sps->ciip_enabled_flag = get_bits1(gb); + + ret = sps_parse_max_num_gpm_merge_cand(sps, gb, log_ctx); + if (ret < 0) + return ret; + + uep(sps->log2_parallel_merge_level, 2, 2, sps->ctb_log2_size_y); + + return 0; +} + +static int sps_parse_intra(VVCSPS *sps, const int sps_max_luma_transform_size_64_flag, + GetBitContext *gb, void *log_ctx) +{ + sps->isp_enabled_flag = get_bits1(gb); + sps->mrl_enabled_flag = get_bits1(gb); + sps->mip_enabled_flag = get_bits1(gb); + + if (sps->chroma_format_idc != 0) + sps->cclm_enabled_flag = get_bits1(gb); + if (sps->chroma_format_idc == 1) { + sps->chroma_horizontal_collocated_flag = get_bits1(gb); + sps->chroma_vertical_collocated_flag = get_bits1(gb); + } else { + sps->chroma_horizontal_collocated_flag = 1; + sps->chroma_vertical_collocated_flag = 1; + } + + sps->palette_enabled_flag = get_bits1(gb); + if (sps->chroma_format_idc == 3 && + !sps_max_luma_transform_size_64_flag) + sps->act_enabled_flag = get_bits1(gb); + if (sps->transform_skip_enabled_flag || sps->palette_enabled_flag) + ue(sps->min_qp_prime_ts, 8); + + sps->ibc_enabled_flag = get_bits1(gb); + if (sps->ibc_enabled_flag) { + uint8_t six_minus_max_num_ibc_merge_cand; + ue(six_minus_max_num_ibc_merge_cand, 5); + sps->max_num_ibc_merge_cand = 6 - six_minus_max_num_ibc_merge_cand; + } + + return 0; +} + +static int sps_parse_ladf(VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + sps->ladf_enabled_flag = get_bits1(gb); + if (sps->ladf_enabled_flag) { + sps->num_ladf_intervals = get_bits(gb, 2) + 2; + se(sps->ladf_lowest_interval_qp_offset, -63, 63); + + sps->ladf_interval_lower_bound[0] = 0; + for (int i = 0; i < sps->num_ladf_intervals - 1; i++) { + int sps_ladf_delta_threshold_minus1; + + se(sps->ladf_qp_offset[i], -63, 63); + + ue(sps_ladf_delta_threshold_minus1, (2 << sps->bit_depth) - 3); + sps->ladf_interval_lower_bound[i + 1] = sps->ladf_interval_lower_bound[i] + sps_ladf_delta_threshold_minus1 + 1; + } + } + + return 0; +} + +static void sps_parse_dequant(VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + sps->explicit_scaling_list_enabled_flag = get_bits1(gb); + if (sps->lfnst_enabled_flag && sps->explicit_scaling_list_enabled_flag) + sps->scaling_matrix_for_lfnst_disabled_flag = get_bits1(gb); + + if (sps->act_enabled_flag && sps->explicit_scaling_list_enabled_flag) + sps->scaling_matrix_for_alternative_colour_space_disabled_flag = get_bits1(gb); + + if (sps->scaling_matrix_for_alternative_colour_space_disabled_flag) + sps->scaling_matrix_designated_colour_space_flag = get_bits1(gb); + + sps->dep_quant_enabled_flag = get_bits1(gb); + sps->sign_data_hiding_enabled_flag = get_bits1(gb); +} + +static int virtual_boundaries_parse(VirtualBoundaries *vbs, const int width, const int height, + GetBitContext *gb, void *log_ctx) +{ + vbs->virtual_boundaries_present_flag = get_bits1(gb); + if (vbs->virtual_boundaries_present_flag) { + ue(vbs->num_ver_virtual_boundaries, width <= 8 ? 0 : 3); + for (int i = 0; i < vbs->num_ver_virtual_boundaries; i++) + ue(vbs->virtual_boundary_pos_x_minus1[i], (width + 7) / 8 - 2); + ue(vbs->num_hor_virtual_boundaries, height <= 8 ? 0 : 3); + for (int i = 0; i < vbs->num_hor_virtual_boundaries; i++) + ue(vbs->virtual_boundary_pos_y_minus1[i], (height + 7) / 8 - 2); + } + + return 0; +} + +static int sps_parse_virtual_boundaries(VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + sps->virtual_boundaries_enabled_flag = get_bits1(gb); + if (sps->virtual_boundaries_enabled_flag) { + const int ret = virtual_boundaries_parse(&sps->vbs, sps->width, sps->height, gb, log_ctx); + if (ret < 0) + return ret; + } + + return 0; +} + +// 7.3.5.1 General timing and HRD parameters syntax +static int general_timing_hrd_parameters_parse(GeneralTimingHrdParameters *hrd, GetBitContext *gb, void *log_ctx) +{ + skip_bits_long(gb, 32); ///< num_units_in_tick + skip_bits_long(gb, 32); ///< time_scale + hrd->general_nal_hrd_params_present_flag = get_bits1(gb); + hrd->general_vcl_hrd_params_present_flag = get_bits1(gb); + if (hrd->general_nal_hrd_params_present_flag || hrd->general_vcl_hrd_params_present_flag) { + skip_bits1(gb); ///< general_same_pic_timing_in_all_ols_flag + hrd->general_du_hrd_params_present_flag = get_bits1(gb); + if (hrd->general_du_hrd_params_present_flag) + skip_bits(gb, 8); ///< tick_divisor_minus2 + skip_bits(gb, 4); ///< bit_rate_scale + skip_bits(gb, 4); ///< cpb_size_scale + if (hrd->general_du_hrd_params_present_flag) + skip_bits(gb, 4); ///< cpb_size_du_scale + uep(hrd->hrd_cpb_cnt, 1, 1, 32); + } + return 0; +} + +static int ols_timing_hrd_parameters_parse(const GeneralTimingHrdParameters *hrd, + const uint8_t first_sublayer, const uint8_t last_sublayer, GetBitContext *gb, void *log_ctx) +{ + const int hrd_params_present_flag = hrd->general_nal_hrd_params_present_flag || + hrd->general_vcl_hrd_params_present_flag; + int elemental_duration_in_tc_minus1; + for (uint8_t i = first_sublayer; i < last_sublayer; i++) { + int fixed_pic_rate_within_cvs_flag = 0; + if (!get_bits1(gb)) ///< fixed_pic_rate_general_flag + fixed_pic_rate_within_cvs_flag = get_bits1(gb); + if (fixed_pic_rate_within_cvs_flag) + ue(elemental_duration_in_tc_minus1, 2047); + else if (hrd_params_present_flag && hrd->hrd_cpb_cnt == 1) + skip_bits1(gb); ///< low_delay_hrd_flag + + if (hrd_params_present_flag) { + int bit_rate_value_minus1, cpb_size_value_minus1; + int cpb_size_du_value_minus1, bit_rate_du_value_minus1; + for (int j = 0; j < hrd->hrd_cpb_cnt; j++) { + ue(bit_rate_value_minus1, UINT32_MAX - 1); + ue(cpb_size_value_minus1, UINT32_MAX - 1); + if (hrd->general_du_hrd_params_present_flag) { + ue(cpb_size_du_value_minus1, UINT32_MAX - 1); + ue(bit_rate_du_value_minus1, UINT32_MAX - 1); + } + skip_bits1(gb); ///< cbr_flag + } + } + } + return 0; +} + +static int sps_parse_hrd(VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + int ret; + sps->timing_hrd_params_present_flag = get_bits1(gb); + if (sps->timing_hrd_params_present_flag) { + uint8_t first_sublayer = 0 ; + GeneralTimingHrdParameters *hrd = &sps->general_timing_hrd_parameters; + ret = general_timing_hrd_parameters_parse(hrd, gb, log_ctx); + if (ret < 0) + return ret; + if (sps->max_sublayers > 1 && get_bits1(gb)) ///< sps_sublayer_cpb_params_present_flag + first_sublayer = sps->max_sublayers - 1; + ret = ols_timing_hrd_parameters_parse(hrd, first_sublayer, sps->max_sublayers, gb, log_ctx); + if (ret < 0) + return ret; + } + return 0; +} + +static int sps_parse_vui(VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + VUI *vui = &sps->vui; + + if (get_bits1(gb)) { //vui_parameters_present_flag + uep(vui->payload_size, 1, 1, 1024); + align_get_bits(gb); + //fixme + } else { + vui->colour_primaries = 2; + vui->transfer_characteristics = 2; + vui->matrix_coeffs = 2; + vui->chroma_sample_loc_type_frame = 6; + vui->chroma_sample_loc_type_top_field = 6; + vui->chroma_sample_loc_type_bottom_field = 6; + } + + return 0; +} + +static int sps_parse(VVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, + int apply_defdispwin, AVBufferRef **vps_list, AVCodecContext *avctx, int nuh_layer_id) +{ + int sps_ptl_dpb_hrd_params_present_flag; + int ret = 0, sps_max_luma_transform_size_64_flag = 0; + const VVCVPS *vps; + void *log_ctx = avctx; + + // Coded parameters + + *sps_id = get_bits(gb, 4); + + sps->video_parameter_set_id = get_bits(gb, 4); + if (vps_list && !vps_list[sps->video_parameter_set_id]) { + if (!sps->video_parameter_set_id) { + VVCVPS *vps0; + AVBufferRef* buf = av_buffer_allocz(sizeof(*vps)); + if (!buf) + return AVERROR(ENOMEM); + vps_list[0] = buf; + vps0 = (VVCVPS *)buf->data; + vps0->max_layers = 1; + vps0->independent_layer_flag[0] = 1; + vps0->layer_id[0] = nuh_layer_id; + } else { + av_log(log_ctx, AV_LOG_ERROR, "VPS %d does not exist\n", + sps->video_parameter_set_id); + return AVERROR_INVALIDDATA; + } + } + vps = (VVCVPS*)vps_list[sps->video_parameter_set_id]->data; + + sps->max_sublayers = get_bits(gb, 3) + 1; + if (sps->max_sublayers > VVC_MAX_SUBLAYERS) { + av_log(log_ctx, AV_LOG_ERROR, "sps_max_sublayers out of range: %d\n", + sps->max_sublayers); + return AVERROR_INVALIDDATA; + } + + sps->chroma_format_idc = get_bits(gb, 2); + + sps->ctb_log2_size_y = get_bits(gb, 2) + 5; + if (sps->ctb_log2_size_y == 8) { + av_log(log_ctx, AV_LOG_ERROR, "sps_log2_ctu_size_minus5 can't be 3\n"); + return AVERROR_INVALIDDATA; + } + sps->ctb_size_y = 1 << sps->ctb_log2_size_y; + + sps_ptl_dpb_hrd_params_present_flag = get_bits1(gb); + if (sps_ptl_dpb_hrd_params_present_flag) { + ret = ptl_parse(&sps->ptl, 1, sps->max_sublayers - 1, gb, log_ctx); + if (ret < 0) + return ret; + } + skip_bits1(gb); ///< sps_gdr_enabled_flag + + ret = sps_parse_pic_resampling(sps, gb, log_ctx); + if (ret < 0) + return ret; + + ue(sps->width, VVC_MAX_WIDTH); + ue(sps->height, VVC_MAX_HEIGHT); + if (!sps->width || !sps->height) + return AVERROR_INVALIDDATA; + + sps_parse_conf_win(sps, gb, avctx); + + ret = sps_parse_subpic(sps, gb, log_ctx); + if (ret < 0) + return ret; + + ret = sps_parse_bit_depth(sps, gb, log_ctx); + if (ret < 0) + return ret; + + sps->entropy_coding_sync_enabled_flag = get_bits1(gb); + sps->entry_point_offsets_present_flag = get_bits1(gb); + + ret = sps_parse_poc(sps, gb, log_ctx); + if (ret < 0) + return ret; + + ret = sps_parse_extra_bytes(sps, gb, log_ctx); + if (ret < 0) + return ret; + + if (sps_ptl_dpb_hrd_params_present_flag) { + const int sps_sublayer_dpb_params_flag = sps->max_sublayers > 1 ? get_bits1(gb) : 0; + dpb_parameters_parse(&sps->dpb, sps->max_sublayers - 1, sps_sublayer_dpb_params_flag, gb); + } + + uep(sps->min_cb_log2_size_y, 2, 2, FFMIN(6, sps->ctb_log2_size_y)); + sps->min_cb_size_y = 1 << sps->min_cb_log2_size_y; + + ret = sps_parse_partition_constraints(sps, gb, log_ctx); + if (ret < 0) + return ret; + + if (sps->ctb_size_y > 32) + sps_max_luma_transform_size_64_flag = get_bits1(gb); + sps->max_tb_size_y = 1 << (sps_max_luma_transform_size_64_flag ? 6 : 5); + + ret = sps_parse_transform(sps, gb, log_ctx); + if (ret < 0) + return ret; + + sps_parse_filter(sps, gb); + + ret = sps_parse_inter(sps, gb, log_ctx); + if (ret < 0) + return ret; + + ret = sps_parse_intra(sps, sps_max_luma_transform_size_64_flag, gb, log_ctx); + if (ret < 0) + return ret; + + ret = sps_parse_ladf(sps, gb, log_ctx); + if (ret < 0) + return ret; + + sps_parse_dequant(sps, gb, log_ctx); + + ret = sps_parse_virtual_boundaries(sps, gb, log_ctx); + if (ret < 0) + return ret; + + if (sps_ptl_dpb_hrd_params_present_flag) { + ret = sps_parse_hrd(sps, gb, log_ctx); + if (ret < 0) + return ret; + } + + sps->field_seq_flag = get_bits1(gb); + + ret = sps_parse_vui(sps, gb, log_ctx); + if (ret < 0) + return ret; + + skip_bits1(gb); ///< sps_extension_flag + + if (get_bits_left(gb) < 0) { + av_log(log_ctx, AV_LOG_ERROR, + "Overread SPS by %d bits\n", -get_bits_left(gb)); + return AVERROR_INVALIDDATA; + } + + return 0; +} + +int ff_vvc_decode_sps(VVCParamSets *ps, GetBitContext *gb, + int apply_defdispwin, int nuh_layer_id, AVCodecContext *avctx) +{ + VVCSPS *sps; + AVBufferRef *sps_buf = av_buffer_allocz(sizeof(*sps)); + unsigned int sps_id; + int ret; + ptrdiff_t nal_size; + + if (!sps_buf) + return AVERROR(ENOMEM); + sps = (VVCSPS*)sps_buf->data; + + av_log(avctx, AV_LOG_DEBUG, "Decoding SPS\n"); + + nal_size = gb->buffer_end - gb->buffer; + if (nal_size > sizeof(sps->data)) { + av_log(avctx, AV_LOG_WARNING, "Truncating likely oversized SPS " + "(%"PTRDIFF_SPECIFIER" > %"SIZE_SPECIFIER")\n", + nal_size, sizeof(sps->data)); + sps->data_size = sizeof(sps->data); + } else { + sps->data_size = nal_size; + } + memcpy(sps->data, gb->buffer, sps->data_size); + + ret = sps_parse(sps, gb, &sps_id, apply_defdispwin, ps->vps_list, avctx, nuh_layer_id); + if (ret < 0) { + av_buffer_unref(&sps_buf); + return ret; + } + + if (avctx->debug & FF_DEBUG_BITSTREAM) { + av_log(avctx, AV_LOG_DEBUG, + "Parsed SPS: id %d; coded wxh: %dx%d; " + "cropped wxh: %dx%d; pix_fmt: %s.\n", + sps_id, sps->width, sps->height, + sps->width - (sps->output_window.left_offset + sps->output_window.right_offset), + sps->height - (sps->output_window.top_offset + sps->output_window.bottom_offset), + av_get_pix_fmt_name(sps->pix_fmt)); + } + + /* check if this is a repeat of an already parsed SPS, then keep the + * original one. + * otherwise drop all PPSes that depend on it */ + if (ps->sps_list[sps_id] && + !memcmp(ps->sps_list[sps_id]->data, sps_buf->data, sps_buf->size)) { + av_buffer_unref(&sps_buf); + } else { + remove_sps(ps, sps_id); + ps->sps_list[sps_id] = sps_buf; + } + return 0; +} + +static void vvc_pps_free(void *opaque, uint8_t *data) +{ + VVCPPS *pps = (VVCPPS *)data; + + av_freep(&pps->column_width); + av_freep(&pps->row_height); + 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); + av_freep(&pps); +} + +static int pps_parse_width_height(VVCPPS *pps, const VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + const int divisor = FFMAX(sps->min_cb_size_y, 8); + + ue(pps->width, sps->width); + ue(pps->height, sps->height); + + if (!pps->width || !pps->height) + return AVERROR_INVALIDDATA; + + if (pps->width % divisor || pps->height % divisor) { + av_log(log_ctx, AV_LOG_ERROR, + "Invalid dimensions: %ux%u not divisible " + "by %u, MinCbSizeY = %u.\n", pps->width, + pps->height, divisor, sps->min_cb_size_y); + return AVERROR_INVALIDDATA; + } + if (!sps->res_change_in_clvs_allowed_flag && + (pps->width != sps->width || pps->height != sps->height)) { + av_log(log_ctx, AV_LOG_ERROR, + "Resoltuion change is not allowed, " + "in max resolution (%ux%u) mismatched with pps(%ux%u).\n", + sps->width, sps->height, + pps->width, pps->height); + return AVERROR_INVALIDDATA; + } + return 0; +} + +static unsigned int* pps_parse_tile_sizes(uint16_t *num, unsigned int num_exp, + unsigned int max_num, unsigned int max_size, GetBitContext *gb, void *log_ctx) +{ + unsigned int i, exp_tile_size = 0; + unsigned int unified_size, remaining_size; + unsigned int *sizes, *p; + sizes = av_malloc_array(num_exp, sizeof(*sizes)); + if (!sizes) + return NULL; + for (i = 0; i < num_exp; i++) { + sizes[i] = get_ue_golomb_long(gb) + 1; + if (exp_tile_size + sizes[i] > max_size) { + goto err; + } + exp_tile_size += sizes[i]; + } + remaining_size = max_size - exp_tile_size; + unified_size = (i == 0 ? max_size : sizes[i-1]); + *num = i + vvc_ceil(remaining_size, unified_size); + if (*num > max_num) { + av_log(log_ctx, AV_LOG_ERROR, "num(%d) large than %d.\n", *num, max_num); + goto err; + } + p = av_realloc_array(sizes, *num, sizeof(*sizes)); + if (!p) + goto err; + sizes = p; + while (remaining_size > unified_size) { + sizes[i] = unified_size; + remaining_size -= unified_size; + i++; + } + if (remaining_size > 0) { + sizes[i] = remaining_size; + } + return sizes; +err: + av_free(sizes); + return NULL; +} + +static inline unsigned int* pps_setup_bd(unsigned int *sizes, int num_sizes) +{ + unsigned int *bd = av_malloc_array(num_sizes + 1, sizeof(*bd)); + if (!bd) + return NULL; + *bd= 0; + for (int i = 0; i < num_sizes; i++) { + bd[i+1] = bd[i] + sizes[i]; + } + return bd; +} + +static int pps_setup(VVCPPS *pps, const VVCSPS *sps) +{ + int ret = AVERROR(ENOMEM), tile_x = 0, tile_y = 0; + + 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->ctb_to_col_bd || !pps->ctb_to_row_bd) + goto err; + + for (int ctb_addr_x = 0; ctb_addr_x < pps->ctb_width; ctb_addr_x++) { + if (ctb_addr_x == pps->col_bd[tile_x + 1]) + tile_x++; + pps->ctb_to_col_bd[ctb_addr_x] = pps->col_bd[tile_x]; + } + pps->ctb_to_col_bd[pps->ctb_width] = pps->ctb_width; + + for (int ctb_addr_y = 0; ctb_addr_y < pps->ctb_height; ctb_addr_y++) { + if (ctb_addr_y == pps->row_bd[tile_y + 1]) + tile_y++; + pps->ctb_to_row_bd[ctb_addr_y] = pps->row_bd[tile_y]; + } + pps->ctb_to_row_bd[pps->ctb_height] = pps->ctb_height; + + return 0; +err: + return ret; +} + +static int pps_parse_subpic_id(VVCPPS *pps, const VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + int pps_subpic_id_mapping_present_flag; + uint8_t subpic_id_len; + + pps->no_pic_partition_flag = get_bits1(gb); + if (sps->num_subpics > 1 || pps->mixed_nalu_types_in_pic_flag) { + if (pps->no_pic_partition_flag) { + av_log(log_ctx, AV_LOG_ERROR, + "no_pic_partition_flag should not true"); + return AVERROR_INVALIDDATA; + } + } + + pps_subpic_id_mapping_present_flag = get_bits1(gb); + if (pps_subpic_id_mapping_present_flag) { + if (!pps->no_pic_partition_flag) { + const uint16_t num_subpics = get_ue_golomb_long(gb) + 1; + if (num_subpics != sps->num_subpics) { + av_log(log_ctx, AV_LOG_ERROR, + "pps_num_subpics %u should equals to " + "sps_num_subpics %u .\n", num_subpics, sps->num_subpics); + return AVERROR_INVALIDDATA; + } + } + + subpic_id_len = get_ue_golomb_long(gb) + 1; + if (subpic_id_len != sps->subpic_id_len) { + av_log(log_ctx, AV_LOG_ERROR, + "pps_subpic_id_len %u should equals to " + "sps_subpic_id_len %u .\n", subpic_id_len, sps->subpic_id_len); + return AVERROR_INVALIDDATA; + } + + } + for (int i = 0; i < sps->num_subpics; i++) { + if (sps->subpic_id_mapping_explicitly_signalled_flag) { + if (pps_subpic_id_mapping_present_flag) + pps->subpic_id[i] = get_bits(gb, sps->subpic_id_len); + else + pps->subpic_id[i] = sps->subpic_id[i]; + } else { + pps->subpic_id[i] = i; + } + } + return 0; +} + +static int pps_parse_chroma_tool_offsets(VVCPPS *pps, GetBitContext *gb, void *log_ctx) +{ + uint8_t pps_joint_cbcr_qp_offset_present_flag; + + pps->chroma_tool_offsets_present_flag = get_bits1(gb); + if (!pps->chroma_tool_offsets_present_flag) + return 0; + + se(pps->chroma_qp_offset[CB - 1], -12, 12); + se(pps->chroma_qp_offset[CR - 1], -12, 12); + + pps_joint_cbcr_qp_offset_present_flag = get_bits1(gb); + if (pps_joint_cbcr_qp_offset_present_flag) + se(pps->chroma_qp_offset[JCBCR - 1], -12, 12); + pps->slice_chroma_qp_offsets_present_flag = get_bits1(gb); + pps->cu_chroma_qp_offset_list_enabled_flag = get_bits1(gb); + if (pps->cu_chroma_qp_offset_list_enabled_flag) { + ue(pps->chroma_qp_offset_list_len_minus1, 5); + for (int i = 0; i <= pps->chroma_qp_offset_list_len_minus1; i++) { + for (int j = CB - 1; j < CR + pps_joint_cbcr_qp_offset_present_flag; j++) + se(pps->chroma_qp_offset_list[i][j], -12, 12); + } + } + return 0; +} + +static int deblock_parse(DBParams *deblock, const VVCPPS *pps, GetBitContext *gb, void *log_ctx) +{ + int beta_offset_div2, tc_offset_div2; + for (int c_idx = 0; c_idx < VVC_MAX_SAMPLE_ARRAYS; c_idx++) { + if (!c_idx || pps->chroma_tool_offsets_present_flag) { + se(beta_offset_div2, -12, 12); + se(tc_offset_div2, -12, 12); + } + deblock->beta_offset[c_idx] = beta_offset_div2 << 1; + deblock->tc_offset[c_idx] = tc_offset_div2 << 1; + } + return 0; +} +static int pps_parse_deblocking_control(VVCPPS *pps, GetBitContext *gb, void *log_ctx) +{ + if (get_bits1(gb)) { //pps_deblocking_filter_control_present_flag + pps->deblocking_filter_override_enabled_flag = get_bits1(gb); + pps->deblocking_filter_disabled_flag = get_bits1(gb); + if (!pps->no_pic_partition_flag && + pps->deblocking_filter_override_enabled_flag) + pps->dbf_info_in_ph_flag = get_bits1(gb); + if (!pps->deblocking_filter_disabled_flag) + return deblock_parse(&pps->deblock, pps, gb, log_ctx); + } + return 0; +} + +static int pps_parse_num_tiles_in_pic(VVCPPS *pps, GetBitContext *gb, void *log_ctx) +{ + unsigned int num_exp_tile_columns, num_exp_tile_rows; + + uep(num_exp_tile_columns, 1, 1, + FFMIN(pps->ctb_width, VVC_MAX_TILE_COLUMNS)); + uep(num_exp_tile_rows, 1, 1, + FFMIN(pps->ctb_height, VVC_MAX_TILE_ROWS)); + + pps->column_width = pps_parse_tile_sizes(&pps->num_tile_columns, + num_exp_tile_columns, VVC_MAX_TILE_COLUMNS, pps->ctb_width, + gb, log_ctx); + if (!pps->column_width) + return AVERROR(ENOMEM); + + pps->row_height = pps_parse_tile_sizes(&pps->num_tile_rows, + num_exp_tile_rows, VVC_MAX_TILE_ROWS, pps->ctb_height, + gb, log_ctx); + if (!pps->row_height) + return AVERROR(ENOMEM); + + pps->num_tiles_in_pic = + pps->num_tile_columns * pps->num_tile_rows; + if (pps->num_tiles_in_pic > VVC_MAX_TILES_PER_AU) { + av_log(log_ctx, AV_LOG_ERROR, + "NumTilesInPic(%d) large than %d.\n", + pps->num_tiles_in_pic, VVC_MAX_TILES_PER_AU); + return AVERROR_INVALIDDATA; + } + + pps->col_bd = pps_setup_bd(pps->column_width, pps->num_tile_columns); + pps->row_bd = pps_setup_bd(pps->row_height, pps->num_tile_rows); + if (!pps->col_bd || !pps->row_bd) + return AVERROR(ENOMEM); + + return 0; +} + +static int pps_parse_slice_width_height_in_tiles(VVCPPS *pps, const int i, + const int tile_x, const int tile_y, const int pps_tile_idx_delta_present_flag, + GetBitContext *gb, void *log_ctx, + int *slice_width_in_tiles, int *slice_height_in_tiles) +{ + *slice_width_in_tiles = 1; + if (tile_x != pps->num_tile_columns - 1) { + uep(*slice_width_in_tiles, 1, 1, pps->num_tile_columns); + } + if (tile_y != pps->num_tile_rows - 1 && + (pps_tile_idx_delta_present_flag || tile_x == 0)) { + uep(*slice_height_in_tiles, 1, 1, pps->num_tile_rows); + } else { + if (tile_y == pps->num_tile_rows - 1) + *slice_height_in_tiles = 1; + } + return 0; +} + +static void pps_derive_tile_xy(const VVCPPS *pps, const int tile_idx, + int *tile_x, int *tile_y) +{ + *tile_x = tile_idx % pps->num_tile_columns; + *tile_y = tile_idx / pps->num_tile_columns; +} + +static int pps_parse_tile_idx(const VVCPPS *pps, const int slice_idx, int tile_idx, + const int slice_width_in_tiles, const int slice_height_in_tiles, const int pps_tile_idx_delta_present_flag, + GetBitContext *gb) +{ + if (pps_tile_idx_delta_present_flag) { + tile_idx += get_se_golomb(gb); //< pps_tile_idx_delta_val + } else { + tile_idx += slice_width_in_tiles; + if (tile_idx % pps->num_tile_columns == 0) + tile_idx += (slice_height_in_tiles - 1) * pps->num_tile_columns; + } + return tile_idx; +} + +typedef struct SliceMap { + uint16_t start_offset; + const VVCSPS *sps; + VVCPPS *pps; + uint16_t top_left_ctu_x[VVC_MAX_SLICES]; + uint16_t top_left_ctu_y[VVC_MAX_SLICES]; +} SliceMap; + +static void slice_map_init(SliceMap *slice_map, const VVCSPS *sps, VVCPPS *pps) +{ + slice_map->start_offset = 0; + slice_map->pps = pps; + slice_map->sps = sps; +} + +static void slice_map_add_new_slice(SliceMap *slice_map, const int slice_idx, const int ctu_x, const int ctu_y, const int height, const int ctb_count) +{ + VVCPPS *pps = slice_map->pps; + pps->slice_start_offset[slice_idx] = slice_map->start_offset; + + slice_map->top_left_ctu_x[slice_idx] = ctu_x; + slice_map->top_left_ctu_y[slice_idx] = ctu_y; + pps->num_ctus_in_slice[slice_idx] = ctb_count; + slice_map->start_offset += ctb_count; +} + +static int slice_map_add_ctus(SliceMap *slice_map, const int slice_idx, const int ctu_x, const int ctu_y, const int width, const int height) +{ + VVCPPS *pps = slice_map->pps; + const int x_end = ctu_x + width; + const int y_end = ctu_y + height; + int ctb_count = 0; + + for (int y = ctu_y; y < y_end; y++) { + for (int x = ctu_x; x < x_end; x++) { + const uint16_t ctb_addr_in_rs = y * pps->ctb_width + x; //< CtbAddrInRs + const int offset = slice_map->start_offset + ctb_count; + pps->ctb_addr_in_slice[offset] = ctb_addr_in_rs; + ctb_count++; + } + } + slice_map_add_new_slice(slice_map, slice_idx, ctu_x, ctu_y, height, ctb_count); + return slice_map->sps->entropy_coding_sync_enabled_flag ? height - 1 : 0; +} + +static void slice_map_add_tiles(SliceMap *slice_map, const int slice_idx, + const int tile_x_start, const int tile_y_start, const int tile_width, const int tile_height) +{ + VVCPPS *pps = slice_map->pps; + const int tile_x_end = tile_x_start + tile_width; + const int tile_y_end = tile_y_start + tile_height; + int height = 0, ctb_count = 0; + + if (!tile_width || !tile_height) + return ; + + for (int tile_y = tile_y_start; tile_y < tile_y_end; tile_y++) { + for (int tile_x = tile_x_start; tile_x < tile_x_end; tile_x++) { + + const int ctu_x = pps->col_bd[tile_x]; + const int ctu_y = pps->row_bd[tile_y]; + const int x_end = ctu_x + pps->column_width[tile_x]; + const int y_end = ctu_y + pps->row_height[tile_y]; + + for (int y = ctu_y; y < y_end; y++) { + for (int x = ctu_x; x < x_end; x++) { + const uint16_t ctb_addr_in_rs = y * pps->ctb_width + x; //< CtbAddrInRs + const int offset = slice_map->start_offset + ctb_count; + pps->ctb_addr_in_slice[offset] = ctb_addr_in_rs; + ctb_count++; + } + } + } + height += pps->row_height[tile_y]; + } + + slice_map_add_new_slice(slice_map, slice_idx, pps->col_bd[tile_x_start], pps->row_bd[tile_y_start], height, ctb_count); +} + +static int pps_parse_slices_in_one_tile(VVCPPS *pps, SliceMap *slice_map, + const int tile_x, const int tile_y, GetBitContext *gb, void *log_ctx, int *slice_idx) +{ + int num_slices_in_tile, pps_num_exp_slices_in_tile, uniform_slice_height, remaining_height_in_ctbs_y; + int ctu_x = pps->col_bd[tile_x]; + int ctu_y = pps->row_bd[tile_y]; + int i = *slice_idx; + + remaining_height_in_ctbs_y = pps->row_height[tile_y]; + ue(pps_num_exp_slices_in_tile, pps->row_height[tile_y] - 1); + if (pps_num_exp_slices_in_tile == 0) { + num_slices_in_tile = 1; + slice_map_add_ctus(slice_map, i, ctu_x, ctu_y, pps->column_width[tile_x], pps->row_height[tile_y]); + } else { + int j, slice_height_in_ctus; + for (j = 0; j < pps_num_exp_slices_in_tile; j++) { + uep(slice_height_in_ctus, 1, 1, pps->row_height[tile_y]); + + slice_map_add_ctus(slice_map, i + j, ctu_x, ctu_y, pps->column_width[tile_x], slice_height_in_ctus); + + ctu_y += slice_height_in_ctus; + remaining_height_in_ctbs_y -= slice_height_in_ctus; + } + uniform_slice_height = (j == 0 ? pps->row_height[tile_y] : slice_height_in_ctus); + + while (remaining_height_in_ctbs_y > 0) { + slice_map_add_ctus(slice_map, i + j, ctu_x, ctu_y, + pps->column_width[tile_x], FFMIN(uniform_slice_height, remaining_height_in_ctbs_y)); + + ctu_y += uniform_slice_height; + remaining_height_in_ctbs_y -= uniform_slice_height; + j++; + } + num_slices_in_tile = j; + } + *slice_idx += num_slices_in_tile - 1; + return 0; +} + +static int sps_pos_in_subpic(const VVCSPS *sps, const int subpic_idx, const int pos_x, const int pos_y) +{ + return (pos_x >= sps->subpic_ctu_top_left_x[subpic_idx]) && + (pos_y >= sps->subpic_ctu_top_left_y[subpic_idx]) && + (pos_x < sps->subpic_ctu_top_left_x[subpic_idx] + sps->subpic_width[subpic_idx]) && + (pos_y < sps->subpic_ctu_top_left_y[subpic_idx] + sps->subpic_height[subpic_idx]); + +} + +static int pps_parse_rect_slice(VVCPPS *pps, const VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + int i; + SliceMap slice_map; + uint16_t num_slices_in_pic; ///< pps_num_slices_in_pic_minus1 + 1; + + slice_map_init(&slice_map, sps, pps); + if (!pps->single_slice_per_subpic_flag) { + int tile_idx = 0, pps_tile_idx_delta_present_flag = 0, tile_x, tile_y, ret, j; + uep(num_slices_in_pic, 1, 1, VVC_MAX_SLICES); + + if (num_slices_in_pic > 2) + pps_tile_idx_delta_present_flag = get_bits1(gb); + + for (i = 0; i < num_slices_in_pic; i++) { + pps_derive_tile_xy(pps, tile_idx, &tile_x, &tile_y); + if (i < num_slices_in_pic - 1) { + int slice_width_in_tiles, slice_height_in_tiles; + ret = pps_parse_slice_width_height_in_tiles(pps, i, tile_x, tile_y, pps_tile_idx_delta_present_flag, + gb, log_ctx, &slice_width_in_tiles, &slice_height_in_tiles); + if (ret < 0) + return ret; + + if (slice_width_in_tiles == 1 && slice_height_in_tiles == 1 && pps->row_height[tile_y] > 1) { + ret = pps_parse_slices_in_one_tile(pps, &slice_map, tile_x, tile_y, gb, log_ctx, &i); + if (ret < 0) + return ret; + } else { + slice_map_add_tiles(&slice_map, i, tile_x, tile_y, slice_width_in_tiles, slice_height_in_tiles); + } + if (i < num_slices_in_pic - 1) { + tile_idx = pps_parse_tile_idx(pps, i, tile_idx, slice_width_in_tiles, slice_height_in_tiles, pps_tile_idx_delta_present_flag, gb); + if (tile_idx < 0 || tile_idx >= pps->num_tiles_in_pic) + return AVERROR_INVALIDDATA; + } + } + else { + slice_map_add_tiles(&slice_map, num_slices_in_pic - 1, + tile_x, tile_y, pps->num_tile_columns - tile_x, pps->num_tile_rows - tile_y); + } + } + + //now, we got all slice information, let's resolve NumSlicesInSubpic + for (i = 0; i < sps->num_subpics; i++) { + pps->num_slices_in_subpic[i] = 0; + for (j = 0; j < num_slices_in_pic; j++) { + if (sps_pos_in_subpic(sps, i, slice_map.top_left_ctu_x[j], slice_map.top_left_ctu_y[j])) { + pps->num_slices_in_subpic[i]++; + } + } + } + } else { + num_slices_in_pic = sps->num_subpics; + for (i = 0; i < num_slices_in_pic; i++) { + int start_x = -1, start_y = -1, width_in_tiles, height_in_tiles; + pps->num_slices_in_subpic[i] = 1; + for (int tile_y = 0; tile_y < pps->num_tile_rows; tile_y++) { + for (int tile_x = 0; tile_x < pps->num_tile_columns; tile_x++) { + if (sps_pos_in_subpic(sps, i, pps->col_bd[tile_x], pps->row_bd[tile_y])) { + if (start_x == -1) { + start_x = tile_x; + start_y = tile_y; + } + width_in_tiles = tile_x - start_x + 1; + height_in_tiles = tile_y - start_y + 1; + } + } + } + if (start_x != -1) + slice_map_add_tiles(&slice_map, i, start_x, start_y, width_in_tiles, height_in_tiles); + } + } + if (pps->single_slice_per_subpic_flag || num_slices_in_pic > 1) + pps->loop_filter_across_slices_enabled_flag = get_bits1(gb); + return 0; +} + +static int pps_parse_slice(VVCPPS *pps, const VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + int ret; + + pps->loop_filter_across_slices_enabled_flag = 0; + if (pps->rect_slice_flag) { + ret = pps_parse_rect_slice(pps, sps, gb, log_ctx); + if (ret < 0) + return ret; + } else { + int ctu_idx = 0; + for (int tile_y = 0; tile_y < pps->num_tile_rows; tile_y++) { + for (int tile_x = 0; tile_x < pps->num_tile_columns; tile_x++) { + for (int ctu_y = pps->row_bd[tile_y]; ctu_y < pps->row_bd[tile_y] + pps->row_height[tile_y]; ctu_y++) { + for (int ctu_x = pps->col_bd[tile_x]; ctu_x < pps->col_bd[tile_x] + pps->column_width[tile_x]; ctu_x++) { + const int ctu_addr_rs = ctu_y * pps->ctb_width + ctu_x; + pps->ctb_addr_in_slice[ctu_idx++] = ctu_addr_rs; + + } + } + } + } + pps->loop_filter_across_slices_enabled_flag = get_bits1(gb); + } + + return 0; +} + +static int pps_no_pic_partition(VVCPPS *pps, const VVCSPS *sps) +{ + SliceMap slice_map; + + pps->num_tile_columns = 1; + pps->num_tile_rows = 1; + pps->num_tiles_in_pic = 1; + + pps->column_width = av_malloc_array(pps->num_tile_columns, sizeof(*pps->column_width)); + pps->row_height = av_malloc_array(pps->num_tile_rows, sizeof(*pps->row_height)); + if (!pps->column_width || !pps->row_height) + return AVERROR(ENOMEM); + pps->column_width[0] = pps->ctb_width; + pps->row_height[0] = pps->ctb_height; + + pps->col_bd = pps_setup_bd(pps->column_width, pps->num_tile_columns); + pps->row_bd = pps_setup_bd(pps->row_height, pps->num_tile_rows); + if (!pps->col_bd || !pps->row_bd) + return AVERROR(ENOMEM); + + slice_map_init(&slice_map, sps, pps); + slice_map_add_ctus(&slice_map, 0, 0, 0, pps->ctb_width, pps->ctb_height); + + return 0; +} + +static int pps_parse_pic_partition(VVCPPS *pps, const VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + 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->no_pic_partition_flag) { + int ret; + uint8_t pps_log2_ctu_size_minus5; + u(2, pps_log2_ctu_size_minus5, sps->ctb_log2_size_y - 5, sps->ctb_log2_size_y - 5); + + ret = pps_parse_num_tiles_in_pic(pps, gb, log_ctx); + if (ret < 0) + return ret; + + pps->rect_slice_flag = 1; + if (pps->num_tiles_in_pic > 1) { + pps->loop_filter_across_tiles_enabled_flag = get_bits1(gb); + pps->rect_slice_flag = get_bits1(gb); + } + + pps->single_slice_per_subpic_flag = pps->rect_slice_flag ? get_bits1(gb) : 0; + + return pps_parse_slice(pps, sps, gb, log_ctx); + } + + return pps_no_pic_partition(pps, sps); +} + +static int pps_parse_inter(VVCPPS *pps, const VVCSPS *sps, GetBitContext *gb, void *log_ctx) +{ + for (int i = 0; i < 2; i++) + uep(pps->num_ref_idx_default_active[i], 1, 1, 15); + + pps->rpl1_idx_present_flag = get_bits1(gb); + pps->weighted_pred_flag = get_bits1(gb); + pps->weighted_bipred_flag = get_bits1(gb); + pps->ref_wraparound_enabled_flag = get_bits1(gb); + + if (pps->ref_wraparound_enabled_flag) + pps->ref_wraparound_offset = (pps->width / sps->min_cb_size_y) - get_ue_golomb_long(gb); + return 0; +} + +static void pps_parse_ph_flags(VVCPPS *pps, GetBitContext *gb) +{ + if (!pps->no_pic_partition_flag) { + pps->rpl_info_in_ph_flag = get_bits1(gb); + pps->sao_info_in_ph_flag = get_bits1(gb); + pps->alf_info_in_ph_flag = get_bits1(gb); + if ((pps->weighted_pred_flag || pps->weighted_bipred_flag) && + pps->rpl_info_in_ph_flag) + pps->wp_info_in_ph_flag = get_bits1(gb); + pps->qp_delta_info_in_ph_flag = get_bits1(gb); + } +} + +int ff_vvc_decode_pps(VVCParamSets *ps, GetBitContext *gb, void *log_ctx) +{ + const VVCSPS *sps = NULL; + int ret = 0; + ptrdiff_t nal_size; + AVBufferRef *pps_buf; + + VVCPPS *pps = av_mallocz(sizeof(*pps)); + if (!pps) + return AVERROR(ENOMEM); + + pps_buf = av_buffer_create((uint8_t *)pps, sizeof(*pps), vvc_pps_free, NULL, 0); + if (!pps_buf) { + av_freep(&pps); + return AVERROR(ENOMEM); + } + + av_log(log_ctx, AV_LOG_DEBUG, "Decoding PPS\n"); + + nal_size = gb->buffer_end - gb->buffer; + if (nal_size > sizeof(pps->data)) { + av_log(log_ctx, AV_LOG_WARNING, "Truncating likely oversized PPS " + "(%"PTRDIFF_SPECIFIER" > %"SIZE_SPECIFIER")\n", + nal_size, sizeof(pps->data)); + pps->data_size = sizeof(pps->data); + } else { + pps->data_size = nal_size; + } + memcpy(pps->data, gb->buffer, pps->data_size); + + pps->pic_parameter_set_id = get_bits(gb, 6); + pps->seq_parameter_set_id = get_bits(gb, 4); + if (!ps->sps_list[pps->seq_parameter_set_id]) { + av_log(log_ctx, AV_LOG_ERROR, "SPS %u does not exist.\n", pps->seq_parameter_set_id); + ret = AVERROR_INVALIDDATA; + goto err; + } + sps = (VVCSPS *)ps->sps_list[pps->seq_parameter_set_id]->data; + + pps->mixed_nalu_types_in_pic_flag = get_bits1(gb); + + ret = pps_parse_width_height(pps, sps, gb, log_ctx); + if (ret < 0) + goto err; + + pps_parse_conf_win(pps, sps, gb); + + ret = pps_scaling_win_parse(pps, sps, gb, log_ctx); + if (ret < 0) + goto err; + + pps->output_flag_present_flag = get_bits1(gb); + + ret = pps_parse_subpic_id(pps, sps, gb, log_ctx); + if (ret < 0) + goto err; + + pps->ctb_width = vvc_ceil(pps->width, sps->ctb_size_y); + pps->ctb_height = vvc_ceil(pps->height, sps->ctb_size_y); + pps->ctb_count = pps->ctb_width * pps->ctb_height; + + ret = pps_parse_pic_partition(pps, sps, gb, log_ctx); + if (ret < 0) + goto err; + + pps->cabac_init_present_flag = get_bits1(gb); + + ret = pps_parse_inter(pps, sps, gb, log_ctx); + if (ret < 0) + goto err; + + sep(pps->init_qp, 26, -(sps->qp_bd_offset), 63); + pps->cu_qp_delta_enabled_flag = get_bits1(gb); + + ret = pps_parse_chroma_tool_offsets(pps, gb, log_ctx); + if (ret < 0) + goto err; + + ret = pps_parse_deblocking_control(pps, gb, log_ctx); + if (ret < 0) + goto err; + + pps_parse_ph_flags(pps, gb); + + pps->picture_header_extension_present_flag = get_bits1(gb); + pps->slice_header_extension_present_flag = get_bits1(gb); + + skip_bits1(gb); ///< pps_extension_flag + + pps->min_cb_width = pps->width / sps->min_cb_size_y; + pps->min_cb_height = pps->height / sps->min_cb_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); + + ret = pps_setup(pps, sps); + if (ret < 0) + goto err; + + if (get_bits_left(gb) < 0) { + av_log(log_ctx, AV_LOG_ERROR, + "Overread PPS by %d bits\n", -get_bits_left(gb)); + goto err; + } + remove_pps(ps, pps->pic_parameter_set_id); + ps->pps_list[pps->pic_parameter_set_id] = pps_buf; + + return 0; + +err: + av_buffer_unref(&pps_buf); + return ret; +} + +static int ph_parse_pic_parameter_set_id(VVCPH *ph, VVCParamSets *ps, GetBitContext* gb, void *log_ctx) +{ + AVBufferRef *buf; + + ue(ph->pic_parameter_set_id, VVC_MAX_PPS_COUNT - 1); + buf = ps->pps_list[ph->pic_parameter_set_id]; + if (!buf) { + av_log(log_ctx, AV_LOG_ERROR, "PPS id %d not available.\n", + ph->pic_parameter_set_id); + return AVERROR_INVALIDDATA; + } + ps->pps = (VVCPPS *)buf->data; + + buf = ps->sps_list[ps->pps->seq_parameter_set_id]; + if (!buf) { + av_log(log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + ps->pps->seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + ps->sps = (VVCSPS*)buf->data; + + buf = ps->vps_list[ps->sps->video_parameter_set_id]; + if (!buf) { + av_log(log_ctx, AV_LOG_ERROR, "VPS id %d not available.\n", + ps->sps->video_parameter_set_id); + return AVERROR_INVALIDDATA; + } + return 0; +} + +static int rpls_list_parse(VVCRefPicListStruct *ref_lists, const VVCParamSets *ps, + const int poc, GetBitContext *gb, void *log_ctx) +{ + const VVCSPS *sps = ps->sps; + const VVCPPS *pps = ps->pps; + int i, j, rpl_sps_flag[2], rpl_idx[2]; + for (i = 0; i < 2; i++) { + VVCRefPicListStruct *rpls = ref_lists + i; + int prev_delta_poc_msb = 0; + + rpl_sps_flag[i] = 0; + if (sps->num_ref_pic_lists[i] > 0) { + if (i == 0 || pps->rpl1_idx_present_flag) + rpl_sps_flag[i] = get_bits1(gb); + else + rpl_sps_flag[i] = rpl_sps_flag[0]; + } + if (rpl_sps_flag[i]) { + if (sps->num_ref_pic_lists[i] == 1) { + rpl_idx[i] = 0; + } else if (i == 0 || pps->rpl1_idx_present_flag) { + uint8_t bits = av_ceil_log2(sps->num_ref_pic_lists[i]); + u(bits, rpl_idx[i], 0, sps->num_ref_pic_lists[i] - 1); + } else { + rpl_idx[1] = rpl_idx[0]; + } + memcpy(rpls, &sps->ref_pic_list_struct[i][rpl_idx[i]], sizeof(*rpls)); + } else { + rpls_parse(rpls, gb, i, sps->num_ref_pic_lists[i], sps, log_ctx); + } + + for (j = 0; j < rpls->num_ref_entries; j++) { + VVCRefPicListStructEntry *ref = &rpls->entries[j]; + + if (!ref->st_ref_pic_flag) { + if (rpls->ltrp_in_header_flag) + ref->lt_poc = get_bits(gb, sps->log2_max_pic_order_cnt_lsb); + + ref->lt_msb_flag = get_bits1(gb); + if (ref->lt_msb_flag) { + uint32_t max = 1 << (32 - sps->log2_max_pic_order_cnt_lsb); + uint32_t delta_poc_msb_cycle_lt; + + ue(delta_poc_msb_cycle_lt, max); + delta_poc_msb_cycle_lt += prev_delta_poc_msb; + ref->lt_poc = + poc - delta_poc_msb_cycle_lt * sps->max_pic_order_cnt_lsb - + (poc & (sps->max_pic_order_cnt_lsb - 1)) + ref->lt_poc; + + prev_delta_poc_msb = delta_poc_msb_cycle_lt; + } + } + } + } + + return 0; + +} + +static int alf_parse(Alf *alf, const VVCParamSets* ps, GetBitContext *gb, void *log_ctx) +{ + const VVCSPS *sps = ps->sps; + alf->cc_enabled_flag[0] = alf->cc_enabled_flag[1] = 0; + alf->enabled_flag[CB] = alf->enabled_flag[CR] = 0; + + alf->enabled_flag[LUMA] = get_bits1(gb); + if (alf->enabled_flag[LUMA]) { + + alf->num_aps_ids_luma = get_bits(gb, 3); + for (int i = 0; i < alf->num_aps_ids_luma; i++) { + alf->aps_id_luma[i] = get_bits(gb, 3); + if (!ps->alf_list[alf->aps_id_luma[i]]) { + av_log(log_ctx, AV_LOG_ERROR, "aps_id_luma %d not available.\n", + alf->aps_id_luma[i]); + return AVERROR_INVALIDDATA; + } + } + if (sps->chroma_format_idc != 0) { + for (int i = CB; i <= CR; i++) + alf->enabled_flag[i] = get_bits1(gb); + } + + if (alf->enabled_flag[CB] || alf->enabled_flag[CR]) { + alf->aps_id_chroma = get_bits(gb, 3); + if (!ps->alf_list[alf->aps_id_chroma]) { + av_log(log_ctx, AV_LOG_ERROR, "aps_id_chroma %d not available.\n", + alf->aps_id_chroma); + return AVERROR_INVALIDDATA; + } + } + + if (sps->ccalf_enabled_flag) { + for (int i = 0; i < 2; i++) { + alf->cc_enabled_flag[i] = get_bits1(gb); + if (alf->cc_enabled_flag[i]) { + alf->cc_aps_id[i] = get_bits(gb, 3); + if (!ps->alf_list[alf->cc_aps_id[i]]) { + av_log(log_ctx, AV_LOG_ERROR, "cc_aps_id[%d] %d not available.\n", + i, alf->cc_aps_id[i]); + return AVERROR_INVALIDDATA; + } + } + } + } + } + return 0; +} + +// 8.3.1 Decoding process for picture order count +static int ph_compute_poc(VVCPH *ph, const VVCSPS *sps, const int poc_tid0, const int is_clvss) +{ + const int max_poc_lsb = sps->max_pic_order_cnt_lsb; + 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->pic_order_cnt_lsb; + int poc_msb; + + if (ph->poc_msb_cycle_present_flag) { + poc_msb = 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 int ph_parse_poc(VVCPH *ph, const VVCSPS *sps, const int poc_tid0, const int is_clvss, GetBitContext *gb, void *log_ctx) +{ + ph->pic_order_cnt_lsb = get_bits(gb, sps->log2_max_pic_order_cnt_lsb); + if (ph->gdr_pic_flag) + ue(ph->recovery_poc_cnt, 1 << sps->log2_max_pic_order_cnt_lsb); + + for (int i = 0; i < sps->num_extra_ph_bytes * 8; i++) { + if (sps->extra_ph_bit_present_flag[i]) + skip_bits1(gb); + } + if (sps->poc_msb_cycle_flag) { + ph->poc_msb_cycle_present_flag = get_bits1(gb); + if (ph->poc_msb_cycle_present_flag) + ph->poc_msb_cycle_val = get_bits(gb, sps->poc_msb_cycle_len); + } + ph->poc = ph_compute_poc(ph, sps, poc_tid0, is_clvss); + + return 0; +} + +static int ph_parse_alf(VVCPH *ph, const VVCParamSets *ps, GetBitContext *gb, void *log_ctx) +{ + + if (ps->sps->alf_enabled_flag && ps->pps->alf_info_in_ph_flag) + return alf_parse(&ph->alf, ps, gb, log_ctx); + ph->alf.enabled_flag[LUMA] = 0; + return 0; +} + +static av_always_inline int lmcs_derive_lut_sample(int sample, + int *pivot1, int *pivot2, int *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 ph_lmcs_derive_lut(VVCPH *ph, const VVCParamSets *ps) +{ + const VVCSPS *sps = ps->sps; + const AVBufferRef *lmcs_buf; + const VVCLMCS *lmcs; + const int max = (1 << sps->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]; + int input_pivot[LMCS_MAX_BIN_SIZE]; + int scale_coeff[LMCS_MAX_BIN_SIZE]; + int inv_scale_coeff[LMCS_MAX_BIN_SIZE]; + int i; + if (sps->bit_depth > LMCS_MAX_BIT_DEPTH) + return AVERROR_PATCHWELCOME; + + lmcs_buf = ps->lmcs_list[ph->lmcs_aps_id]; + if (!lmcs_buf) + return AVERROR_INVALIDDATA; + lmcs = (VVCLMCS*)lmcs_buf->data; + + memset(cw, 0, sizeof(cw)); + for (int i = lmcs->min_bin_idx; i <= lmcs->max_bin_idx; i++) + cw[i] = org_cw + lmcs->delta_cw[i]; + + ph->lmcs_pivot[0] = 0; + for (i = 0; i < LMCS_MAX_BIN_SIZE; i++) { + input_pivot[i] = i * org_cw; + ph->lmcs_pivot[i + 1] = ph->lmcs_pivot[i] + cw[i]; + scale_coeff[i] = (cw[i] * (1 << 11) + off) >> shift; + if (cw[i] == 0) { + inv_scale_coeff[i] = 0; + ph->lmcs_chroma_scale_coeff[i] = (1 << 11); + } else { + inv_scale_coeff[i] = org_cw * (1 << 11) / cw[i]; + ph->lmcs_chroma_scale_coeff[i] = org_cw * (1 << 11) / (cw[i] + lmcs->delta_crs); + } + } + + //derive lmcs_fwd_lut + for (int sample = 0; sample < max; sample++) { + const int idx_y = sample / org_cw; + const int fwd_sample = lmcs_derive_lut_sample(sample, ph->lmcs_pivot, + input_pivot, scale_coeff, idx_y, max); + if (sps->bit_depth > 8) + ((uint16_t *)ph->lmcs_fwd_lut)[sample] = fwd_sample; + else + ph->lmcs_fwd_lut[sample] = fwd_sample; + + } + + //derive lmcs_inv_lut + i = lmcs->min_bin_idx; + for (int sample = 0; sample < max; sample++) { + int inv_sample; + while (sample >= ph->lmcs_pivot[i + 1] && i <= lmcs->max_bin_idx) + i++; + + inv_sample = lmcs_derive_lut_sample(sample, input_pivot, ph->lmcs_pivot, + inv_scale_coeff, i, max); + + if (sps->bit_depth > 8) + ((uint16_t *)ph->lmcs_inv_lut)[sample] = inv_sample; + else + ph->lmcs_inv_lut[sample] = inv_sample; + } + + ph->lmcs_min_bin_idx = lmcs->min_bin_idx; + ph->lmcs_max_bin_idx = lmcs->max_bin_idx; + + return 0; +} + +static int ph_parse_lmcs(VVCPH *ph, const VVCParamSets *ps, GetBitContext *gb) +{ + const VVCSPS *sps = ps->sps; + int ret; + + ph->lmcs_enabled_flag = 0; + ph->chroma_residual_scale_flag = 0; + if (sps->lmcs_enabled_flag) { + ph->lmcs_enabled_flag = get_bits1(gb); + if (ph->lmcs_enabled_flag) { + ph->lmcs_aps_id = get_bits(gb, 2); + if (sps->chroma_format_idc) + ph->chroma_residual_scale_flag = get_bits1(gb); + ret = ph_lmcs_derive_lut(ph, ps); + if (ret < 0) + return ret; + } + } + return 0; +} + +static void ph_parse_scaling_list(VVCPH *ph, const VVCSPS *sps, GetBitContext *gb) +{ + ph->explicit_scaling_list_enabled_flag = 0; + if (sps->explicit_scaling_list_enabled_flag) { + ph->explicit_scaling_list_enabled_flag = get_bits1(gb); + if (ph->explicit_scaling_list_enabled_flag) { + //todo: check the ph->scaling_list_aps_id range, when aps ready + ph->scaling_list_aps_id = get_bits(gb, 3); + } + } +} + +static int ph_parse_virtual_boundaries(VVCPH *ph, const VVCSPS *sps, + const VVCPPS *pps, GetBitContext *gb, void *log_ctx) +{ + ph->vbs.virtual_boundaries_present_flag = 0; + if (sps->virtual_boundaries_enabled_flag && !sps->vbs.virtual_boundaries_present_flag) { + const int ret = virtual_boundaries_parse(&ph->vbs, pps->width, pps->height, gb, log_ctx); + if (ret < 0) + return ret; + } + return 0; +} + +static int ph_parse_temporal_mvp(VVCPH *ph, const VVCSPS *sps, const VVCPPS *pps, GetBitContext *gb, void *log_ctx) +{ + ph->temporal_mvp_enabled_flag = 0; + if (sps->temporal_mvp_enabled_flag) { + ph->temporal_mvp_enabled_flag = get_bits1(gb); + if (ph->temporal_mvp_enabled_flag && pps->rpl_info_in_ph_flag) { + int num_ref_entries; + ph->collocated_list = L0; + if (ph->rpls[1].num_ref_entries > 0) + ph->collocated_list = !get_bits1(gb); + num_ref_entries = ph->rpls[ph->collocated_list].num_ref_entries; + if (num_ref_entries > 1) { + ue(ph->collocated_ref_idx, num_ref_entries - 1); + } + } + } + return 0; +} + +static void ph_parse_sao(VVCPH *ph, const VVCSPS *sps, const VVCPPS *pps, GetBitContext *gb) +{ + ph->sao_luma_enabled_flag = 0; + ph->sao_chroma_enabled_flag = 0; + if (sps->sao_enabled_flag && pps->sao_info_in_ph_flag) { + ph->sao_luma_enabled_flag = get_bits1(gb); + if (sps->chroma_format_idc != 0) + ph->sao_chroma_enabled_flag = get_bits1(gb); + } +} + +static int ph_parse_dbf(VVCPH *ph, const VVCPPS *pps, GetBitContext *gb, void *log_ctx) +{ + ph->deblocking_filter_disabled_flag = pps->deblocking_filter_disabled_flag; + if (pps->dbf_info_in_ph_flag) { + int ph_deblocking_params_present_flag = get_bits1(gb); + if (ph_deblocking_params_present_flag) { + ph->deblocking_filter_disabled_flag = 0; + if (!pps->deblocking_filter_disabled_flag) + ph->deblocking_filter_disabled_flag = get_bits1(gb); + if (!ph->deblocking_filter_disabled_flag) + return deblock_parse(&ph->deblock, pps, gb, log_ctx); + } + } + memcpy(&ph->deblock, &pps->deblock, sizeof(ph->deblock)); + return 0; +} + +static int ph_parse_cu_qp(VVCPH *ph, const int inter, const VVCSPS *sps, const VVCPPS *pps, GetBitContext *gb, void *log_ctx) +{ + const PartitionConstraints *pc = inter ? &ph->inter_slice : &ph->intra_slice_luma; + const int ctb_log2_size_y = sps->ctb_log2_size_y; + const int min_qt_log2_size_y = pc->log2_diff_min_qt_min_cb + sps->min_cb_log2_size_y; + + ph->cu_qp_delta_subdiv[inter] = 0; + if (pps->cu_qp_delta_enabled_flag) + ue(ph->cu_qp_delta_subdiv[inter], + 2 * (ctb_log2_size_y - min_qt_log2_size_y + + pc->max_mtt_hierarchy_depth)); + + ph->cu_chroma_qp_offset_subdiv[inter] = 0; + if (pps->cu_chroma_qp_offset_list_enabled_flag) + ue(ph->cu_chroma_qp_offset_subdiv[inter], + 2 * (ctb_log2_size_y - min_qt_log2_size_y + + pc->max_mtt_hierarchy_depth)); + + return 0; +} + +static int ph_parse_intra(VVCPH *ph, const VVCSPS *sps, const VVCPPS *pps, GetBitContext *gb, void *log_ctx) +{ + int ret; + + if (ph->intra_slice_allowed_flag) { + ph->intra_slice_luma = sps->intra_slice_luma; + ph->intra_slice_chroma = sps->intra_slice_chroma; + if (ph->partition_constraints_override_flag) { + ret = partition_constraints_parse(&ph->intra_slice_luma, sps, gb, log_ctx); + if (ret < 0) + return ret; + + if (sps->qtbtt_dual_tree_intra_flag) { + ret = partition_constraints_parse(&ph->intra_slice_chroma, sps, gb, log_ctx); + if (ret < 0) + return ret; + } + } + ret = ph_parse_cu_qp(ph, 0, sps, pps, gb, log_ctx); + if (ret < 0) + return ret; + } + + return 0; +} + +static int hls_pred_weight_table(PredWeightTable *w, const uint8_t *num_ref_idx_active, + const VVCRefPicListStruct *rpls, const VVCSPS *sps, const VVCPPS *pps, GetBitContext *gb, + void *log_ctx) +{ + const int has_chroma = (sps->chroma_format_idc > 0); + + ue(w->log2_denom[LUMA], 7); + + w->log2_denom[CHROMA] = 0; + if (has_chroma) + sep(w->log2_denom[CHROMA], w->log2_denom[LUMA], 0, 7); + + for (int lx = L0; lx <= L1; lx++) { + w->nb_weights[lx] = num_ref_idx_active[lx]; + if (pps->wp_info_in_ph_flag) { + w->nb_weights[lx] = 0; + if (lx == L0 || (pps->weighted_bipred_flag && rpls[L1].num_ref_entries)) + ue(w->nb_weights[lx], FFMIN(15, rpls[lx].num_ref_entries)); + } + + for (int is_chroma = 0; is_chroma <= has_chroma; is_chroma++) { + for (int i = 0; i < w->nb_weights[lx]; i++) + w->weight_flag[lx][is_chroma][i] = get_bits1(gb); + } + + for (int i = 0; i < w->nb_weights[lx]; i++) { + const int c_end = has_chroma ? VVC_MAX_SAMPLE_ARRAYS : 1; + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int is_chroma = !!c_idx; + const int denom = 1 << w->log2_denom[is_chroma]; + + w->weight[lx][c_idx][i] = denom; + w->offset[lx][c_idx][i] = 0; + if (w->weight_flag[lx][is_chroma][i]) { + sep(w->weight[lx][c_idx][i], denom, -128 + denom, 127 + denom); + if (!c_idx) { + se(w->offset[lx][c_idx][i], -128, 127); + } else { + int offset; + se(offset, -4 * 128, 4 * 127); + offset += 128 - ((128 * w->weight[lx][c_idx][i]) >> w->log2_denom[CHROMA]); + w->offset[lx][c_idx][i] = av_clip_intp2(offset, 7); + } + } + } + } + } + return 0; +} + +static int ph_parse_inter(VVCPH *ph, const VVCSPS *sps, const VVCPPS *pps, GetBitContext *gb, void *log_ctx) +{ + int ret; + + if (ph->inter_slice_allowed_flag) { + ph->inter_slice = sps->inter_slice; + if (ph->partition_constraints_override_flag) { + ret = partition_constraints_parse(&ph->inter_slice, sps, gb, log_ctx); + if (ret < 0) + return ret; + } + + ret = ph_parse_cu_qp(ph, 1, sps, pps, gb, log_ctx); + if (ret < 0) + return ret; + + ret = ph_parse_temporal_mvp(ph, sps, pps, gb, log_ctx); + if (ret < 0) + return ret; + + if (sps->affine_enabled_flag) + ph->max_num_subblock_merge_cand = 5 - sps->five_minus_max_num_subblock_merge_cand; + else + ph->max_num_subblock_merge_cand = sps->sbtmvp_enabled_flag && ph->temporal_mvp_enabled_flag; + + ph->mmvd_fullpel_only_flag = 0; + if (sps->mmvd_fullpel_only_enabled_flag) + ph->mmvd_fullpel_only_flag = get_bits1(gb); + + ph->mvd_l1_zero_flag = 1; + ph->bdof_disabled_flag = 1; + ph->dmvr_disabled_flag = 1; + if (!pps->rpl_info_in_ph_flag || ph->rpls[1].num_ref_entries > 0) { + ph->mvd_l1_zero_flag = get_bits1(gb); + + ph->bdof_disabled_flag = 1 - sps->bdof_enabled_flag; + if (sps->bdof_control_present_in_ph_flag) + ph->bdof_disabled_flag = get_bits1(gb); + + ph->dmvr_disabled_flag = 1 - sps->dmvr_enabled_flag; + if (sps->dmvr_control_present_in_ph_flag) + ph->dmvr_disabled_flag = get_bits1(gb); + } + + ph->prof_disabled_flag = 1; + if (sps->prof_control_present_in_ph_flag) + ph->prof_disabled_flag = get_bits1(gb); + + if ((pps->weighted_pred_flag || + pps->weighted_bipred_flag) && + pps->wp_info_in_ph_flag) { + // if pps->wp_info_in_ph->flag == 1 + // hls_pred_weight_table will not use num_ref_idx_active + uint8_t num_ref_idx_active[2] = {0, 0}; + ret = hls_pred_weight_table(&ph->pwt, num_ref_idx_active, + ph->rpls, sps, pps, gb, log_ctx); + if (ret < 0) + return ret; + } + } + return 0; +} + +static int ph_parse(VVCPH *ph, VVCParamSets *ps, const int poc_tid0, const int is_clvss, GetBitContext *gb, void *log_ctx) +{ + const VVCSPS *sps; + const VVCPPS *pps; + int ret, start_bit; + + start_bit = get_bits_count(gb); + ph->gdr_or_irap_pic_flag = get_bits1(gb); + ph->non_ref_pic_flag = get_bits1(gb); + + if (ph->gdr_or_irap_pic_flag) + ph->gdr_pic_flag = get_bits1(gb); + + ph->inter_slice_allowed_flag = get_bits1(gb); + ph->intra_slice_allowed_flag = !ph->inter_slice_allowed_flag || get_bits1(gb); + + ret = ph_parse_pic_parameter_set_id(ph, ps, gb, log_ctx); + if (ret < 0) + return ret; + sps = ps->sps; + pps = ps->pps; + + ret = ph_parse_poc(ph, sps, poc_tid0, is_clvss, gb, log_ctx); + if (ret < 0) + return ret; + + ret = ph_parse_alf(ph, ps, gb, log_ctx); + if (ret < 0) + return ret; + + ret = ph_parse_lmcs(ph, ps, gb); + if (ret < 0) + return ret; + ph_parse_scaling_list(ph, sps, gb); + ph_parse_virtual_boundaries(ph, sps, pps, gb, log_ctx); + + ph->pic_output_flag = 1; + if (pps->output_flag_present_flag && !ph->non_ref_pic_flag) + ph->pic_output_flag = get_bits1(gb); + + if (pps->rpl_info_in_ph_flag) + rpls_list_parse(ph->rpls, ps, ph->poc, gb, log_ctx); + + if (sps->partition_constraints_override_enabled_flag) + ph->partition_constraints_override_flag = get_bits1(gb); + + ret = ph_parse_intra(ph, sps, pps, gb, log_ctx); + if (ret < 0) + return ret; + + ret = ph_parse_inter(ph, sps, pps, gb, log_ctx); + if (ret < 0) + return ret; + + if (pps->qp_delta_info_in_ph_flag) + se(ph->qp_delta, -sps->qp_bd_offset - pps->init_qp, 63 - pps->init_qp); + + ph->joint_cbcr_sign_flag = 0; + if (sps->joint_cbcr_enabled_flag) + ph->joint_cbcr_sign_flag = get_bits1(gb); + + ph_parse_sao(ph, sps, pps, gb); + ph_parse_dbf(ph, pps, gb, log_ctx); + + if (get_bits_left(gb) < 0) { + av_log(log_ctx, AV_LOG_ERROR, + "Overread PH by %d bits\n", -get_bits_left(gb)); + return AVERROR_INVALIDDATA; + } + ph->size = get_bits_count(gb) - start_bit; + return 0; + +} + +int ff_vvc_decode_ph(VVCParamSets *ps, const int poc_tid0, const int is_clvss, GetBitContext *gb, void *log_ctx) +{ + const VVCSPS *sps = ps->sps; + const VVCPPS *pps = ps->pps; + VVCPH *ph; + AVBufferRef *ph_buf; + int ret; + + ph_buf = av_buffer_allocz(sizeof(*ps->ph)); + if (!ph_buf) + return AVERROR(ENOMEM); + + ph = (VVCPH *)ph_buf->data; + ret = ph_parse(ph, ps, poc_tid0, is_clvss, gb, log_ctx); + if (ret < 0) { + //sps and pps may changed + ps->sps = sps; + ps->pps = pps; + av_buffer_unref(&ph_buf); + return ret; + } + av_buffer_unref(&ps->ph_buf); + ps->ph_buf = ph_buf; + ps->ph = ph; + return 0; +} + +static int aps_alf_parse_luma(VVCALF *alf, GetBitContext *gb, void *log_ctx) +{ + int alf_luma_coeff_delta_idx[ALF_NUM_FILTERS_LUMA] = { 0 }; + int8_t luma_coeff [ALF_NUM_FILTERS_LUMA * ALF_NUM_COEFF_LUMA]; + uint8_t luma_clip_idx[ALF_NUM_FILTERS_LUMA * ALF_NUM_COEFF_LUMA]; + + const int alf_luma_clip_flag = get_bits1(gb); + const int alf_luma_num_filters_signalled = get_ue_golomb(gb) + 1; + + if (alf_luma_num_filters_signalled > ALF_NUM_FILTERS_LUMA) { + av_log(log_ctx, AV_LOG_WARNING, "alf_luma_num_filters_signalled(%d) should <= %d\n", + alf_luma_num_filters_signalled, ALF_NUM_FILTERS_LUMA); + return AVERROR_INVALIDDATA; + } + + //alf_luma_coeff_delta_idx[] + if (alf_luma_num_filters_signalled > 1) { + const int num_bits = av_ceil_log2(alf_luma_num_filters_signalled); + for (int i = 0; i < ALF_NUM_FILTERS_LUMA; i++) { + alf_luma_coeff_delta_idx[i] = get_bits(gb, num_bits); + if (alf_luma_coeff_delta_idx[i] >= ALF_NUM_FILTERS_LUMA) { + av_log(log_ctx, AV_LOG_WARNING, "alf_luma_coeff_delta_idx[%d](%d) should < %d\n", + i, alf_luma_coeff_delta_idx[i], ALF_NUM_FILTERS_LUMA); + return AVERROR_INVALIDDATA; + } + } + } + + //alf_luma_coeff_abs, alf_luma_coeff_sign + for (int i = 0; i < alf_luma_num_filters_signalled; i++) { + for (int j = 0; j < ALF_NUM_COEFF_LUMA; j++) { + + int alf_luma_coeff = get_ue_golomb(gb); + + if (alf_luma_coeff && get_bits1(gb)) + alf_luma_coeff = -alf_luma_coeff; + if (alf_luma_coeff < -128 || alf_luma_coeff > 127) { + av_log(log_ctx, AV_LOG_WARNING, "alf_luma_coeff[%d][%d](%d) should in range [-128, 127]\n", + i, j, alf_luma_coeff); + return AVERROR_INVALIDDATA; + } + luma_coeff[i * ALF_NUM_COEFF_LUMA + j] = alf_luma_coeff; + } + } + + if (alf_luma_clip_flag ) { + for (int i = 0; i < alf_luma_num_filters_signalled; i++) { + for (int j = 0; j < ALF_NUM_COEFF_LUMA; j++) + luma_clip_idx[i * ALF_NUM_COEFF_LUMA + j] = get_bits(gb, 2); + } + } else { + memset(luma_clip_idx, 0, sizeof(luma_clip_idx)); + } + + for (int i = 0; i < ALF_NUM_FILTERS_LUMA; i++) { + const int idx = alf_luma_coeff_delta_idx[i]; + memcpy(&alf->luma_coeff[i * ALF_NUM_COEFF_LUMA], &luma_coeff[idx * ALF_NUM_COEFF_LUMA], + sizeof(luma_coeff[0]) * ALF_NUM_COEFF_LUMA); + memcpy(&alf->luma_clip_idx[i * ALF_NUM_COEFF_LUMA], &luma_clip_idx[idx * ALF_NUM_COEFF_LUMA], + sizeof(luma_clip_idx[0]) * ALF_NUM_COEFF_LUMA); + } + return 0; +} + +static int aps_alf_parse_chroma(VVCALF *alf, GetBitContext *gb, void *log_ctx) +{ + const int alf_chroma_clip_flag = get_bits1(gb); + + alf->num_chroma_filters = get_ue_golomb_long(gb) + 1; + + if (alf->num_chroma_filters > ALF_NUM_FILTERS_CHROMA) { + av_log(log_ctx, AV_LOG_WARNING, "alf_num_chroma_filters_signalled(%d) should <= %d\n", + alf->num_chroma_filters, ALF_NUM_FILTERS_CHROMA); + return AVERROR_INVALIDDATA; + } + + //alf_chroma_coeff_abs, alf_chroma_coeff_sign + for (int i = 0; i < alf->num_chroma_filters; i++) { + for (int j = 0; j < ALF_NUM_COEFF_CHROMA; j++) { + int alf_chroma_coeff = get_ue_golomb(gb); + + if (alf_chroma_coeff && get_bits1(gb)) + alf_chroma_coeff = -alf_chroma_coeff; + if (alf_chroma_coeff < -128 || alf_chroma_coeff > 127) { + av_log(log_ctx, AV_LOG_WARNING, "alf_chroma_coeff[%d][%d](%d) should in range [-128, 127]\n", + i, j, alf_chroma_coeff); + return AVERROR_INVALIDDATA; + } + alf->chroma_coeff[i * ALF_NUM_COEFF_CHROMA + j] = alf_chroma_coeff; + } + if (alf_chroma_clip_flag) { + for (int j = 0; j < ALF_NUM_COEFF_CHROMA; j++) { + const int alf_chroma_clip_idx = get_bits(gb, 2); + alf->chroma_clip_idx[i * ALF_NUM_COEFF_CHROMA + j] = alf_chroma_clip_idx; + } + } + } + return 0; +} + +static int aps_alf_parse_cc(VVCALF *alf, const int idx, GetBitContext *gb, void *log_ctx) +{ + alf->cc_filters_signalled[idx] = get_ue_golomb(gb) + 1; + if (alf->cc_filters_signalled[idx] > ALF_NUM_FILTERS_CC) { + av_log(log_ctx, AV_LOG_WARNING, "cc_filters_signalled[%d](%d) should in range <= %d\n", + idx, alf->cc_filters_signalled[idx], ALF_NUM_FILTERS_CC); + return AVERROR_INVALIDDATA; + } + for (int i = 0; i < alf->cc_filters_signalled[idx]; i++) { + for (int j = 0; j < ALF_NUM_COEFF_CC; j++) { + int coeff = get_bits(gb, 3); + if (coeff) + coeff = (1 - 2 * get_bits1(gb)) * (1 << (coeff - 1)); + alf->cc_coeff[idx][i * ALF_NUM_COEFF_CC + j] = coeff; + } + } + return 0; +} + +static int aps_alf_parse(VVCALF *alf, GetBitContext *gb, void *log_ctx) +{ + const int aps_chroma_present_flag = get_bits1(gb); + const int alf_luma_filter_signal_flag = get_bits1(gb); + int alf_chroma_filter_signal_flag = 0; + int alf_cc_filter_signal_flag[2] = { 0 }; + int ret; + + if (aps_chroma_present_flag) { + alf_chroma_filter_signal_flag = get_bits1(gb); + alf_cc_filter_signal_flag[0] = get_bits1(gb); + alf_cc_filter_signal_flag[1] = get_bits1(gb); + } + if (alf_luma_filter_signal_flag) { + ret = aps_alf_parse_luma(alf, gb, log_ctx); + if (ret < 0) + return ret; + } + if (alf_chroma_filter_signal_flag) { + ret = aps_alf_parse_chroma(alf, gb, log_ctx); + if (ret < 0 ) + return ret; + } + for (int i = 0; i < 2; i++) { + if (alf_cc_filter_signal_flag[i]) { + ret = aps_alf_parse_cc(alf, i, gb, log_ctx); + if (ret < 0) + return ret; + } + } + return 0; +} + +static int aps_lmcs_parse(VVCLMCS *lmcs, GetBitContext *gb, void *log_ctx) +{ + const int aps_chroma_present_flag = get_bits1(gb); + int lmcs_delta_cw_prec; + + lmcs->min_bin_idx = get_ue_golomb(gb); + lmcs->max_bin_idx = LMCS_MAX_BIN_SIZE - 1 - get_ue_golomb(gb); + lmcs_delta_cw_prec = get_ue_golomb(gb) + 1; + if (lmcs->min_bin_idx >= LMCS_MAX_BIN_SIZE) { + av_log(log_ctx, AV_LOG_WARNING, "lmcs_min_bin_idx(%d) should < %d\n", + lmcs->min_bin_idx, LMCS_MAX_BIN_SIZE); + return AVERROR_INVALIDDATA; + } + if (lmcs->max_bin_idx >= LMCS_MAX_BIN_SIZE || lmcs->max_bin_idx < lmcs->min_bin_idx) { + av_log(log_ctx, AV_LOG_WARNING, "lmcs_max_bin_idx(%d) should in range [%d, %d]\n", + lmcs->max_bin_idx, lmcs->min_bin_idx, LMCS_MAX_BIN_SIZE - 1); + return AVERROR_INVALIDDATA; + } + if (lmcs_delta_cw_prec > LMCS_MAX_BIN_SIZE - 1) { + av_log(log_ctx, AV_LOG_WARNING, "lmcs_delta_cw_prec_minus1(%d) should in range [0, 14]\n", + lmcs_delta_cw_prec - 1); + return AVERROR_INVALIDDATA; + } + + memset(&lmcs->delta_cw, 0, sizeof(lmcs->delta_cw)); + for (int i = lmcs->min_bin_idx; i <= lmcs->max_bin_idx; i++) { + lmcs->delta_cw[i] = get_bits(gb, lmcs_delta_cw_prec); + if (lmcs->delta_cw[i] && get_bits1(gb)) + lmcs->delta_cw[i] = -lmcs->delta_cw[i]; + } + if (aps_chroma_present_flag) { + lmcs->delta_crs = get_bits(gb, 3); + if (lmcs->delta_crs && get_bits1(gb)) + lmcs->delta_crs = -lmcs->delta_crs; + } + 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); +} + +static int derive_max_delta(const int id) +{ + return (id < SL_START_4x4) ? id : ((id < SL_START_8x8) ? (id - 2) : (id - 8)); +} + +// 7.4.3.20 Scaling list data semantics +static int aps_scaling_parse(VVCScalingList *sl, GetBitContext *gb, AVCodecContext *log_ctx) +{ + + int coeff[SL_MAX_MATRIX_SIZE * SL_MAX_MATRIX_SIZE]; + const uint8_t *pred; + const int *scaling_list; + const int aps_chroma_present_flag = get_bits1(gb); + + 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 scaling_list_copy_mode_flag = 1; + int scaling_list_pred_mode_flag = 0; + int scaling_list_pred_id_delta = 0; + int dc = 0; + + if (aps_chroma_present_flag || is_luma_list(id)) { + scaling_list_copy_mode_flag = get_bits1(gb); + if (!scaling_list_copy_mode_flag) + scaling_list_pred_mode_flag = get_bits1(gb); + if ((scaling_list_copy_mode_flag || scaling_list_pred_mode_flag) && + id != SL_START_2x2 && + id != SL_START_4x4 && + id != SL_START_8x8) { + const int max_delta = derive_max_delta(id); + ue(scaling_list_pred_id_delta, max_delta); + } + if (!scaling_list_copy_mode_flag) { + int next_coef = 0; + int scaling_list_delta_coef, scaling_list_dc_coef; + + if (id >= SL_START_16x16) { + se(scaling_list_dc_coef, -128, 127); + dc = next_coef = scaling_list_dc_coef; + } + + 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)) { + se(scaling_list_delta_coef, -128, 127); + next_coef += scaling_list_delta_coef; + } + coeff[i] = next_coef; + } + } + } + + //dc + if (id >= SL_START_16x16) { + if (!scaling_list_copy_mode_flag && !scaling_list_pred_mode_flag) { + sl->scaling_matrix_dc_rec[id - SL_START_16x16] = 8; + } else if (!scaling_list_pred_id_delta) { + sl->scaling_matrix_dc_rec[id - SL_START_16x16] = 16; + } else { + const int ref_id = id - scaling_list_pred_id_delta; + 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 = scaling_list_copy_mode_flag ? ff_vvc_scaling_list0 : coeff; + if (!scaling_list_copy_mode_flag && !scaling_list_pred_mode_flag) + pred = ff_vvc_scaling_pred_8; + else if (!scaling_list_pred_id_delta) + pred = ff_vvc_scaling_pred_16; + else + pred = sl->scaling_matrix_rec[id - scaling_list_pred_id_delta]; + 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; + } + } + + return 0; +} + +int ff_vvc_decode_aps(VVCParamSets *ps, GetBitContext *gb, void *log_ctx) +{ + int ret = 0; + const int aps_params_type = get_bits(gb, 3); + const int aps_adaptation_parameter_set_id = get_bits(gb, 5); + + const int max_id[] = { VVC_MAX_ALF_COUNT, VVC_MAX_LMCS_COUNT, VVC_MAX_SL_COUNT }; + const int size[] = { sizeof(VVCALF), sizeof(VVCLMCS), sizeof(VVCScalingList) }; + AVBufferRef **list[] = { ps->alf_list, ps->lmcs_list, ps->scaling_list }; + AVBufferRef *buf; + + if (aps_params_type >= FF_ARRAY_ELEMS(max_id)) { + av_log(log_ctx, AV_LOG_INFO, "Skipping APS type %d\n", aps_params_type); + return 0; + } + if (aps_adaptation_parameter_set_id >= max_id[aps_params_type]) { + av_log(log_ctx, AV_LOG_WARNING, "aps_adaptation_parameter_set_id(%d) should <= %d\n", + aps_adaptation_parameter_set_id, max_id[aps_params_type]); + return AVERROR_INVALIDDATA; + } + + buf = av_buffer_allocz(size[aps_params_type]); + if (!buf) + return AVERROR(ENOMEM); + + switch (aps_params_type) { + case APS_ALF: + ret = aps_alf_parse((VVCALF *)buf->data, gb, log_ctx); + break; + case APS_LMCS: + ret = aps_lmcs_parse((VVCLMCS *)buf->data, gb, log_ctx); + break; + case APS_SCALING: + ret = aps_scaling_parse((VVCScalingList *)buf->data, gb, log_ctx); + break; + default: + av_log(log_ctx, AV_LOG_INFO, "Skipping APS type %d\n", aps_params_type); + goto fail; + } + if (ret < 0) + goto fail; + + get_bits1(gb); // aps_extension_flag + + if (get_bits_left(gb) < 0) { + av_log(log_ctx, AV_LOG_ERROR, + "Overread aps type %d by %d bits\n", aps_params_type, -get_bits_left(gb)); + ret = AVERROR_INVALIDDATA; + goto fail; + } + + ret = av_buffer_replace(&list[aps_params_type][aps_adaptation_parameter_set_id], buf); + +fail: + av_buffer_unref(&buf); + return ret; +} + + +void ff_vvc_ps_uninit(VVCParamSets *ps) +{ + int i; + + for (i = 0; i < FF_ARRAY_ELEMS(ps->scaling_list); i++) + av_buffer_unref(&ps->scaling_list[i]); + for (i = 0; i < FF_ARRAY_ELEMS(ps->lmcs_list); i++) + av_buffer_unref(&ps->lmcs_list[i]); + for (i = 0; i < FF_ARRAY_ELEMS(ps->alf_list); i++) + av_buffer_unref(&ps->alf_list[i]); + for (i = 0; i < FF_ARRAY_ELEMS(ps->vps_list); i++) + av_buffer_unref(&ps->vps_list[i]); + for (i = 0; i < FF_ARRAY_ELEMS(ps->sps_list); i++) + av_buffer_unref(&ps->sps_list[i]); + for (i = 0; i < FF_ARRAY_ELEMS(ps->pps_list); i++) + av_buffer_unref(&ps->pps_list[i]); + av_buffer_unref(&ps->ph_buf); +} + +static int sh_parse_subpic_idx(VVCSH *sh, const VVCParamSets *ps, GetBitContext *gb) +{ + const VVCSPS *sps = ps->sps; + const VVCPPS *pps = ps->pps; + if (!sps->subpic_info_present_flag) + return 0; + + sh->subpic_id = get_bits(gb, sps->subpic_id_len ); + if (!sps->subpic_id_mapping_explicitly_signalled_flag) + return sh->subpic_id; + + for (int i = 0; i < sps->num_subpics; i++) { + if (pps->subpic_id[i] == sh->subpic_id) + return i; + } + return sps->num_subpics; +} + +static void sh_parse_extra_bits(const VVCSPS *sps, GetBitContext *gb) +{ + for (int i = 0; i < sps->num_extra_sh_bytes * 8; i++) { + if (sps->extra_sh_bit_present_flag[i]) + get_bits1(gb); + } +} + +static int sh_parse_slice_address(VVCSH *sh, VVCContext *s, const int curr_subpic_idx, GetBitContext *gb) +{ + void *log_ctx = s->avctx; + const VVCSPS *sps = s->ps.sps; + const VVCPPS *pps = s->ps.pps; + int num_slices_in_subpic = pps->num_slices_in_subpic[curr_subpic_idx]; + + sh->slice_address = 0; + if ((pps->rect_slice_flag && num_slices_in_subpic > 1) || + (!pps->rect_slice_flag && pps->num_tiles_in_pic > 1)) { + unsigned int bits, max; + if (!pps->rect_slice_flag) { + bits = av_ceil_log2(pps->num_tiles_in_pic); + max = pps->num_tiles_in_pic - 1; + } else { + bits = av_ceil_log2(num_slices_in_subpic); + max = num_slices_in_subpic - 1; + } + u(bits, sh->slice_address, 0, max); + } + + sh_parse_extra_bits(sps, gb); + + if (pps->rect_slice_flag) { + int pic_level_slice_idx = sh->slice_address; + for (int j = 0; j < curr_subpic_idx; j++) + pic_level_slice_idx += pps->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 = sh->slice_address % pps->num_tile_columns; + int tile_y = sh->slice_address / pps->num_tile_columns; + const int slice_start_ctb = pps->row_bd[tile_y] * pps->ctb_width + pps->col_bd[tile_x] * pps->row_height[tile_y]; + sh->num_tiles_in_slice = 1; + if (pps->num_tiles_in_pic - sh->slice_address > 1) + uep(sh->num_tiles_in_slice, 1, 1, pps->num_tiles_in_pic - sh->slice_address); + 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 = sh->slice_address; tile_idx < sh->slice_address + sh->num_tiles_in_slice; tile_idx++) { + tile_x = tile_idx % pps->num_tile_columns; + tile_y = tile_idx / pps->num_tile_columns; + sh->num_ctus_in_curr_slice += pps->row_height[tile_y] * pps->column_width[tile_x]; + } + } + + return 0; +} + +static int sh_parse_nb_refs(const VVCSH *sh, uint8_t *nb_refs, const VVCPPS *pps, GetBitContext* gb, void *log_ctx) +{ + if ((!IS_I(sh)&& sh->rpls[0].num_ref_entries > 1) || + (IS_B(sh) && sh->rpls[1].num_ref_entries > 1)) { + int num_ref_idx_active_override_flag = get_bits1(gb); + for (int i = 0; i < (IS_B(sh) ? 2 : 1); i++) { + if (num_ref_idx_active_override_flag) { + nb_refs[i] = 1; + if (sh->rpls[i].num_ref_entries > 1) { + nb_refs[i] = get_ue_golomb_long(gb) + 1; + if (nb_refs[i] > 15) { + av_log(log_ctx, AV_LOG_ERROR, + "num_ref_idx_active_minus1(%d) should in range [0, 14]\n", + nb_refs[i] - 1); + return AVERROR_INVALIDDATA; + } + } + } else { + nb_refs[i] = + FFMIN(pps->num_ref_idx_default_active[i], sh->rpls[i].num_ref_entries); + } + } + } else { + nb_refs[0] = !IS_I(sh); + nb_refs[1] = IS_B(sh); + } + return 0; +} + +static int sh_parse_alf(VVCSH *sh, VVCContext *s, GetBitContext *gb) +{ + int ret; + const VVCParamSets *ps = &s->ps; + + sh->alf.enabled_flag[LUMA] = 0; + if (ps->sps->alf_enabled_flag) { + if (!ps->pps->alf_info_in_ph_flag) { + ret = alf_parse(&sh->alf, ps, gb, s->avctx); + if (ret < 0) + return ret; + } else { + sh->alf = ps->ph->alf; + } + } + return 0; +} + +static int sh_parse_inter(VVCSH *sh, VVCContext *s, GetBitContext *gb) +{ + int ret; + void *log_ctx = s->avctx; + const VVCParamSets *ps = &s->ps; + const VVCSPS *sps = ps->sps; + const VVCPPS *pps = ps->pps; + const VVCPH *ph = ps->ph; + + if (!pps->rpl_info_in_ph_flag && (!IS_IDR(s) || sps->idr_rpl_present_flag)) { + ret = rpls_list_parse(sh->rpls, ps, ph->poc, gb, log_ctx); + if (ret < 0) + return ret; + } else { + memcpy(&sh->rpls, &ph->rpls, sizeof(ph->rpls)); + } + + ret = sh_parse_nb_refs(sh, sh->nb_refs, pps, gb, log_ctx); + if (ret < 0) + return ret; + + sh->cabac_init_flag = 0; + if (!IS_I(sh)) { + if (pps->cabac_init_present_flag) + sh->cabac_init_flag = get_bits1(gb); + if (ph->temporal_mvp_enabled_flag && !pps->rpl_info_in_ph_flag) { + sh->collocated_list = L0; + if (IS_B(sh)) + sh->collocated_list = !get_bits1(gb); + if (sh->nb_refs[sh->collocated_list] > 1) { + ue(sh->collocated_ref_idx, sh->nb_refs[sh->collocated_list] - 1); + } + if (!pps->wp_info_in_ph_flag && + ((pps->weighted_pred_flag && IS_P(sh)) || + (pps->weighted_bipred_flag && IS_B(sh)))) { + ret = hls_pred_weight_table(&sh->pwt, sh->nb_refs, sh->rpls, + sps, pps, gb, log_ctx); + if (ret < 0) + return ret; + } + } + + } + return 0; +} + +static int sh_parse_slice_qp_y(VVCSH *sh, const VVCParamSets *ps, GetBitContext* gb, void *log_ctx) +{ + const VVCSPS *sps = ps->sps; + const VVCPPS *pps = ps->pps; + const VVCPH *ph = ps->ph; + if (!pps->qp_delta_info_in_ph_flag) { + int8_t sh_qp_delta; + se(sh_qp_delta, -6 * (sps->bit_depth - 8) - pps->init_qp, + 63 - pps->init_qp); + sh->slice_qp_y = pps->init_qp + sh_qp_delta; + } else { + sh->slice_qp_y = pps->init_qp + ph->qp_delta; + } + return 0; +} + +static int sh_parse_chroma_qp_offsets(VVCSH *sh, const VVCParamSets *ps, GetBitContext* gb, void *log_ctx) +{ + const VVCSPS *sps = ps->sps; + const VVCPPS *pps = ps->pps; + + memset(sh->chroma_qp_offset, 0, sizeof(sh->chroma_qp_offset)); + + if (pps->slice_chroma_qp_offsets_present_flag) { + int8_t off; + for (int i = CB - 1; i < CR + sps->joint_cbcr_enabled_flag; i++) { + se(sh->chroma_qp_offset[i], -12, 12); + off = pps->chroma_qp_offset[i] + sh->chroma_qp_offset[i]; + if (off < -12 || off > 12) { + av_log(log_ctx, AV_LOG_ERROR, + "chroma_qp_offset(%d) not in range [-12, 12].\n", off); + return AVERROR_INVALIDDATA; + } + } + } + + sh->cu_chroma_qp_offset_enabled_flag = 0; + if (pps->cu_chroma_qp_offset_list_enabled_flag) + sh->cu_chroma_qp_offset_enabled_flag = get_bits1(gb); + return 0; +} + +static void sh_parse_sao_used_flag(VVCSH *sh, const VVCParamSets *ps, GetBitContext *gb) +{ + const VVCSPS *sps = ps->sps; + const VVCPPS *pps = ps->pps; + const VVCPH *ph = ps->ph; + if (sps->sao_enabled_flag && !pps->sao_info_in_ph_flag) { + const int has_chroma = sps->chroma_format_idc != 0; + sh->sao_used_flag[0] = get_bits1(gb); + sh->sao_used_flag[1] = has_chroma ? get_bits1(gb) : 0; + } else { + sh->sao_used_flag[0] = ph->sao_luma_enabled_flag; + sh->sao_used_flag[1] = ph->sao_chroma_enabled_flag; + } + sh->sao_used_flag[2] = sh->sao_used_flag[1]; +} + +static int sh_parse_deblock(VVCSH *sh, const VVCPPS *pps, const VVCPH *ph, GetBitContext *gb, void *log_ctx) +{ + int sh_deblocking_params_present_flag = 0; + + if (pps->deblocking_filter_override_enabled_flag && !pps->dbf_info_in_ph_flag) + sh_deblocking_params_present_flag = get_bits1(gb); + + sh->deblocking_filter_disabled_flag = ph->deblocking_filter_disabled_flag; + if (sh_deblocking_params_present_flag) { + sh->deblocking_filter_disabled_flag = 0; + if (!pps->deblocking_filter_disabled_flag) + sh->deblocking_filter_disabled_flag = get_bits1(gb); + + if (!sh->deblocking_filter_disabled_flag) + return deblock_parse(&sh->deblock,pps, gb, log_ctx); + } + memcpy(&sh->deblock, &ph->deblock, sizeof(sh->deblock)); + + return 0; +} + +static void sh_parse_transform(VVCSH *sh, const VVCSPS *sps, GetBitContext *gb) +{ + sh->dep_quant_used_flag = 0; + if (sps->dep_quant_enabled_flag) + sh->dep_quant_used_flag = get_bits1(gb); + + sh->sign_data_hiding_used_flag = 0; + if (sps->sign_data_hiding_enabled_flag && !sh->dep_quant_used_flag) + sh->sign_data_hiding_used_flag = get_bits1(gb); + + sh->ts_residual_coding_disabled_flag = 0; + if (sps->transform_skip_enabled_flag && + !sh->dep_quant_used_flag && + !sh->sign_data_hiding_used_flag) + sh->ts_residual_coding_disabled_flag = get_bits1(gb); +} + +static int sh_parse_entry_points(VVCSH *sh, const int curr_subpic_idx, + const VVCParamSets *ps, GetBitContext *gb, void *log_ctx) +{ + const VVCSPS *sps = ps->sps; + const VVCPPS *pps = ps->pps; + + sh->num_entry_points = 0; + if (sps->entry_point_offsets_present_flag) { + for (int i = 1; i 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->entropy_coding_sync_enabled_flag)) { + sh->entry_point_start_ctu[sh->num_entry_points] = i; + sh->num_entry_points++; + } + } + + if (sh->num_entry_points > VVC_MAX_ENTRY_POINTS) { + avpriv_request_sample(log_ctx, "Too many entry points: " + "%"PRIu16".\n", sh->num_entry_points); + return AVERROR_PATCHWELCOME; + } + } + + if (sh->num_entry_points > 0) { + int sh_entry_offset_len_minus1; + ue(sh_entry_offset_len_minus1, 31); + for (int i = 0; i < sh->num_entry_points; i++) + sh->entry_point_offset[i] = get_bits(gb, sh_entry_offset_len_minus1 + 1) + 1; + } + + return 0; +} + +static int sh_parse_param_set(VVCContext *s, const int sh_picture_header_in_slice_header_flag, + const int is_first_slice, GetBitContext *gb) +{ + VVCPH *ph = s->ps.ph; + int ret = 0; + + if (!(sh_picture_header_in_slice_header_flag && is_first_slice) && !ph) + return AVERROR_INVALIDDATA; + + if (sh_picture_header_in_slice_header_flag){ + if (is_first_slice) { + ret = ff_vvc_decode_ph(&s->ps, s->pocTid0, IS_CLVSS(s), gb, s->avctx); + if (ret < 0) + return ret; + } else { + skip_bits(gb, ph->size); + } + } + + return ret; +} + +int ff_vvc_decode_sh(VVCSH *sh, VVCContext *s, const int is_first_slice, GetBitContext *gb) +{ + void *log_ctx = s->avctx; + VVCParamSets *ps = &s->ps; + const int sh_picture_header_in_slice_header_flag = get_bits1(gb); + int ret, curr_subpic_idx; + + ret = sh_parse_param_set(s, sh_picture_header_in_slice_header_flag, is_first_slice, gb); + if (ret < 0) + return ret; + + curr_subpic_idx = sh_parse_subpic_idx(sh, ps, gb); + if (curr_subpic_idx >= ps->sps->num_subpics) { + av_log(log_ctx, AV_LOG_ERROR, + "sh->subpic_id(%d) should in range [0, %d]\n", + curr_subpic_idx, ps->sps->num_subpics - 1); + return AVERROR_INVALIDDATA; + } + + ret = sh_parse_slice_address(sh, s, curr_subpic_idx, gb); + if (ret < 0) + return ret; + + sh->slice_type = VVC_SLICE_TYPE_I; + if (ps->ph->inter_slice_allowed_flag) + ue(sh->slice_type, VVC_SLICE_TYPE_I); + + if (IS_CVSS(s)) + sh->no_output_of_prior_pics_flag = get_bits1(gb); + + ret = sh_parse_alf(sh, s, gb); + if (ret < 0) + return ret; + + if (ps->ph->lmcs_enabled_flag && !sh_picture_header_in_slice_header_flag) + sh->lmcs_used_flag = get_bits1(gb); + else + sh->lmcs_used_flag = sh_picture_header_in_slice_header_flag ? ps->ph->lmcs_enabled_flag : 0; + + sh->explicit_scaling_list_used_flag = 0; + if (ps->ph->explicit_scaling_list_enabled_flag) { + sh->explicit_scaling_list_used_flag = 1; + if (!sh_picture_header_in_slice_header_flag) + sh->explicit_scaling_list_used_flag = get_bits1(gb); + } + + ret = sh_parse_inter(sh, s, gb); + if (ret < 0) + return ret; + + ret = sh_parse_slice_qp_y(sh, ps, gb, log_ctx); + if (ret < 0) + return ret; + + ret = sh_parse_chroma_qp_offsets(sh, ps, gb, log_ctx); + if (ret < 0) + return ret; + + sh_parse_sao_used_flag(sh, ps, gb); + + ret = sh_parse_deblock(sh, ps->pps, ps->ph, gb, log_ctx); + if (ret < 0) + return ret; + + sh_parse_transform(sh, ps->sps, gb); + + if (ps->pps->slice_header_extension_present_flag) { + int sh_slice_header_extension_length; + ue(sh_slice_header_extension_length, 256); + skip_bits(gb, 8 * sh_slice_header_extension_length); + } + + ret = sh_parse_entry_points(sh, curr_subpic_idx, ps, gb, log_ctx); + if (ret < 0) + return ret; + + skip_bits(gb, 1); + align_get_bits(gb); + + if (get_bits_left(gb) < 0) { + av_log(log_ctx, AV_LOG_ERROR, + "Overread slice header by %d bits\n", -get_bits_left(gb)); + return AVERROR_INVALIDDATA; + } + + //get calculated values + { + const PartitionConstraints *constraints[2] = { &ps->ph->inter_slice, &ps->ph->inter_slice }; + if (IS_I(sh)) { + constraints[LUMA] = &ps->ph->intra_slice_luma; + constraints[CHROMA] = &ps->ph->intra_slice_chroma; + } + + sh->cu_qp_delta_subdiv = ps->ph->cu_qp_delta_subdiv[!IS_I(sh)]; + sh->cu_chroma_qp_offset_subdiv = ps->ph->cu_chroma_qp_offset_subdiv[!IS_I(sh)]; + + for (int i = LUMA; i <= CHROMA; i++) { + const PartitionConstraints *pc = constraints[i]; + const int min_qt_log2_size = ps->sps->min_cb_log2_size_y + pc->log2_diff_min_qt_min_cb; + + sh->max_bt_size[i] = 1 << (min_qt_log2_size + pc->log2_diff_max_bt_min_qt); + sh->max_tt_size[i] = 1 << (min_qt_log2_size + pc->log2_diff_max_tt_min_qt); + sh->max_mtt_depth[i] = pc->max_mtt_hierarchy_depth; + sh->min_qt_size[i] = 1 << min_qt_log2_size; + } + + } + return 0; +} diff --git a/libavcodec/vvc/vvc_ps.h b/libavcodec/vvc/vvc_ps.h new file mode 100644 index 0000000000..dc094bee41 --- /dev/null +++ b/libavcodec/vvc/vvc_ps.h @@ -0,0 +1,811 @@ +/* + * 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/vvc.h" + +#define IS_VCL(t) ((t) <= VVC_RSV_IRAP_11 && (t) >= VVC_TRAIL_NUT) + +#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(sh) ((sh)->slice_type == VVC_SLICE_TYPE_I) +#define IS_P(sh) ((sh)->slice_type == VVC_SLICE_TYPE_P) +#define IS_B(sh) ((sh)->slice_type == VVC_SLICE_TYPE_B) + +#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 VUI { + uint16_t payload_size; + + uint8_t progressive_source_flag; + uint8_t interlaced_source_flag; + uint8_t non_packed_constraint_flag; + uint8_t non_projected_constraint_flag; + + uint8_t aspect_ratio_info_present_flag; + uint8_t aspect_ratio_constant_flag; + uint8_t aspect_ratio_idc; + + uint16_t sar_width; + uint16_t sar_height; + + uint8_t overscan_info_present_flag; + uint8_t overscan_appropriate_flag; + + uint8_t colour_description_present_flag; + uint8_t colour_primaries; + + uint8_t transfer_characteristics; + uint8_t matrix_coeffs; + uint8_t full_range_flag; + + uint8_t chroma_loc_info_present_flag; + uint8_t chroma_sample_loc_type_frame; + uint8_t chroma_sample_loc_type_top_field; + uint8_t chroma_sample_loc_type_bottom_field; +} VUI; + +typedef struct GeneralConstraintsInfo { + uint8_t present_flag; + /* general */ + uint8_t intra_only_constraint_flag; + uint8_t all_layers_independent_constraint_flag; + uint8_t one_au_only_constraint_flag; + + /* picture format */ + uint8_t sixteen_minus_max_bitdepth_constraint_idc; + uint8_t three_minus_max_chroma_format_constraint_idc; + + /* NAL unit type related */ + uint8_t no_mixed_nalu_types_in_pic_constraint_flag; + uint8_t no_trail_constraint_flag; + uint8_t no_stsa_constraint_flag; + uint8_t no_rasl_constraint_flag; + uint8_t no_radl_constraint_flag; + uint8_t no_idr_constraint_flag; + uint8_t no_cra_constraint_flag; + uint8_t no_gdr_constraint_flag; + uint8_t no_aps_constraint_flag; + uint8_t no_idr_rpl_constraint_flag; + + /* tile, slice, subpicture partitioning */ + uint8_t one_tile_per_pic_constraint_flag; + uint8_t pic_header_in_slice_header_constraint_flag; + uint8_t one_slice_per_pic_constraint_flag; + uint8_t no_rectangular_slice_constraint_flag; + uint8_t one_slice_per_subpic_constraint_flag; + uint8_t no_subpic_info_constraint_flag; + + /* CTU and block partitioning */ + uint8_t three_minus_max_log2_ctu_size_constraint_idc; + uint8_t no_partition_constraints_override_constraint_flag; + uint8_t no_mtt_constraint_flag; + uint8_t no_qtbtt_dual_tree_intra_constraint_flag; + + /* intra */ + uint8_t no_palette_constraint_flag; + uint8_t no_ibc_constraint_flag; + uint8_t no_isp_constraint_flag; + uint8_t no_mrl_constraint_flag; + uint8_t no_mip_constraint_flag; + uint8_t no_cclm_constraint_flag; + + /* inter */ + uint8_t no_ref_pic_resampling_constraint_flag; + uint8_t no_res_change_in_clvs_constraint_flag; + uint8_t no_weighted_prediction_constraint_flag; + uint8_t no_ref_wraparound_constraint_flag; + uint8_t no_temporal_mvp_constraint_flag; + uint8_t no_sbtmvp_constraint_flag; + uint8_t no_amvr_constraint_flag; + uint8_t no_bdof_constraint_flag; + uint8_t no_smvd_constraint_flag; + uint8_t no_dmvr_constraint_flag; + uint8_t no_mmvd_constraint_flag; + uint8_t no_affine_motion_constraint_flag; + uint8_t no_prof_constraint_flag; + uint8_t no_bcw_constraint_flag; + uint8_t no_ciip_constraint_flag; + uint8_t no_gpm_constraint_flag; + + /* transform, quantization, residual */ + uint8_t no_luma_transform_size_64_constraint_flag; + uint8_t no_transform_skip_constraint_flag; + uint8_t no_bdpcm_constraint_flag; + uint8_t no_mts_constraint_flag; + uint8_t no_lfnst_constraint_flag; + uint8_t no_joint_cbcr_constraint_flag; + uint8_t no_sbt_constraint_flag; + uint8_t no_act_constraint_flag; + uint8_t no_explicit_scaling_list_constraint_flag; + uint8_t no_dep_quant_constraint_flag; + uint8_t no_sign_data_hiding_constraint_flag; + uint8_t no_cu_qp_delta_constraint_flag; + uint8_t no_chroma_qp_offset_constraint_flag; + + /* loop filter */ + uint8_t no_sao_constraint_flag; + uint8_t no_alf_constraint_flag; + uint8_t no_ccalf_constraint_flag; + uint8_t no_lmcs_constraint_flag; + uint8_t no_ladf_constraint_flag; + uint8_t no_virtual_boundaries_constraint_flag; +} GeneralConstraintsInfo; + +typedef struct PTL { + uint8_t general_profile_idc; + uint8_t general_tier_flag; + uint8_t general_level_idc; + uint8_t frame_only_constraint_flag; + uint8_t multilayer_enabled_flag; + GeneralConstraintsInfo gci; + uint8_t sublayer_level_present_flag[VVC_MAX_SUBLAYERS - 1]; + uint8_t sublayer_level_idc[VVC_MAX_SUBLAYERS - 1]; + uint8_t num_sub_profiles; + uint32_t general_sub_profile_idc[VVC_MAX_SUB_PROFILES]; +} PTL; + +typedef struct DpbParameters { + uint8_t max_dec_pic_buffering[VVC_MAX_SUBLAYERS]; + uint8_t max_num_reorder_pics[VVC_MAX_SUBLAYERS]; + uint8_t max_latency_increase[VVC_MAX_SUBLAYERS]; +} DpbParameters; + +typedef struct VVCVPS { + uint8_t video_parameter_set_id; + uint8_t max_layers; + uint8_t max_sublayers_minus1; + uint8_t default_ptl_dpb_hrd_max_tid_flag; + uint8_t all_independent_layers_flag; + uint8_t layer_id[VVC_MAX_LAYERS]; + uint8_t independent_layer_flag[VVC_MAX_LAYERS]; + uint8_t max_tid_ref_present_flag[VVC_MAX_LAYERS]; + uint8_t direct_ref_layer_flag[VVC_MAX_LAYERS][VVC_MAX_LAYERS - 1]; + uint8_t max_tid_il_ref_pics_plus1[VVC_MAX_LAYERS][VVC_MAX_LAYERS - 1]; + uint8_t each_layer_is_an_ols_flag; + uint8_t ols_mode_idc; + uint8_t num_output_layer_sets_minus2; + uint8_t ols_output_layer_flag[VVC_MAX_TOTAL_NUM_OLSS][VVC_MAX_LAYERS]; + + uint8_t num_ptls_minus1; + uint8_t pt_present_flag[VVC_MAX_PTLS]; + uint8_t ptl_max_tid[VVC_MAX_PTLS]; + + uint8_t ols_ptl_idx[VVC_MAX_TOTAL_NUM_OLSS]; + + uint16_t num_dpb_params_minus1; + uint8_t sublayer_dpb_params_present_flag; + uint8_t dpb_max_tid[VVC_MAX_TOTAL_NUM_OLSS]; + uint16_t ols_dpb_pic_width[VVC_MAX_TOTAL_NUM_OLSS]; + uint16_t ols_dpb_pic_height[VVC_MAX_TOTAL_NUM_OLSS]; + uint8_t ols_dpb_chroma_format[VVC_MAX_TOTAL_NUM_OLSS]; + uint8_t ols_dpb_bitdepth_minus8[VVC_MAX_TOTAL_NUM_OLSS]; + uint16_t ols_dpb_params_idx[VVC_MAX_TOTAL_NUM_OLSS]; + + uint8_t timing_hrd_params_present_flag; + uint8_t sublayer_cpb_params_present_flag; + uint16_t num_ols_timing_hrd_params_minus1; + uint8_t hrd_max_tid[VVC_MAX_TOTAL_NUM_OLSS]; + uint8_t ols_timing_hrd_idx[VVC_MAX_TOTAL_NUM_OLSS]; + + uint8_t data[4096]; + int data_size; +} VVCVPS; + +typedef struct VVCWindow { + int left_offset; + int right_offset; + int top_offset; + int bottom_offset; +} VVCWindow; + +typedef struct VVCRefPicListStructEntry { + uint8_t inter_layer_ref_pic_flag; + uint8_t st_ref_pic_flag; + + //shortterm + int8_t delta_poc_val_st; ///< DeltaPocValSt + + //longterm + uint8_t lt_msb_flag; ///< delta_poc_msb_cycle_present_flag + int16_t lt_poc; + + //interlayer + uint8_t ilrp_idx; +} VVCRefPicListStructEntry; + +typedef struct VVCRefPicListStruct { + uint8_t num_ref_entries; + uint8_t num_ltrp_entries; ///< NumLtrpEntries + uint8_t ltrp_in_header_flag; + VVCRefPicListStructEntry entries[VVC_MAX_REF_ENTRIES]; +} VVCRefPicListStruct; + +/* + * we use PartitionConstraints for following fields in spec + uint8_t log2_diff_min_qt_min_cb_intra_slice_luma; + uint8_t max_mtt_hierarchy_depth_intra_slice_luma; + uint8_t log2_diff_max_bt_min_qt_intra_slice_luma; + uint8_t log2_diff_max_tt_min_qt_intra_slice_luma; + + uint8_t log2_diff_min_qt_min_cb_intra_slice_chroma; + uint8_t max_mtt_hierarchy_depth_intra_slice_chroma; + uint8_t log2_diff_max_bt_min_qt_intra_slice_chroma; + uint8_t log2_diff_max_tt_min_qt_intra_slice_chroma; + + uint8_t log2_diff_min_qt_min_cb_inter_slice; + uint8_t max_mtt_hierarchy_depth_inter_slice; + uint8_t log2_diff_max_bt_min_qt_inter_slice; + uint8_t log2_diff_max_tt_min_qt_inter_slice; + +*/ +typedef struct PartitionConstraints { + uint8_t log2_diff_min_qt_min_cb; + uint8_t max_mtt_hierarchy_depth; + uint8_t log2_diff_max_bt_min_qt; + uint8_t log2_diff_max_tt_min_qt; +} PartitionConstraints; + +typedef struct GeneralTimingHrdParameters { + uint8_t general_nal_hrd_params_present_flag; + uint8_t general_vcl_hrd_params_present_flag; + uint8_t general_du_hrd_params_present_flag; + int hrd_cpb_cnt; ///< hrd_cpb_cnt_minus1 + 1 +} GeneralTimingHrdParameters; + +typedef struct VirtualBoundaries { + uint8_t virtual_boundaries_present_flag; + uint8_t num_ver_virtual_boundaries; + uint16_t virtual_boundary_pos_x_minus1[3]; + uint8_t num_hor_virtual_boundaries; + uint16_t virtual_boundary_pos_y_minus1[3]; +} VirtualBoundaries; + +typedef struct VVCSPS { + unsigned video_parameter_set_id; + + VVCWindow output_window; + + VVCWindow conf_win; + + int pixel_shift; + + enum AVPixelFormat pix_fmt; + + uint8_t max_sublayers; + uint8_t chroma_format_idc; + + uint8_t ref_pic_resampling_enabled_flag; + uint8_t res_change_in_clvs_allowed_flag; + + uint16_t width; + uint16_t height; + + uint8_t subpic_info_present_flag; + uint16_t num_subpics; + uint8_t independent_subpics_flag; + uint16_t subpic_ctu_top_left_x[VVC_MAX_SLICES]; + uint16_t subpic_ctu_top_left_y[VVC_MAX_SLICES]; + uint16_t subpic_width[VVC_MAX_SLICES]; + uint16_t subpic_height[VVC_MAX_SLICES]; + uint8_t subpic_treated_as_pic_flag[VVC_MAX_SLICES]; + uint8_t loop_filter_across_subpic_enabled_flag[VVC_MAX_SLICES]; + uint8_t subpic_id_len; + uint8_t subpic_id_mapping_explicitly_signalled_flag; + uint8_t subpic_id_mapping_present_flag; + uint32_t subpic_id[VVC_MAX_SLICES]; + + uint8_t entropy_coding_sync_enabled_flag; + uint8_t entry_point_offsets_present_flag; + + uint8_t log2_max_pic_order_cnt_lsb; + uint8_t poc_msb_cycle_flag; + uint8_t poc_msb_cycle_len; + + uint8_t num_extra_ph_bytes; + uint8_t extra_ph_bit_present_flag[16]; + + uint8_t num_extra_sh_bytes; + uint8_t extra_sh_bit_present_flag[16]; + + uint8_t sublayer_dpb_params_flag; + DpbParameters dpb; + + uint8_t partition_constraints_override_enabled_flag; + uint8_t qtbtt_dual_tree_intra_flag; + + PartitionConstraints intra_slice_luma; + PartitionConstraints intra_slice_chroma; + PartitionConstraints inter_slice; + + uint8_t transform_skip_enabled_flag; + uint8_t bdpcm_enabled_flag; + + uint8_t mts_enabled_flag; + uint8_t explicit_mts_intra_enabled_flag; + uint8_t explicit_mts_inter_enabled_flag; + + uint8_t lfnst_enabled_flag; + + uint8_t joint_cbcr_enabled_flag; + + uint8_t sao_enabled_flag; + uint8_t alf_enabled_flag; + uint8_t ccalf_enabled_flag; + uint8_t lmcs_enabled_flag; + + // inter + uint8_t weighted_pred_flag; + uint8_t weighted_bipred_flag; + uint8_t long_term_ref_pics_flag; + uint8_t inter_layer_prediction_enabled_flag; + uint8_t idr_rpl_present_flag; + uint8_t num_ref_pic_lists[2]; + VVCRefPicListStruct ref_pic_list_struct[2][VVC_MAX_REF_PIC_LISTS]; + uint8_t ref_wraparound_enabled_flag; + uint8_t temporal_mvp_enabled_flag; + uint8_t sbtmvp_enabled_flag; + uint8_t amvr_enabled_flag; + uint8_t bdof_enabled_flag; + uint8_t bdof_control_present_in_ph_flag; + uint8_t smvd_enabled_flag; + uint8_t dmvr_enabled_flag; + uint8_t dmvr_control_present_in_ph_flag; + uint8_t mmvd_enabled_flag; + uint8_t mmvd_fullpel_only_enabled_flag; + uint8_t sbt_enabled_flag; + uint8_t affine_enabled_flag; + uint8_t five_minus_max_num_subblock_merge_cand; + uint8_t six_param_affine_enabled_flag; + uint8_t affine_amvr_enabled_flag; + uint8_t affine_prof_enabled_flag; + uint8_t prof_control_present_in_ph_flag; + uint8_t bcw_enabled_flag; + uint8_t ciip_enabled_flag; + uint8_t gpm_enabled_flag; + uint8_t log2_parallel_merge_level; + + // intra + uint8_t isp_enabled_flag; + uint8_t mrl_enabled_flag; + uint8_t mip_enabled_flag; + uint8_t cclm_enabled_flag; + uint8_t chroma_horizontal_collocated_flag; + uint8_t chroma_vertical_collocated_flag; + uint8_t palette_enabled_flag; + uint8_t act_enabled_flag; + uint8_t min_qp_prime_ts; + uint8_t ibc_enabled_flag; + + uint8_t ladf_enabled_flag; + uint8_t num_ladf_intervals; ///< sps_num_ladf_intervals_minus2 + 2; + int8_t ladf_lowest_interval_qp_offset; + int8_t ladf_qp_offset[LADF_MAX_INTERVAL]; + + uint8_t explicit_scaling_list_enabled_flag; + uint8_t scaling_matrix_for_lfnst_disabled_flag; + uint8_t scaling_matrix_for_alternative_colour_space_disabled_flag; + uint8_t scaling_matrix_designated_colour_space_flag; + uint8_t dep_quant_enabled_flag; + uint8_t sign_data_hiding_enabled_flag; + + uint8_t virtual_boundaries_enabled_flag; + VirtualBoundaries vbs; + + uint8_t timing_hrd_params_present_flag; + uint8_t sublayer_cpb_params_present_flag; + GeneralTimingHrdParameters general_timing_hrd_parameters; + + uint8_t field_seq_flag; + + VUI vui; + + PTL ptl; + + int hshift[VVC_MAX_SAMPLE_ARRAYS]; + int vshift[VVC_MAX_SAMPLE_ARRAYS]; + + //derived values + unsigned int max_pic_order_cnt_lsb; ///< MaxPicOrderCntLsb + 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 + unsigned int ladf_interval_lower_bound[LADF_MAX_INTERVAL]; ///< SpsLadfIntervalLowerBound[] + + uint8_t data[4096]; + int data_size; + + int chroma_qp_table[VVC_MAX_SAMPLE_ARRAYS][VVC_MAX_POINTS_IN_QP_TABLE]; +} VVCSPS; + +typedef struct DBParams { + int beta_offset[VVC_MAX_SAMPLE_ARRAYS]; + int tc_offset[VVC_MAX_SAMPLE_ARRAYS]; +} DBParams; + +typedef struct VVCPPS { + uint8_t pic_parameter_set_id; + uint8_t seq_parameter_set_id; + uint8_t mixed_nalu_types_in_pic_flag; + uint16_t width; + uint16_t height; + + VVCWindow conf_win; + VVCWindow scaling_win; + + uint8_t output_flag_present_flag; + uint8_t no_pic_partition_flag; + + uint8_t loop_filter_across_tiles_enabled_flag; + uint8_t rect_slice_flag; + uint8_t single_slice_per_subpic_flag; + + uint8_t tile_idx_delta_present_flag; + + uint8_t loop_filter_across_slices_enabled_flag; + uint8_t cabac_init_present_flag; + + uint8_t num_ref_idx_default_active[2]; + uint8_t rpl1_idx_present_flag; + uint8_t weighted_pred_flag; + uint8_t weighted_bipred_flag; + uint8_t ref_wraparound_enabled_flag; + int8_t init_qp; ///< 26 + pps_init_qp_minus26 + uint8_t cu_qp_delta_enabled_flag; + + uint8_t chroma_tool_offsets_present_flag; + int8_t chroma_qp_offset[3]; ///< pps_cb_qp_offset, pps_cr_qp_offset, pps_joint_cbcr_qp_offset_value + uint8_t slice_chroma_qp_offsets_present_flag; + uint8_t cu_chroma_qp_offset_list_enabled_flag; + uint8_t chroma_qp_offset_list_len_minus1; + int8_t chroma_qp_offset_list[6][3]; ///< pps_cb_qp_offset_list, pps_cr_qp_offset_list, pps_joint_cbcr_qp_offset_list + + uint8_t deblocking_filter_control_present_flag; + uint8_t deblocking_filter_override_enabled_flag; + uint8_t deblocking_filter_disabled_flag; + uint8_t dbf_info_in_ph_flag; + DBParams deblock; + + uint8_t rpl_info_in_ph_flag; + uint8_t sao_info_in_ph_flag; + uint8_t alf_info_in_ph_flag; + uint8_t wp_info_in_ph_flag; + uint8_t qp_delta_info_in_ph_flag; + + uint8_t picture_header_extension_present_flag; + uint8_t slice_header_extension_present_flag; + + //derived value; + uint16_t num_tiles_in_pic; + uint16_t slice_start_offset [VVC_MAX_SLICES]; + uint16_t num_slices_in_subpic[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; + int ctb_count; + + uint16_t min_pu_width; + uint16_t min_pu_height; + uint16_t min_tu_width; + uint16_t min_tu_height; + + uint16_t num_tile_columns; ///< NumTileColumns + uint16_t num_tile_rows; ///< NumTileRows + unsigned int *column_width; ///< ColWidthVal + unsigned int *row_height; ///< RowHeightVal + unsigned int *col_bd; ///< TileColBdVal + unsigned int *row_bd; ///< TileRowBdVal + unsigned int *ctb_to_col_bd; ///< CtbToTileColBd + unsigned int *ctb_to_row_bd; ///< CtbToTileRowBd + unsigned int *ctb_addr_in_slice; ///< CtbAddrInCurrSlice for entire picture + + 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 subpic_id[VVC_MAX_SLICES]; ///< SubpicIdVal[] + uint16_t ref_wraparound_offset; ///< PpsRefWraparoundOffset + + uint8_t data[4096]; + int data_size; +} VVCPPS; + +#define VVC_MAX_ALF_COUNT 8 +#define VVC_MAX_LMCS_COUNT 4 +#define VVC_MAX_SL_COUNT 8 + +#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 + +enum { + APS_ALF, + APS_LMCS, + APS_SCALING, +}; + +typedef struct Alf { + //< ph_alf_enabled_flag, ph_alf_cb_enabled_flag, ph_alf_cr_enabled_flag + //< sh_alf_enabled_flag, sh_alf_cb_enabled_flag, sh_alf_cr_enabled_flag + uint8_t enabled_flag[VVC_MAX_SAMPLE_ARRAYS]; + + //< ph_num_alf_aps_ids_luma + //< sh_alf_aps_id_luma + uint8_t num_aps_ids_luma; + + //< ph_alf_aps_id_luma + //< sh_alf_aps_id_luma + uint8_t aps_id_luma[8]; + + //< ph_alf_aps_id_chroma + //< sh_alf_aps_id_chroma + uint8_t aps_id_chroma; + + //< ph_alf_cc_cb_enabled_flag, ph_alf_cc_cr_enabled_flag + //< sh_alf_cc_cb_enabled_flag, sh_alf_cc_cr_enabled_flag + uint8_t cc_enabled_flag[2]; + + //< ph_alf_cc_cb_aps_id, ph_alf_cc_cr_aps_id + //< sh_alf_cc_cb_aps_id, sh_alf_cc_cr_aps_id + uint8_t cc_aps_id[2]; +} Alf; + +#define MAX_WEIGHTS 15 +typedef struct PredWeightTable { + int log2_denom[2]; ///< luma_log2_weight_denom, ChromaLog2WeightDenom + + int nb_weights[2]; ///< num_l0_weights, num_l1_weights + int weight_flag[2][2][MAX_WEIGHTS]; ///< luma_weight_l0_flag, chroma_weight_l0_flag, + ///< luma_weight_l1_flag, chroma_weight_l1_flag, + int weight[2][VVC_MAX_SAMPLE_ARRAYS][MAX_WEIGHTS]; ///< LumaWeightL0, LumaWeightL1, ChromaWeightL0, ChromaWeightL1 + int offset[2][VVC_MAX_SAMPLE_ARRAYS][MAX_WEIGHTS]; ///< luma_offset_l0, luma_offset_l1, ChromaOffsetL0, ChromaOffsetL1 +} PredWeightTable; + +typedef struct VVCPH { + uint8_t gdr_or_irap_pic_flag; + uint8_t non_ref_pic_flag; + uint8_t gdr_pic_flag; + + uint8_t inter_slice_allowed_flag; + uint8_t intra_slice_allowed_flag; + + uint8_t pic_parameter_set_id; + + uint16_t pic_order_cnt_lsb; + uint8_t recovery_poc_cnt; + uint8_t poc_msb_cycle_present_flag; + uint8_t poc_msb_cycle_val; + + Alf alf; + + uint8_t lmcs_enabled_flag; + uint8_t lmcs_aps_id; + + uint8_t chroma_residual_scale_flag; + uint8_t explicit_scaling_list_enabled_flag; + uint8_t scaling_list_aps_id; + + VirtualBoundaries vbs; + + uint8_t pic_output_flag; + VVCRefPicListStruct rpls[2]; + + uint8_t partition_constraints_override_flag; + PartitionConstraints intra_slice_luma; + PartitionConstraints intra_slice_chroma; + PartitionConstraints inter_slice; + + + uint8_t cu_qp_delta_subdiv[2]; ///< cu_qp_delta_subdiv_intra_slice, cu_qp_delta_subdiv_inter_slice + uint8_t cu_chroma_qp_offset_subdiv[2]; ///< cu_chroma_qp_offset_subdiv_intra_slice, cu_chroma_qp_offset_subdiv_inter_slice + + uint8_t temporal_mvp_enabled_flag; + uint8_t collocated_ref_idx; + uint8_t mmvd_fullpel_only_flag; + uint8_t mvd_l1_zero_flag; + uint8_t bdof_disabled_flag; + uint8_t dmvr_disabled_flag; + uint8_t prof_disabled_flag; + PredWeightTable pwt; + + int8_t qp_delta; + uint8_t joint_cbcr_sign_flag; + + uint8_t sao_luma_enabled_flag; + uint8_t sao_chroma_enabled_flag; + + uint8_t deblocking_filter_disabled_flag; + DBParams deblock; + + //derived values + unsigned int max_num_subblock_merge_cand; ///< MaxNumSubblockMergeCand + + int poc; ///< PicOrderCntVal + uint8_t collocated_list; ///< !collocated_from_l0_flag + + //*2 for high depth + uint8_t lmcs_fwd_lut[LMCS_MAX_LUT_SIZE * 2]; + uint8_t lmcs_inv_lut[LMCS_MAX_LUT_SIZE * 2]; + int lmcs_min_bin_idx; + int lmcs_max_bin_idx; + int lmcs_pivot[LMCS_MAX_BIN_SIZE + 1]; + int lmcs_chroma_scale_coeff[LMCS_MAX_BIN_SIZE]; + + int size; +} VVCPH; + +typedef struct VVCALF { + int8_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; + int8_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 cc_filters_signalled[2]; //< alf_cc_cb_filters_signalled + 1, alf_cc_cr_filters_signalled + 1 + int8_t cc_coeff[2][ALF_NUM_FILTERS_CC * ALF_NUM_COEFF_CC]; +} VVCALF; + +#define SL_MAX_ID 28 +#define SL_MAX_MATRIX_SIZE 8 + +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, +}; + +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 VVCParamSets { + AVBufferRef *vps_list[VVC_MAX_VPS_COUNT]; + AVBufferRef *sps_list[VVC_MAX_SPS_COUNT]; + AVBufferRef *pps_list[VVC_MAX_PPS_COUNT]; + AVBufferRef *alf_list[VVC_MAX_ALF_COUNT]; + AVBufferRef *lmcs_list[VVC_MAX_LMCS_COUNT]; + AVBufferRef *scaling_list[VVC_MAX_SL_COUNT]; + AVBufferRef *ph_buf; + + const VVCSPS *sps; + const VVCPPS *pps; + VVCPH *ph; +} VVCParamSets; + +typedef struct VVCFrameParamSets { + AVBufferRef *sps_buf; + AVBufferRef *pps_buf; + AVBufferRef *ph_buf; + AVBufferRef *alf_list[VVC_MAX_ALF_COUNT]; + AVBufferRef *lmcs_list[VVC_MAX_LMCS_COUNT]; + AVBufferRef *scaling_list[VVC_MAX_SL_COUNT]; + + const VVCSPS *sps; + const VVCPPS *pps; + const VVCPH *ph; +} VVCFrameParamSets; + +typedef struct VVCSH { + + uint16_t subpic_id; + uint16_t slice_address; + uint8_t num_tiles_in_slice; + uint8_t slice_type; + uint8_t no_output_of_prior_pics_flag; + + Alf alf; + + uint8_t lmcs_used_flag; + uint8_t explicit_scaling_list_used_flag; + + VVCRefPicListStruct rpls[2]; + + uint8_t num_ref_idx_active_override_flag; + uint8_t cabac_init_flag; + uint8_t collocated_ref_idx; + + PredWeightTable pwt; + + int8_t chroma_qp_offset[3]; ///< cb_qp_offset, cr_qp_offset, joint_cbcr_qp_offset + uint8_t cu_chroma_qp_offset_enabled_flag; + + uint8_t sao_used_flag[VVC_MAX_SAMPLE_ARRAYS]; ///< sh_sao_luma_used_flag and sh_sao_chroma_used_flag + + uint8_t deblocking_filter_disabled_flag; + DBParams deblock; + uint8_t dep_quant_used_flag; + + uint8_t sign_data_hiding_used_flag; + uint8_t ts_residual_coding_disabled_flag; + + //calculated value; + uint8_t collocated_list; ///< !sh_collocated_from_l0_flag + uint8_t nb_refs[2]; ///< NumRefIdxActive[] + 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 + int8_t slice_qp_y; ///< SliceQpY + int8_t ref_idx_sym[2]; ///< RefIdxSymL0, RefIdxSymL1 + int num_ctus_in_curr_slice; ///< NumCtusInCurrSlice + uint32_t entry_point_offset[VVC_MAX_ENTRY_POINTS]; ///< entry_point_offset_minus1 + 1 + uint32_t entry_point_start_ctu[VVC_MAX_ENTRY_POINTS]; ///< entry point start in ctu_addr + unsigned int *ctb_addr_in_curr_slice; ///< CtbAddrInCurrSlice + unsigned int num_entry_points; ///< NumEntryPoints + + uint8_t first_slice_flag; +} VVCSH; + +struct VVCContext; + +int ff_vvc_decode_sps(VVCParamSets *ps, GetBitContext *gb, + int apply_defdispwin, int nuh_layer_id, AVCodecContext *avctx); +int ff_vvc_decode_pps(VVCParamSets *ps, GetBitContext *gb, void *log_ctx); +int ff_vvc_decode_ph(VVCParamSets *ps, const int poc_tid0, const int is_clvss, GetBitContext *gb, void *log_ctx); +int ff_vvc_decode_aps(VVCParamSets *ps, GetBitContext *gb, void *log_ctx); +int ff_vvc_decode_sh(VVCSH *sh, struct VVCContext *s, const int is_first_slice, GetBitContext *gb); +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 7097293af8..2b01ae94ff 100644 --- a/libavcodec/vvc/vvcdec.h +++ b/libavcodec/vvc/vvcdec.h @@ -30,6 +30,7 @@ #include "libavcodec/vvc.h" #include "vvc_executor.h" +#include "vvc_ps.h" #define LUMA 0 #define CHROMA 1 @@ -268,6 +269,8 @@ typedef struct VVCContext { const AVClass *c; // needed by private avoptions AVCodecContext *avctx; + VVCParamSets ps; + int temporal_id; ///< temporal_id_plus1 - 1 int pocTid0; From patchwork Sun May 21 13:03:09 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 41762 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:ba91:b0:105:feb:71f2 with SMTP id fb17csp1090667pzb; Sun, 21 May 2023 06:04:32 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5d9dOkIg1PPsYTom9yL6FGY21EkK5oqWVPdgDoFkEwB6r1iCulA+wzQHnCjrdtsvxCm9B7 X-Received: by 2002:aa7:d8d4:0:b0:50b:bfee:ea26 with SMTP id k20-20020aa7d8d4000000b0050bbfeeea26mr6104492eds.35.1684674271974; Sun, 21 May 2023 06:04:31 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684674271; cv=none; d=google.com; s=arc-20160816; b=dMDCnjKkqU1uHDCVceZDQoIxUhuYquDTju4fMb5e4Lw3SxEfnScWSkgC6Cp5Qi19KI 5RekHRSWxDIpY9wFS3ZLmKSxU8bNXvLHFcFNWhRgOTLAKbeKuXM+UDpLpRwBHBfTT09e j6k2+IoBlUmK6QhkmYhMmRkBiciD0iZfT6QAdAME+NqD8riVjwnqmkl0p/OOuXQHO+HT fOrsHVRJNp15OkvmfIUhY8J3MMI5PFXw+GDPpKUrudvFzGugGrL+YGc8YqmRIYfHwv6u VEcXnBlWbsxAyjn58Ir8reaurOi4qvQTukR7OswLrXx/kF1BDdmB2FsgPJRwIJUn2qh8 rndA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=zgb9qUlLHnsQeG6AKib8agqJ8SqS0GBitKVnD8ygPAg=; b=Fj2zjnVOThO1R1BYXp+qoNtjMV3y1Ezrp9gnt/qBpHhbSBR4Jwn+cUfXY3PR4N2wGt LsITb45nPpC2u0gboffw7ogC0EW0N60pm1Y0G+tNx9neseZ9KNYBnObvt3TL9gp0MAcT IIkuwYiuTAek2O+wQD7ESq4wv0xFkh+NUhBOljHeYB0DzxBMUxX+cK+1Q9nYJBs3QDEB Y3G+ZV1VL31teqmW3erjwgIylNRhdBwsejCbDAX5STicIe7M1PZJJopbyStiBEjs85Xc G3GRgHT/slaISHlXHO+YKgZuvDKL81Q6uepPnrEhfSYGEPtihlWgLOBzF3CDTPLeASCA VxPg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20221208 header.b=E7eMq7qX; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id s2-20020aa7c542000000b0050d8954be10si2912522edr.119.2023.05.21.06.04.31; Sun, 21 May 2023 06:04:31 -0700 (PDT) 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=@gmail.com header.s=20221208 header.b=E7eMq7qX; 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 0693768C122; Sun, 21 May 2023 16:03:46 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f182.google.com (mail-pf1-f182.google.com [209.85.210.182]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 393FB68C125 for ; Sun, 21 May 2023 16:03:38 +0300 (EEST) Received: by mail-pf1-f182.google.com with SMTP id d2e1a72fcca58-64d30ab1ef2so1751928b3a.2 for ; Sun, 21 May 2023 06:03:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1684674216; x=1687266216; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=iA3RV7oVydu4/ZTdJosSHLi2t+/NF2N9OZqI9avpdFA=; b=E7eMq7qXFsCRIaqjDZOL82JYJ7rkRl94lOUSv3/jwKSq3RLhcmwL7M6ODb8uEmQs53 ksXRU9kALCkyOAlBqVyIqwikCiaOj31PVShtIMaVMi4sQu1zCm3pwXNPyDIig3H9oi5f iv+lB2kn9UOP8QWOL4zzSWsCxKOOWftpVS+s+h6EXC+RGlwvMhs5qKoLkv8IfZf/FmTo GWEzA9O7Usyksee0mqwtXy02Eihq6q5xPmC1MCjEBs1zw2p/LCwHtsqt/9MQSv0fpTeQ 2Y0vktrjjXW8IQCtXPER8FvOu34FwGJhRH1qagr6+Mhizoi44px6uaO+IOW5xli9B1yz aJeg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684674216; x=1687266216; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=iA3RV7oVydu4/ZTdJosSHLi2t+/NF2N9OZqI9avpdFA=; b=Qiv1/piS77cRLWBvosWZ3rw+7GB8XGgzOBh6ND78qKniBSOyqmGrMRMrrcNM9+U2Tx pM0uOMocMdR1W0Yfes5pzk1LPaf4s5Tzef+amUGalUulW2G6JiHJN64pXAUjKl6jdsoO C06yJbP6NPEizzDImzIQWpdUVMfR/naoS2XDrkgGQIH4FxVB9egaN73+KmNuTYTLnsEP 57n7etqzwSJnVErHmJIHyy7aNyZFyXfMX3+hNywIU6C7huR713KqMYhXIryNOuQo+rnX ACthfFUVE3jfx1DXeINPO+Esg+5/9YEM54gWq3HOxXX61+Nt8qoaA48ato6LfMO4TQiS uYPA== X-Gm-Message-State: AC+VfDxurAGRxsXfdsqPmYvZ9+2BjRAVCB9xlGovl3Vyb55O/FeChj4N s/QMNx4GgMbqIJkFM9hCLX2a+G5be36XmQ== X-Received: by 2002:a05:6a21:33aa:b0:10a:b2a2:a301 with SMTP id yy42-20020a056a2133aa00b0010ab2a2a301mr5127153pzb.12.1684674214781; Sun, 21 May 2023 06:03:34 -0700 (PDT) Received: from NuoMi.localdomain ([112.64.8.97]) by smtp.gmail.com with ESMTPSA id t9-20020aa79389000000b0064d45eea573sm1671872pfe.41.2023.05.21.06.03.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 21 May 2023 06:03:34 -0700 (PDT) From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 21 May 2023 21:03:09 +0800 Message-Id: <20230521130319.13813-5-nuomi2021@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230521130319.13813-1-nuomi2021@gmail.com> References: <20230521130319.13813-1-nuomi2021@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 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: RntFXBDnMTzz add Context-based Adaptive Binary Arithmetic Coding (CABAC) decoder --- libavcodec/vvc/Makefile | 1 + libavcodec/vvc/vvc_cabac.c | 2423 ++++++++++++++++++++++++++++++++++++ libavcodec/vvc/vvc_cabac.h | 126 ++ libavcodec/vvc/vvc_ctu.h | 387 ++++++ libavcodec/vvc/vvcdec.h | 5 + 5 files changed, 2942 insertions(+) create mode 100644 libavcodec/vvc/vvc_cabac.c create mode 100644 libavcodec/vvc/vvc_cabac.h create mode 100644 libavcodec/vvc/vvc_ctu.h diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile index 3dbfb70407..f70cc7a6f2 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -5,3 +5,4 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ vvc/vvc_executor.o \ vvc/vvc_ps.o \ vvc/vvc_data.o \ + vvc/vvc_cabac.o diff --git a/libavcodec/vvc/vvc_cabac.c b/libavcodec/vvc/vvc_cabac.c new file mode 100644 index 0000000000..73a95c2d6b --- /dev/null +++ b/libavcodec/vvc/vvc_cabac.c @@ -0,0 +1,2423 @@ +/* + * 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 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 VVCSH *sh = &lc->sc->sh; + const int qp = av_clip_uintp2(sh->slice_qp_y, 6); + int init_type = 2 - sh->slice_type; + + av_assert0(VVC_CONTEXTS == SYNTAX_ELEMENT_LAST); + + if (sh->cabac_init_flag && sh->slice_type != VVC_SLICE_TYPE_I) + 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 ctb_addr, + const int ctb_addr_x, const int ctb_addr_y) +{ + int ret = 0; + const VVCPPS *pps = lc->fc->ps.pps; + const int first_ctb_in_slice = !ctb_addr; + const int first_ctb_in_tile = ctb_addr_x == pps->ctb_to_col_bd[ctb_addr_x] && ctb_addr_y == pps->ctb_to_row_bd[ctb_addr_y]; + + 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.alf.num_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->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) +{ + int loc_sum = 0; + level += w * yc + xc; + if (xc < w - 1) { + loc_sum += level[1]; + if (xc < w - 2) + loc_sum += level[2]; + if (yc < h - 1) + loc_sum += level[w + 1]; + } + if (yc < h - 1) { + loc_sum += level[w]; + if (yc < h - 2) + loc_sum += level[w << 1]; + } + 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); + const int loc_sum_abs_pass1 = get_local_sum(rc->abs_level_pass1, tb->tb_width, tb->tb_height, xc, yc); + 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 VVCSH *sh = &lc->sc->sh; + const TransformBlock *tb = rc->tb; + const int w = rc->width_in_sbs; + const int h = rc->height_in_sbs; + int inc; + + if (tb->ts && !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 VVCSH *sh = &lc->sc->sh; + const TransformBlock *tb = rc->tb; + int inc; + + if (tb->ts && !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); + + 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(const ResidualCoding* rc, const int xc, const int yc, const int base_level) +{ + 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; + + loc_sum_abs = get_local_sum(rc->abs_level, tb->tb_width, tb->tb_height, xc, yc); + loc_sum_abs = av_clip_uintp2(loc_sum_abs - base_level * 5, 5); + return rice_params[loc_sum_abs]; +} + +static int abs_decode(VVCLocalContext *lc, const int c_rice_param) +{ + 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, 11, 15); + } + return suffix + (prefix << c_rice_param); +} + +static int abs_remainder_decode(VVCLocalContext *lc, const ResidualCoding* rc, const int xc, const int yc) +{ + const int c_rice_param = abs_get_rice_param(rc, xc, yc, 4); + 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 int c_rice_param = 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 abs_level_decode(VVCLocalContext *lc, const ResidualCoding *rc, const int xc, const int yc) +{ + const int c_rice_param = abs_get_rice_param(rc, xc, yc, 0); + int abs_dec_level = abs_decode(lc, c_rice_param); + const int zero_pos = (rc->qstate < 2 ? 1 : 2) << c_rice_param; + if (abs_dec_level != zero_pos) { + if (abs_dec_level < zero_pos) + abs_dec_level++; + } else { + abs_dec_level = 0; + } + return abs_dec_level; +} + +static void init_residual_coding(ResidualCoding *rc, const int log2_zo_tb_width, const int log2_zo_tb_height, TransformBlock *tb) +{ + 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->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(&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 VVCSH *sh = &lc->sc->sh; + 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 (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]) + *abs_level += abs_remainder_decode(lc, rc, xc, yc) << 1; + } + 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; + + *abs_level = abs_level_decode(lc, rc, xc, yc); + if (*abs_level > 0) { + if (last_sig_scan_pos_sb == -1) + last_sig_scan_pos_sb = n; + first_sig_scan_pos_sb = n; + } + if (sh->dep_quant_used_flag) + rc->qstate = qstate_translate_table[rc->qstate][*abs_level & 1]; + } + sig_hidden_flag = sh->sign_data_hiding_used_flag && + (last_sig_scan_pos_sb - first_sig_scan_pos_sb > 3 ? 1 : 0); + + if (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 (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 (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 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; + } + 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->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->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(&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 VVCSH *sh = &lc->sc->sh; + const int ts = !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..6f56f63852 --- /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 ctb_addr, int ctb_addr_x, int ctb_addr_y); + +//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.h b/libavcodec/vvc/vvc_ctu.h new file mode 100644 index 0000000000..7da9e15b45 --- /dev/null +++ b/libavcodec/vvc/vvc_ctu.h @@ -0,0 +1,387 @@ +/* + * 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 "vvcdec.h" + +enum SAOType { + SAO_NOT_APPLIED = 0, + SAO_BAND, + SAO_EDGE, + SAO_APPLIED +}; + +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]; +} 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_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; + + 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; + +struct CodingUnit; +typedef struct CodingUnit { + AVBufferRef *buf; + + 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[] + + TransformUnit tus[MAX_TUS_IN_CU]; + int num_tus; + + int8_t qp[4]; ///< QpY, Qp′Cb, Qp′Cr, Qp′CbCr + + PredictionUnit pu; + + struct CodingUnit *next; +} CodingUnit; + +struct CTU { + CodingUnit *cus; +}; + +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 + + VVCCabacState cabac_state[VVC_CONTEXTS]; + CABACContext cc; + + VVCTask *parse_task; + int ctu_addr_last; + + uint8_t is_first_qg; + MvField hmvp[MAX_NUM_HMVP_CANDS]; ///< HmvpCandList + int num_hmvp; ///< NumHmvpCand +} EntryPoint; + +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, 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; +} ; + +typedef struct VVCAllowedSplit { + int qt; + int btv; + int bth; + int ttv; + int tth; +} VVCAllowedSplit; + +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: 41763 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:ba91:b0:105:feb:71f2 with SMTP id fb17csp1090803pzb; Sun, 21 May 2023 06:04:44 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5SD48PjLoKcgNRdSii/16R1sne9Yf/04yB3nN55hMtKMjsfnVsUVPV9wpXqaalHpm0LHGU X-Received: by 2002:a17:907:9804:b0:96a:99d2:a680 with SMTP id ji4-20020a170907980400b0096a99d2a680mr7422190ejc.34.1684674283722; Sun, 21 May 2023 06:04:43 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684674283; cv=none; d=google.com; s=arc-20160816; b=GPS19BF1rWAXgNCbupCZkn7AHQ7orDq5EgfvU7CUKwnhESjGYruQMprLxl1qWBQdSa qIOpgBWEDV4zO8FdH1LhwSLvCShVr+qaPeN/UXqkkQJ/H6ZwuZounmIQgxX4atVm2YzS Oq3MPz500i/5hxsHDFBoLbQ8jLWJ74iyjYUKlgg4/lNPG6c1GNXoE1oHiMTITO/8caAJ m0/DNAZtomGxNWwRFl/Mm14RV9FyABDa++FDvJj9PoZtxzGiIGS59oQ21VDExrId09Xk 3Hg+Ma8U2isbGWF2gcLcjkxF4M4aW8TE6yiNViJS9Hbwz3XuydrtwLJlKIJVQtJFWxvW IMaA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=BK70/ALhus7rpJ6lDGrOud6rRUlnbrwRzaVGBZqgrHQ=; b=jToWSi/wwnIggdr46svtWLvBqlMI/CTPansWjFrF2CVeViwcT8RVA9YjrhUc6U6EgF nqh6bMwnRBnaCAaETyDil6Ok26SJVllgWRvkCaeX6Ul7nlPedolQjfUf/3juLj+j8Ve8 doP7Icj5BEGdPQ+XEDwqIpmZLu+++dhc4S8bLx0bKiZp+wKcgeMpmmZcTEnomuWKxpul jPZPhMhn8M2m78kKuKNsOnmcVt3VzQzlbHfG/NtkqzgPWc0+MDE/a25WpLER9rG5Qe91 B13a/6VDW+uKu3U2KFh88U9xLHR0B5MxcDbZMGPca8+YzGRUbNfMP5Fx6Qp/+DBxmtn5 nbJQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20221208 header.b=CXqYTelv; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id y7-20020a170906070700b0096f76f492c2si2059210ejb.324.2023.05.21.06.04.43; Sun, 21 May 2023 06:04:43 -0700 (PDT) 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=@gmail.com header.s=20221208 header.b=CXqYTelv; 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 0CFA068C162; Sun, 21 May 2023 16:03:47 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f176.google.com (mail-pf1-f176.google.com [209.85.210.176]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 04B6A68C121 for ; Sun, 21 May 2023 16:03:39 +0300 (EEST) Received: by mail-pf1-f176.google.com with SMTP id d2e1a72fcca58-64d2a613ec4so2152097b3a.1 for ; Sun, 21 May 2023 06:03:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1684674217; x=1687266217; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=SZ9geK82cQbMXlfX3Xv7EusHydKMtqhHig95usCbB+s=; b=CXqYTelvjBtiiIP4uHbwJyx5VkZGgnWe5yZ2+1/mWCwQGAAgDvUmHd1Dh4wjfkdmXD beAUqMD4/PvqhEYp+GF9l41WZ40NUhdQ5ctE68L2MGPxbPETIwwpvnTUUd0E8uOmgwQJ oq6/Q45q+7yCZR4f53V1GqjYGAuKzQjyHCcE8cmwIQMe7Dv671HNhAr6LEtNRMa5gepj S2fDtddhDaMVo06kEhsNUSSp18Kx13rHgWC8ljYWmJ0lUysUGDjNiL488sDX6XlYR4Dq FVpv1CT9TljPLa+FXHehMe5VDv4d9Gm0FInS3a+B3A8bjea7rQ8q8XvmdkkTZIYXQq4u +Mlw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684674217; x=1687266217; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=SZ9geK82cQbMXlfX3Xv7EusHydKMtqhHig95usCbB+s=; b=JLQyp1P2EQW/wx1mBt0sL+bBem6jBA6vX9PQ9bX6RYLHsV/4pjhpwCjEug1E2S44OR Rx/dq8ZA/TavHjD/aBsYNVAPQlaV/f58fh4mvg3lv6RSOVZNszZ6L+t1QiW1Rpdv56dG bHheaiRpghtsZtPGYixCYAaFHtFOPQdo45t1dGImMe1G3UgD9qWgYKps8GG3LcgosFG3 bai+xGOzUGpFWJRaPiO6zZ358DXszJ2Pz9N5KE2EN+PiHeQ/eTkUoZgxijw0fm+m1wXR KFLS41txbQSVM0HIONHfv87oMu0Jn8danP7hy0FtwtXSCjaRq/jvfzLWsFwJXxJ/wFk3 7f4Q== X-Gm-Message-State: AC+VfDweInEFfeAwgdcdwz+cTcJ+KXYhGBTRFncspBhuZipYcZBBaOby ojC8hETuJFfI2vSHzp2yyo/ReuxvFbn4cw== X-Received: by 2002:a05:6a00:2288:b0:643:aa8d:8cd7 with SMTP id f8-20020a056a00228800b00643aa8d8cd7mr12090643pfe.32.1684674216631; Sun, 21 May 2023 06:03:36 -0700 (PDT) Received: from NuoMi.localdomain ([112.64.8.97]) by smtp.gmail.com with ESMTPSA id t9-20020aa79389000000b0064d45eea573sm1671872pfe.41.2023.05.21.06.03.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 21 May 2023 06:03:36 -0700 (PDT) From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 21 May 2023 21:03:10 +0800 Message-Id: <20230521130319.13813-6-nuomi2021@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230521130319.13813-1-nuomi2021@gmail.com> References: <20230521130319.13813-1-nuomi2021@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 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: iDYyHnvBsSfx --- libavcodec/vvc/Makefile | 3 +- libavcodec/vvc/vvc_refs.c | 498 ++++++++++++++++++++++++++++++++++++++ libavcodec/vvc/vvc_refs.h | 44 ++++ 3 files changed, 544 insertions(+), 1 deletion(-) 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 f70cc7a6f2..14c23a6fdf 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -5,4 +5,5 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ vvc/vvc_executor.o \ vvc/vvc_ps.o \ vvc/vvc_data.o \ - vvc/vvc_cabac.o + vvc/vvc_cabac.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..06fde66f1e --- /dev/null +++ b/libavcodec/vvc/vvc_refs.c @@ -0,0 +1,498 @@ +/* + * 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 "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; + pthread_mutex_t lock; + pthread_cond_t 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) { + ff_thread_release_ext_buffer(fc->avctx, &frame->tf); + + av_buffer_unref(&frame->progress_buf); + + av_buffer_unref(&frame->tab_mvf_buf); + frame->tab_mvf = NULL; + + av_buffer_unref(&frame->rpl_buf); + av_buffer_unref(&frame->rpl_tab_buf); + frame->rpl_tab = NULL; + frame->refPicList = NULL; + + 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(void *opaque, uint8_t *data) +{ + FrameProgress *p = (FrameProgress*)data; + pthread_cond_destroy(&p->cond); + pthread_mutex_destroy(&p->lock); + av_free(data); +} + +static AVBufferRef *alloc_progress(void) +{ + int ret; + AVBufferRef *buf; + FrameProgress *p = av_mallocz(sizeof(FrameProgress)); + + if (!p) + return NULL; + ret = pthread_cond_init(&p->cond, NULL); + if (ret) { + av_free(p); + return NULL; + } + + ret = pthread_mutex_init(&p->lock, NULL); + if (ret) { + pthread_cond_destroy(&p->cond); + av_free(p); + return NULL; + } + buf = av_buffer_create((void*)p, sizeof(*p), free_progress, NULL, 0); + if (!buf) + free_progress(NULL, (void*)p); + return buf; +} + +static VVCFrame *alloc_frame(VVCContext *s, VVCFrameContext *fc) +{ + const VVCPPS *pps = s->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_ext_buffer(fc->avctx, &frame->tf, + AV_GET_BUFFER_FLAG_REF); + if (ret < 0) + return NULL; + + frame->rpl_buf = av_buffer_allocz(fc->pkt.nb_nals * sizeof(RefPicListTab)); + if (!frame->rpl_buf) + goto fail; + + frame->tab_mvf_buf = av_buffer_pool_get(fc->tab_mvf_pool); + if (!frame->tab_mvf_buf) + goto fail; + frame->tab_mvf = (MvField *)frame->tab_mvf_buf->data; + //fixme: remove this + memset(frame->tab_mvf, 0, frame->tab_mvf_buf->size); + + frame->rpl_tab_buf = av_buffer_pool_get(fc->rpl_tab_pool); + if (!frame->rpl_tab_buf) + goto fail; + frame->rpl_tab = (RefPicListTab **)frame->rpl_tab_buf->data; + frame->ctb_count = pps->ctb_width * pps->ctb_height; + for (j = 0; j < frame->ctb_count; j++) + frame->rpl_tab[j] = (RefPicListTab *)frame->rpl_buf->data; + + + frame->progress_buf = alloc_progress(); + if (!frame->progress_buf) + 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->pic_output_flag) + ref->flags = VVC_FRAME_FLAG_OUTPUT; + + if (!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->conf_win.left_offset; + ref->frame->crop_right = fc->ps.pps->conf_win.right_offset; + ref->frame->crop_top = fc->ps.pps->conf_win.top_offset; + ref->frame->crop_bottom = fc->ps.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->dpb.max_dec_pic_buffering[sps->max_sublayers - 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->dpb.max_dec_pic_buffering[sps->max_sublayers - 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_progress(frame, INT_MAX); + + 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, const SliceContext *sc) +{ + VVCFrame *frame = fc->ref; + const VVCSH *sh = &sc->sh; + + if (sc->slice_idx >= frame->rpl_buf->size / sizeof(RefPicListTab)) + 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] = (RefPicListTab *)frame->rpl_buf->data + sc->slice_idx; + } + + frame->refPicList = (RefPicList *)frame->rpl_tab[sh->ctb_addr_in_curr_slice[0]]; + + return 0; +} + +int ff_vvc_slice_rpl(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc) +{ + const VVCSH *sh = &sc->sh; + int i, ret = 0; + + init_slice_rpl(fc, sc); + + for (i = 0; i < 2; i++) { + const VVCRefPicListStruct *rpls = sh->rpls + i; + RefPicList *rpl = fc->ref->refPicList + i; + int poc_base = fc->ps.ph->poc; + rpl->nb_refs = 0; + for (int j = 0; j < rpls->num_ref_entries; j++) { + const VVCRefPicListStructEntry *ref = &rpls->entries[j]; + int poc; + if (!ref->inter_layer_ref_pic_flag) { + int use_msb = 1; + int ref_flag; + if (ref->st_ref_pic_flag) { + poc = poc_base + ref->delta_poc_val_st; + poc_base = poc; + ref_flag = VVC_FRAME_FLAG_SHORT_REF; + } else { + use_msb = ref->lt_msb_flag; + poc = ref->lt_poc; + ref_flag = VVC_FRAME_FLAG_LONG_REF; + } + 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 (sh->collocated_list == i && + sh->collocated_ref_idx < rpl->nb_refs) + fc->ref->collocated_ref = rpl->ref[sh->collocated_ref_idx]; + } +fail: + return ret; +} + +int ff_vvc_frame_rpl(VVCContext *s, VVCFrameContext *fc, const 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_progress(VVCFrame *frame, int n) +{ + FrameProgress *p = (FrameProgress*)frame->progress_buf->data; + + pthread_mutex_lock(&p->lock); + + av_assert0(p->progress < n || p->progress == INT_MAX); + p->progress = n; + + pthread_cond_broadcast(&p->cond); + pthread_mutex_unlock(&p->lock); +} + +void ff_vvc_await_progress(VVCFrame *frame, int n) +{ + FrameProgress *p = (FrameProgress*)frame->progress_buf->data; + + pthread_mutex_lock(&p->lock); + + // +1 for progress default value 0 + while (p->progress < n + 1) + pthread_cond_wait(&p->cond, &p->lock); + + pthread_mutex_unlock(&p->lock); +} diff --git a/libavcodec/vvc/vvc_refs.h b/libavcodec/vvc/vvc_refs.h new file mode 100644 index 0000000000..b4e8866b50 --- /dev/null +++ b/libavcodec/vvc/vvc_refs.h @@ -0,0 +1,44 @@ +/* + * 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" + +#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) + +int ff_vvc_output_frame(VVCContext *s, VVCFrameContext *fc, 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, 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, const SliceContext *sc); +int ff_vvc_slice_rpl(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc); +void ff_vvc_unref_frame(VVCFrameContext *fc, VVCFrame *frame, int flags); +void ff_vvc_clear_refs(VVCFrameContext *fc); + +void ff_vvc_report_progress(VVCFrame *frame, int n); +void ff_vvc_await_progress(VVCFrame *frame, int n); + +#endif // AVCODEC_VVC_REFS_H From patchwork Sun May 21 13:03:11 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 41765 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:ba91:b0:105:feb:71f2 with SMTP id fb17csp1091017pzb; Sun, 21 May 2023 06:05:07 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ4sKFbXELkV68GEu9kphve/GARXqjWvWywpzhopAIl34czVxLcx0KpUN7EjIdh/zTqqJhl6 X-Received: by 2002:a17:907:c1e:b0:96f:d6bd:390c with SMTP id ga30-20020a1709070c1e00b0096fd6bd390cmr904355ejc.67.1684674306872; Sun, 21 May 2023 06:05:06 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684674306; cv=none; d=google.com; s=arc-20160816; b=kRB5dd9ZyXWVqn7Is2oPoNY1knQP7hOvq+o2aheEff/K3GEtR0WdUT+bh480Z6x/GA GN39P4n4qgFPCF5INpp+IiMMp/JsKNQNol4fNUkjlV7soo4a5ZDTAj/H0EFbZB/zviwC pIixZD2WqchKHkzxx846OdbSkUHVipbjNtrcm/44OGJ7OUl+fXB0BH6w8XIVbLilz4v4 hwSWBihzRDcMnk4f0AJ/39VKnCDgs/g97ea7Xfbx/v0eE6kNDfyqKF3wB1DsGLbmsRZt EIdWLqHO3AsDWBTqBd8gQeXh0tf26NJmTTrXWuqbTm5VKTx8uTo3Ya7wU6K6eDXyGGv2 quAA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=HVSv9Z5GMITjIvTCzLm7h0/riJR5sQuJCwfsOnroAH8=; b=YQadPM3Fvo9+fhdsnBkzMbtG70apizzjmcWxLna3cptsrqHVX4VNh4aZdc3YnuzvL+ TN4/pgHrW7eQ8F4do5w/prmJjvy6b4i++XRnt4rm6X96Dg7xTjGC2HN3XUNIoqUCke4m NQinsMhak589yA92o5SJthOe4/FiO5BDLLF8/OEyP21uJMRK6gHJ+R5eNTMm8hThqGYm WwwNBQDpX58tJV++zaDOcIEHDIhUyXkuTbeKR4r6nEgTnp+PaJ3FR7kWzAD8SJXAdn/F OvXJhUJaBKa3s3GMYmr0xRC8ojTk0umHMPM28h9S9hcxiDIjJQ0sn5JwdgpDLdcVlPQT mgGA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20221208 header.b=hca1aMzU; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id ov28-20020a170906fc1c00b0096b1c28511fsi756514ejb.745.2023.05.21.06.05.06; Sun, 21 May 2023 06:05:06 -0700 (PDT) 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=@gmail.com header.s=20221208 header.b=hca1aMzU; 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 C21BB68C173; Sun, 21 May 2023 16:03:48 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f174.google.com (mail-pf1-f174.google.com [209.85.210.174]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id CD8BA68C161 for ; Sun, 21 May 2023 16:03:41 +0300 (EEST) Received: by mail-pf1-f174.google.com with SMTP id d2e1a72fcca58-64d2f99c8c3so1691823b3a.0 for ; Sun, 21 May 2023 06:03:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1684674219; x=1687266219; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ofV59qCEwQgpmzd8ki0Nyhqa/bVTbJtCUDLfEhtQwMY=; b=hca1aMzUsRQX0zG2IO8QGjFy6lZOsqWlwAkCqJDnqUvq07MUnHD30aYFJmb1OiLvFl O7ixyNR+Mb5fFPqw2c0WsYrYCDjVgaZzoSRu3001xSb1sgudN3qwU6EtGkP3ANif62tY B0EFqxHsf/xKLcsTMVEUj827DkanuBim7PYgc84X0quX0psLUDHFjr+o24zvS6As1aSe 2jq+uacm218g6+UJYyM8AUZBcjNTNGHjKvlT4p0WW13F2IU1uX9SrNRVxhiElWMWzlld 2U01glQnnFL9+ma3XeW19GsA2630ZuMgnAHPj2QTU2Uj44qCuNyNd0MMskOk8ULiYHQz tHAg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684674219; x=1687266219; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ofV59qCEwQgpmzd8ki0Nyhqa/bVTbJtCUDLfEhtQwMY=; b=IRl4JDyjhE2KnL79pHBxUP7r9eHhVNbAliF275hAlsNG85xJcOeiX6aZ9ZxCxhAD0i Ilhge/iBqO8KicSo9nyyZtAHbjo8SWv7ubk2fFgwPgDrjTPpuVhEZUe1BevIfn3q1rFZ XsqcKyhZteVPRoLeh8HCG+xWpTaORxlCIjZY/0FcR3Ij32AXdULaLWwfvupVAxY0lRv+ xxK06LD9cI/OTm14qirMiUbcgmHrp4HTd9or+It+rBfG/yV8HKuSDLCAKLiXZkF/XWEg HIoniG8VKJezeLAIuiTXO6kwfluKgeQp+y978Dc2c1sfzM+x/VvrfcSLi6kUb5GaPq0v Mg7w== X-Gm-Message-State: AC+VfDxc0a1Fz57Z6I5ZLlKnjPRaofagd3sLbVlCii/1YgMFnXpvkP3Z 5JmjEDSFj2C5548pS3K7FE1+iRx2vBV2NQ== X-Received: by 2002:a05:6a00:2195:b0:64d:42ca:a4c4 with SMTP id h21-20020a056a00219500b0064d42caa4c4mr6586797pfi.31.1684674218494; Sun, 21 May 2023 06:03:38 -0700 (PDT) Received: from NuoMi.localdomain ([112.64.8.97]) by smtp.gmail.com with ESMTPSA id t9-20020aa79389000000b0064d45eea573sm1671872pfe.41.2023.05.21.06.03.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 21 May 2023 06:03:38 -0700 (PDT) From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 21 May 2023 21:03:11 +0800 Message-Id: <20230521130319.13813-7-nuomi2021@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230521130319.13813-1-nuomi2021@gmail.com> References: <20230521130319.13813-1-nuomi2021@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 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: O53MDflAqb+z --- libavcodec/vvc/Makefile | 4 +- libavcodec/vvc/vvc_ctu.c | 41 + libavcodec/vvc/vvc_ctu.h | 4 + libavcodec/vvc/vvc_mvs.c | 1807 ++++++++++++++++++++++++++++++++++++++ libavcodec/vvc/vvc_mvs.h | 46 + 5 files changed, 1901 insertions(+), 1 deletion(-) create mode 100644 libavcodec/vvc/vvc_ctu.c 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 14c23a6fdf..7c59b99f15 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -6,4 +6,6 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ vvc/vvc_ps.o \ vvc/vvc_data.o \ vvc/vvc_cabac.o \ - vvc/vvc_refs.o + vvc/vvc_refs.o \ + vvc/vvc_mvs.o \ + vvc/vvc_ctu.o diff --git a/libavcodec/vvc/vvc_ctu.c b/libavcodec/vvc/vvc_ctu.c new file mode 100644 index 0000000000..4e6e582718 --- /dev/null +++ b/libavcodec/vvc/vvc_ctu.c @@ -0,0 +1,41 @@ +/* + * 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_cabac.h" +#include "vvc_ctu.h" +#include "vvc_data.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; +} diff --git a/libavcodec/vvc/vvc_ctu.h b/libavcodec/vvc/vvc_ctu.h index 7da9e15b45..02c757559e 100644 --- a/libavcodec/vvc/vvc_ctu.h +++ b/libavcodec/vvc/vvc_ctu.h @@ -23,6 +23,8 @@ #ifndef AVCODEC_VVC_CTU_H #define AVCODEC_VVC_CTU_H +#include "libavcodec/cabac.h" + #include "vvcdec.h" enum SAOType { @@ -384,4 +386,6 @@ struct ALFParams { uint8_t applied[3]; }; +//utils +void ff_vvc_set_neighbour_available(VVCLocalContext *lc, int x0, int y0, int w, int h); #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..39261ad26e --- /dev/null +++ b/libavcodec/vvc/vvc_mvs.c @@ -0,0 +1,1807 @@ +/* + * 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 VVCFrameContext *fc) +{ + int check_diffpicount = 0; + int i, j; + const RefPicList *refPicList = fc->ref->refPicList; + + for (j = 0; j < 2; j++) { + for (i = 0; i < refPicList[j].nb_refs; i++) { + if (refPicList[j].list[i] > 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; + RefPicList* refPicList = fc->ref->refPicList; + + 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(fc) && (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(fc) && (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(fc)) { + if (X == 0) + return CHECK_MVSET(0); + else + return CHECK_MVSET(1); + } else { + if (lc->sc->sh.collocated_list) + 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->temporal_mvp_enabled_flag || (cu->cb_width * cu->cb_height <= 32)) + return 0; + + tab_mvf = ref->tab_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; + ff_vvc_await_progress(ref, y); + 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; + ff_vvc_await_progress(ref, y); + 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->ref->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->ref->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->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->ref->tab_mvf; + const int min_pu_width = fc->ps.pps->min_pu_width; + + if (!n->checked) { + n->checked = 1; + n->available = !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->ref->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->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 = (lc->sc->sh.slice_type == VVC_SLICE_TYPE_B) ? + 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 VVCSH *sh = &lc->sc->sh; + const int nb_refs = (sh->slice_type == VVC_SLICE_TYPE_P) ? + sh->rpls[0].num_ref_entries : FFMIN(sh->rpls[0].num_ref_entries, sh->rpls[1].num_ref_entries); + int zero_idx = 0; + + while (num_cands < sps->max_num_merge_cand) { + MvField *cand = cand_list + num_cands; + + cand->pred_flag = PF_L0 + ((sh->slice_type == VVC_SLICE_TYPE_B) << 1); + AV_ZERO64(cand->mv + 0); + AV_ZERO64(cand->mv + 1); + cand->ref_idx[0] = zero_idx < nb_refs ? zero_idx : 0; + cand->ref_idx[1] = zero_idx < nb_refs ? 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, lc->sc->sh.slice_type == VVC_SLICE_TYPE_B)) { + 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->ref->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->ref->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->ref->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_mvf; + int colPic = ref->poc; + int X = 0; + + sb_clip_location(fc, x_ctb, y_ctb, temp_mv, &x, &y); + + ff_vvc_await_progress(ref, y); + + temp_col = TAB_MVF(x, y); + mvLXCol = mv + 0; + *pred_flag = DERIVE_TEMPORAL_COLOCATED_MVS(1); + if (sh->slice_type == VVC_SLICE_TYPE_B) { + 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 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 == fc->ref->refPicList[0].list[a1->ref_idx[0]]) + *temp_mv = a1->mv[0]; + else if ((a1->pred_flag & PF_L1) && colPic == fc->ref->refPicList[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->temporal_mvp_enabled_flag || + !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 + ((lc->sc->sh.slice_type == VVC_SLICE_TYPE_B) << 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->six_param_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->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.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->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 VVCFrameContext *fc, + const int x_cand, const int y_cand, const int lx, const int8_t *ref_idx, + Mv *mv) +{ + + const int min_pu_width = fc->ps.pps->min_pu_width; + const MvField* tab_mvf = fc->ref->tab_mvf; + const MvField *mvf = &TAB_MVF(x_cand, y_cand); + const RefPicList* refPicList = fc->ref->refPicList; + const PredFlag maskx = lx + 1; + const int poc = refPicList[lx].list[ref_idx[lx]]; + int available = 0; + + if ((mvf->pred_flag & maskx) && refPicList[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) && refPicList[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->ref->tab_mvf; + const MvField *mvf = &TAB_MVF(x_nb, y_nb); + RefPicList* refPicList = fc->ref->refPicList; + const PredFlag maskx = lx + 1; + const int poc = refPicList[lx].list[ref_idx[lx]]; + + if ((mvf->pred_flag & maskx) && refPicList[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) && refPicList[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 VVCFrameContext *fc = lc->fc; + 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(fc, 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 VVCFrameContext *fc = lc->fc; + const EntryPoint* ep = lc->ep; + const RefPicList* refPicList = fc->ref->refPicList; + const int poc = refPicList[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 == refPicList[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 VVCFrameContext *fc = ctx->lc->fc; + const MvField *tab_mvf = fc->ref->tab_mvf; + const int min_pu_width = fc->ps.pps->min_pu_width; + const RefPicList* refPicList = fc->ref->refPicList; + 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 = refPicList[lx].list[ref_idx]; + if ((mvf->pred_flag & maskx) && refPicList[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) && refPicList[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->ref->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->ref->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..4742a18c75 --- /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 VVCFrameContext *fc); +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 May 21 13:03:12 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 41767 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:ba91:b0:105:feb:71f2 with SMTP id fb17csp1091328pzb; Sun, 21 May 2023 06:05:31 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ7fEv3D9H3CkjwWcM93egEEYiYs5ECaPUJakRWx3o63TWNzuPCeaV5HVE7I9HKZUnhWInTA X-Received: by 2002:a17:907:7288:b0:957:28b2:560a with SMTP id dt8-20020a170907728800b0095728b2560amr6958431ejc.46.1684674331065; Sun, 21 May 2023 06:05:31 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684674331; cv=none; d=google.com; s=arc-20160816; b=JHUOVSDS12ypqw1blbsJwBPDgJ08CtJjRBAyW4Suhuy29SQ7OnDnAbtG4myaaxOw2Y 15UtZtuXwuh3+cChVxdytUqAhMS5GUgdfTb45p2FCAAZE+Yg9YJr86ZriqDFOOqwVxSB KfA+qKy/yBc6lhB61KwpJTAZXw9aKhwiauNuIxbWL+L/DuiZvGP/Kv9IQI0vZBcdYFb3 CXF5ZNk2TjjYGga2jpL0iqN8ZXVOhP3tPuaaqHNHOlgjziTMeCqKGZExjQ70S2ShIfOJ CvtwAYROlWVr7f5dqTGA1zZp1DJkiWy1p1ZzIWvorUHqsaJPZOyjHFiTwdGqo0vukHEl xgcw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=eXn8HLPX3zZRfoSTvCDPSsRV1qfHWGBVrXO8PqCI/Ls=; b=g26XRh+vNK/Dn+5YHAbzKnvclXMAHBKH0jSiqykPLweWyf3MxKMPVce8BOJIcnGisr Y2dQ4WMzIEFDt1KwTIUn5bGUYU+YOMSenTW1Z19TB8WRk+2VQQqN3O1wIycVgYWtTg/7 /VNAYHFeSuEBKQ2NiGz/x+BwO2c5XLKEEQ9R5cd5M4LLK5jgoEeURtRyooIgV9PSV6DS TQaeSg9y+R+NsJF5qWPQ8LeGxldhxRmHNKpFs1Vm31p2wMY992y/Q+fggu5XhBm4G/hS OyB2vOw7KtHPgbnGbTJC2fx3Lt/ciqzm0KuW/m8kYwWtHy4lMuTYNl5fZnUL3P4jeqBy h/jg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20221208 header.b="fh24/WEu"; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id l5-20020a170907914500b009603d347cb1si2592613ejs.882.2023.05.21.06.05.30; Sun, 21 May 2023 06:05:31 -0700 (PDT) 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=@gmail.com header.s=20221208 header.b="fh24/WEu"; 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 D17C168C174; Sun, 21 May 2023 16:03:52 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f177.google.com (mail-pf1-f177.google.com [209.85.210.177]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5296068C16A for ; Sun, 21 May 2023 16:03:44 +0300 (EEST) Received: by mail-pf1-f177.google.com with SMTP id d2e1a72fcca58-64d15660784so2568229b3a.0 for ; Sun, 21 May 2023 06:03:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1684674222; x=1687266222; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=NJjDXzbzLKfXX1+wkNkFjwKyDMxrMu+2B9UgEba4q5U=; b=fh24/WEuZhZF3XH2xIQ6H1km/JwDgnzz6DhIqYHPVjdWrpLe538uYpvxczf+IBSEy1 EdBBsarNR3P7hH3z0jKV2Y008U3y4iibT1Vpqg39QofHmn5Fj8+VpIRdfmy4z5qsoBZk Vwnrr2YHIfWjUUeoqVyak0uSKpLS+2US9LKTZYFu/hoYJg6ZYzPXGzHKZZXr2S6MnjaN FYdQFrIdaffPfUYTUIflV/2JGvzoQ3UxgkAqTK4Sjs8EVb4wvisMK+mHWRORjJ5PAMZZ NHPjB8dWaLQOkRf8PoXEuTxJQTNcvVebBt3JPdlBEcVvAHaQ3RCqvU0b2FsuEzchO/yW Odhg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684674222; x=1687266222; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=NJjDXzbzLKfXX1+wkNkFjwKyDMxrMu+2B9UgEba4q5U=; b=dKES1tsWaGeS5DUIybnYVt6MgMRVuxRqMOPaXGykPWyxIa/KEGahFzzGw/vnguFN74 sWOPi6K8oKfo4Rwg0NJoYfjb3BcdEWCvNLV/839o6nefGIX/h2FNMUk4kCI4Qtl6mAhu nEbkGTJKmxTWW68Q0RNBhFqi8qXDxop/OKKGam5Ql5SowR721j/0A8lnbht+pO1V7cke YoHfHPWHCKnJcf4WqxoIn/vd+JsakwZlGlV0WbVa2bm5++Nl0Y9VRYdkdKTFCCgbgX2X a5BYNa/1BCS3US0dj+VzD/+Yms1ttdcMu5oF3CnD84g/f2HNMPESVU52YFwZ26pZxPEA sgTg== X-Gm-Message-State: AC+VfDxYx3qebvUPJHW2qUSkltXwpV2OprFZKuIMpIoR5wTs+Oz962cH +cI4OuKyQOquH738o/qi3qBsVaOSUfmWyQ== X-Received: by 2002:a05:6a00:c96:b0:64a:ff32:7347 with SMTP id a22-20020a056a000c9600b0064aff327347mr10737077pfv.13.1684674220715; Sun, 21 May 2023 06:03:40 -0700 (PDT) Received: from NuoMi.localdomain ([112.64.8.97]) by smtp.gmail.com with ESMTPSA id t9-20020aa79389000000b0064d45eea573sm1671872pfe.41.2023.05.21.06.03.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 21 May 2023 06:03:40 -0700 (PDT) From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 21 May 2023 21:03:12 +0800 Message-Id: <20230521130319.13813-8-nuomi2021@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230521130319.13813-1-nuomi2021@gmail.com> References: <20230521130319.13813-1-nuomi2021@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 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: S5BaA7KPeeHV --- libavcodec/vvc/Makefile | 3 +- libavcodec/vvc/vvc_inter.c | 1051 +++++++++++++++++++ libavcodec/vvc/vvc_inter.h | 50 + libavcodec/vvc/vvc_inter_template.c | 1510 +++++++++++++++++++++++++++ libavcodec/vvc/vvcdec.h | 2 + libavcodec/vvc/vvcdsp.h | 183 ++++ 6 files changed, 2798 insertions(+), 1 deletion(-) 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 7c59b99f15..f60a85ac52 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -8,4 +8,5 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ vvc/vvc_cabac.o \ vvc/vvc_refs.o \ vvc/vvc_mvs.o \ - vvc/vvc_ctu.o + vvc/vvc_ctu.o \ + vvc/vvc_inter.o diff --git a/libavcodec/vvc/vvc_inter.c b/libavcodec/vvc/vvc_inter.c new file mode 100644 index 0000000000..7fe1fd7ef4 --- /dev/null +++ b/libavcodec/vvc/vvc_inter.c @@ -0,0 +1,1051 @@ +/* + * 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 "vvc_data.h" +#include "vvc_inter.h" +#include "vvc_mvs.h" +#include "vvc_refs.h" + +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, 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) && pps->weighted_pred_flag) || + (IS_B(sh) && pps->weighted_bipred_flag); + if (weight_flag) { + const int lx = mvf->pred_flag - PF_L0; + const PredWeightTable *w = 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) && pps->weighted_pred_flag) || + (IS_B(sh) && fc->ps.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->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 mx = mv->x & 0xf; + const int my = mv->y & 0xf; + + x_off += mv->x >> 4; + y_off += mv->y >> 4; + src += y_off * src_stride + (x_off * (1 << fc->ps.sps->pixel_shift)); + + EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off); + + fc->vvcdsp.inter.put[LUMA][!!my][!!mx](dst, src, src_stride, block_h, mx, my, block_w, 0, 0); +} + +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 intptr_t mx = av_mod_uintp2(mv->x, 4 + hs); + const intptr_t my = av_mod_uintp2(mv->y, 4 + vs); + const intptr_t _mx = mx << (1 - hs); + const intptr_t _my = my << (1 - vs); + + x_off += mv->x >> (4 + hs); + y_off += mv->y >> (4 + vs); + src += y_off * src_stride + (x_off * (1 << fc->ps.sps->pixel_shift)); + + EMULATED_EDGE_CHROMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off); + fc->vvcdsp.inter.put[CHROMA][!!my][!!mx](dst, src, src_stride, block_h, _mx, _my, block_w, 0, 0); +} + +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 mx = mv->x & 0xf; + const int my = mv->y & 0xf; + int denom, wx, ox; + + x_off += mv->x >> 4; + y_off += mv->y >> 4; + src += y_off * src_stride + (x_off * (1 << 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][!!my][!!mx](dst, dst_stride, src, src_stride, + block_h, denom, wx, ox, mx, my, block_w, hf_idx, vf_idx); + } else { + fc->vvcdsp.inter.put_uni[LUMA][!!my][!!mx](dst, dst_stride, src, src_stride, + block_h, mx, my, block_w, hf_idx, vf_idx); + } +} + +static void luma_bdof(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const uint8_t *_src0, const ptrdiff_t src0_stride, const int mx0, const int my0, + const uint8_t *_src1, const ptrdiff_t src1_stride, const int mx1, const int my1, + const int block_w, const int block_h, const int hf_idx, const int vf_idx) +{ + const VVCFrameContext *fc = lc->fc; + int16_t *tmp0 = lc->tmp + 1 + MAX_PB_SIZE; + int16_t *tmp1 = lc->tmp1 + 1 + MAX_PB_SIZE; + + fc->vvcdsp.inter.put[LUMA][!!my0][!!mx0](tmp0, _src0, src0_stride, + block_h, mx0, my0, block_w, hf_idx, vf_idx); + fc->vvcdsp.inter.bdof_fetch_samples(tmp0, _src0, src0_stride, mx0, my0, block_w, block_h); + + fc->vvcdsp.inter.put[LUMA][!!my1][!!mx1](tmp1, _src1, src1_stride, + block_h, mx1, my1, block_w, hf_idx, vf_idx); + fc->vvcdsp.inter.bdof_fetch_samples(tmp1, _src1, src1_stride, mx1, my1, block_w, block_h); + fc->vvcdsp.inter.apply_bdof(dst, dst_stride, tmp0, tmp1, block_w, block_h); +} + + 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 dmvr_flag, const int sb_bdof_flag) +{ + const VVCFrameContext *fc = lc->fc; + ptrdiff_t src0_stride = ref0->linesize[0]; + ptrdiff_t src1_stride = ref1->linesize[0]; + const int mx0 = mv0->x & 0xf; + const int my0 = mv0->y & 0xf; + const int mx1 = mv1->x & 0xf; + const int my1 = mv1->y & 0xf; + + const int x_off0 = x_off + (mv0->x >> 4); + const int y_off0 = y_off + (mv0->y >> 4); + const int x_off1 = x_off + (mv1->x >> 4); + const int y_off1 = y_off + (mv1->y >> 4); + + const uint8_t *src0 = ref0->data[0] + y_off0 * src0_stride + (int)((unsigned)x_off0 << fc->ps.sps->pixel_shift); + const uint8_t *src1 = ref1->data[0] + y_off1 * src1_stride + (int)((unsigned)x_off1 << fc->ps.sps->pixel_shift); + + if (dmvr_flag) { + const int x_sb0 = x_off + (orig_mv->mv[L0].x >> 4); + const int y_sb0 = y_off + (orig_mv->mv[L0].y >> 4); + const int x_sb1 = x_off + (orig_mv->mv[L1].x >> 4); + const int y_sb1 = y_off + (orig_mv->mv[L1].y >> 4); + EMULATED_EDGE_DMVR_LUMA(lc->edge_emu_buffer, &src0, &src0_stride, x_sb0, y_sb0, x_off0, y_off0); + EMULATED_EDGE_DMVR_LUMA(lc->edge_emu_buffer2, &src1, &src1_stride, x_sb1, y_sb1, x_off1, y_off1); + } else { + EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src0, &src0_stride, x_off0, y_off0); + EMULATED_EDGE_LUMA(lc->edge_emu_buffer2, &src1, &src1_stride, x_off1, y_off1); + } + if (sb_bdof_flag) { + luma_bdof(lc, dst, dst_stride, src0, src0_stride, mx0, my0, src1, src1_stride, mx1, my1, + block_w, block_h, hf_idx, vf_idx); + } else { + int denom, w0, w1, o0, o1; + fc->vvcdsp.inter.put[LUMA][!!my0][!!mx0](lc->tmp, src0, src0_stride, + block_h, mx0, my0, block_w, hf_idx, vf_idx); + if (derive_weight(&denom, &w0, &w1, &o0, &o1, lc, mvf, LUMA, dmvr_flag)) { + fc->vvcdsp.inter.put_bi_w[LUMA][!!my1][!!mx1](dst, dst_stride, src1, src1_stride, lc->tmp, + block_h, denom, w0, w1, o0, o1, mx1, my1, block_w, hf_idx, vf_idx); + } else { + fc->vvcdsp.inter.put_bi[LUMA][!!my1][!!mx1](dst, dst_stride, src1, src1_stride, lc->tmp, + block_h, mx1, my1, block_w, hf_idx, vf_idx); + } + } +} + +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 Mv *mv = &mvf->mv[lx]; + const intptr_t mx = av_mod_uintp2(mv->x, 4 + hs); + const intptr_t my = av_mod_uintp2(mv->y, 4 + vs); + const intptr_t _mx = mx << (1 - hs); + const intptr_t _my = my << (1 - vs); + int denom, wx, ox; + + x_off += mv->x >> (4 + hs); + y_off += mv->y >> (4 + vs); + src += y_off * src_stride + (x_off * (1 << 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][!!my][!!mx](dst, dst_stride, src, src_stride, + block_h, denom, wx, ox, _mx, _my, block_w, hf_idx, vf_idx); + } else { + fc->vvcdsp.inter.put_uni[CHROMA][!!my][!!mx](dst, dst_stride, src, src_stride, + block_h, _mx, _my, block_w, hf_idx, vf_idx); + } +} + +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 uint8_t *src0 = ref0->data[c_idx]; + const uint8_t *src1 = ref1->data[c_idx]; + ptrdiff_t src0_stride = ref0->linesize[c_idx]; + ptrdiff_t src1_stride = ref1->linesize[c_idx]; + const Mv *mv0 = &mvf->mv[0]; + const Mv *mv1 = &mvf->mv[1]; + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + + const intptr_t mx0 = av_mod_uintp2(mv0->x, 4 + hs); + const intptr_t my0 = av_mod_uintp2(mv0->y, 4 + vs); + const intptr_t mx1 = av_mod_uintp2(mv1->x, 4 + hs); + const intptr_t my1 = av_mod_uintp2(mv1->y, 4 + vs); + const intptr_t _mx0 = mx0 << (1 - hs); + const intptr_t _my0 = my0 << (1 - vs); + const intptr_t _mx1 = mx1 << (1 - hs); + const intptr_t _my1 = my1 << (1 - vs); + + const int x_off0 = x_off + (mv0->x >> (4 + hs)); + const int y_off0 = y_off + (mv0->y >> (4 + vs)); + const int x_off1 = x_off + (mv1->x >> (4 + hs)); + const int y_off1 = y_off + (mv1->y >> (4 + vs)); + int denom, w0, w1, o0, o1; + + src0 += y_off0 * src0_stride + (int)((unsigned)x_off0 << fc->ps.sps->pixel_shift); + src1 += y_off1 * src1_stride + (int)((unsigned)x_off1 << fc->ps.sps->pixel_shift); + + if (dmvr_flag) { + const int x_sb0 = x_off + (orig_mv->mv[L0].x >> (4 + hs)); + const int y_sb0 = y_off + (orig_mv->mv[L0].y >> (4 + vs)); + const int x_sb1 = x_off + (orig_mv->mv[L1].x >> (4 + hs)); + const int y_sb1 = y_off + (orig_mv->mv[L1].y >> (4 + vs)); + EMULATED_EDGE_DMVR_CHROMA(lc->edge_emu_buffer, &src0, &src0_stride, x_sb0, y_sb0, x_off0, y_off0); + EMULATED_EDGE_DMVR_CHROMA(lc->edge_emu_buffer2, &src1, &src1_stride, x_sb1, y_sb1, x_off1, y_off1); + } else { + EMULATED_EDGE_CHROMA(lc->edge_emu_buffer, &src0, &src0_stride, x_off0, y_off0); + EMULATED_EDGE_CHROMA(lc->edge_emu_buffer2, &src1, &src1_stride, x_off1, y_off1); + } + + fc->vvcdsp.inter.put[CHROMA][!!my0][!!mx0](lc->tmp, src0, src0_stride, + block_h, _mx0, _my0, block_w, hf_idx, vf_idx); + if (derive_weight(&denom, &w0, &w1, &o0, &o1, lc, mvf, c_idx, dmvr_flag)) { + fc->vvcdsp.inter.put_bi_w[CHROMA][!!my1][!!mx1](dst, dst_stride, src1, src1_stride, lc->tmp, + block_h, denom, w0, w1, o0, o1, _mx1, _my1, block_w, hf_idx, vf_idx); + } else { + fc->vvcdsp.inter.put_bi[CHROMA][!!my1][!!mx1](dst, dst_stride, src1, src1_stride, lc->tmp, + block_h, _mx1, _my1, block_w, hf_idx, vf_idx); + } +} + +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 + 1 + MAX_PB_SIZE; + 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; + 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 * (1 << 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][!!my][!!mx](prof_tmp, src, src_stride, AFFINE_MIN_BLOCK_SIZE, mx, my, AFFINE_MIN_BLOCK_SIZE, 2, 2); + 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][!!my][!!mx](dst, dst_stride, src, src_stride, block_h, mx, my, block_w, 2, 2); + else + fc->vvcdsp.inter.put_uni_w[LUMA][!!my][!!mx](dst, dst_stride, src, src_stride, block_h, denom, wx, ox, mx, my, block_w, 2, 2); + } +} + +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; + ptrdiff_t src0_stride = ref0->linesize[0]; + ptrdiff_t src1_stride = ref1->linesize[0]; + uint16_t *prof_tmp = lc->tmp1 + 1 + MAX_PB_SIZE; + const Mv *mv0 = mvf->mv + L0; + const Mv *mv1 = mvf->mv + L1; + const int mx0 = mv0->x & 0xf; + const int my0 = mv0->y & 0xf; + const int mx1 = mv1->x & 0xf; + const int my1 = mv1->y & 0xf; + const int x_off0 = x_off + (mv0->x >> 4); + const int y_off0 = y_off + (mv0->y >> 4); + const int x_off1 = x_off + (mv1->x >> 4); + const int y_off1 = y_off + (mv1->y >> 4); + + const uint8_t *src0 = ref0->data[0] + y_off0 * src0_stride + (int)((unsigned)x_off0 << fc->ps.sps->pixel_shift); + const uint8_t *src1 = ref1->data[0] + y_off1 * src1_stride + (int)((unsigned)x_off1 << fc->ps.sps->pixel_shift); + + int denom, w0, w1, o0, o1; + const int weight_flag = derive_weight(&denom, &w0, &w1, &o0, &o1, lc, mvf, LUMA, 0); + + EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src0, &src0_stride, x_off0, y_off0); + EMULATED_EDGE_LUMA(lc->edge_emu_buffer2, &src1, &src1_stride, x_off1, y_off1); + + if (!pu->cb_prof_flag[L0]) { + fc->vvcdsp.inter.put[LUMA][!!my0][!!mx0](lc->tmp, src0, src0_stride, + block_h, mx0, my0, block_w, 2, 2); + } else { + fc->vvcdsp.inter.put[LUMA][!!my0][!!mx0](prof_tmp, src0, src0_stride, AFFINE_MIN_BLOCK_SIZE, mx0, my0, AFFINE_MIN_BLOCK_SIZE, 2, 2); + fc->vvcdsp.inter.fetch_samples(prof_tmp, src0, src0_stride, mx0, my0); + fc->vvcdsp.inter.apply_prof(lc->tmp, prof_tmp, pu->diff_mv_x[L0], pu->diff_mv_y[L0]); + } + if (!pu->cb_prof_flag[L1]) { + if (weight_flag) { + fc->vvcdsp.inter.put_bi_w[LUMA][!!my1][!!mx1](dst, dst_stride, src1, src1_stride, lc->tmp, + block_h, denom, w0, w1, o0, o1, mx1, my1, block_w, 2, 2); + } else { + fc->vvcdsp.inter.put_bi[LUMA][!!my1][!!mx1](dst, dst_stride, src1, src1_stride, lc->tmp, + block_h, mx1, my1, block_w, 2, 2); + } + } else { + fc->vvcdsp.inter.put[LUMA][!!my1][!!mx1](prof_tmp, src1, src1_stride, AFFINE_MIN_BLOCK_SIZE, mx1, my1, AFFINE_MIN_BLOCK_SIZE, 2, 2); + fc->vvcdsp.inter.fetch_samples(prof_tmp, src1, src1_stride, mx1, my1); + if (weight_flag) { + fc->vvcdsp.inter.apply_prof_bi_w(dst, dst_stride, lc->tmp, prof_tmp, pu->diff_mv_x[L1], pu->diff_mv_y[L1], + denom, w0, w1, o0, o1); + } else { + fc->vvcdsp.inter.apply_prof_bi(dst, dst_stride, lc->tmp, prof_tmp, pu->diff_mv_x[L1], pu->diff_mv_y[L1]); + } + } + +} + +static void vvc_await_progress(const VVCFrameContext *fc, VVCFrame *ref, + const Mv *mv, const int y0, const int height) +{ + //todo: check why we need magic number 9 + const int y = FFMAX(0, (mv->y >> 4) + y0 + height + 9); + + ff_vvc_await_progress(ref, y); +} + +static int pred_await_progress(const VVCFrameContext *fc, VVCFrame *ref[2], + const MvField *mv, const int y0, const int height) +{ + for (int mask = PF_L0; mask <= PF_L1; mask++) { + if (mv->pred_flag & mask) { + const int lx = mask - PF_L0; + ref[lx] = fc->ref->refPicList[lx].ref[mv->ref_idx[lx]]; + if (!ref[lx]) + return AVERROR_INVALIDDATA; + vvc_await_progress(fc, ref[lx], mv->mv + lx, y0, height); + } + } + 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->chroma_format_idc ? 3 : 1; + + int16_t *tmp[2] = {lc->tmp, lc->tmp1}; + const ptrdiff_t tmp_stride = MAX_PB_SIZE; + + 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 = fc->ref->refPicList[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 { + vvc_await_progress(fc, ref, mv->mv + lx, y, height); + 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], tmp_stride, 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->ref->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->ref->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 dmvr_flag, 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_await_progress(fc, ref, mv, y0, sbh) < 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, dmvr_flag, 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.lmcs_used_flag) + fc->vvcdsp.lmcs.filter(inter, inter_stride, sbw, sbh, fc->ps.ph->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; + if (mv->pred_flag != PF_BI) { + const int lx = mv->pred_flag - PF_L0; + VVCFrame* ref = fc->ref->refPicList[lx].ref[mv->ref_idx[lx]]; + if (!ref) + return; + chroma_mc_uni(lc, inter1, inter1_stride, ref->frame->data[1], ref->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->frame->data[2], ref->frame->linesize[2], + x0_c, y0_c, w_c, h_c, mv, CR, hf_idx, vf_idx); + } else { + VVCFrame* ref0 = fc->ref->refPicList[0].ref[mv->ref_idx[0]]; + VVCFrame* ref1 = fc->ref->refPicList[1].ref[mv->ref_idx[1]]; + if (!ref0 || !ref1) + return; + chroma_mc_bi(lc, inter1, inter1_stride, ref0->frame, ref1->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, ref0->frame, ref1->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); + + } +} + +// 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(VVCLocalContext *lc, int *dmvr_flag, int *bdof_flag, const 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 = fc->ref->refPicList + L0; + const RefPicList *rpl1 = fc->ref->refPicList + L1; + const int8_t *ref_idx = pu->mi.ref_idx; + const MotionInfo *mi = &pu->mi; + const CodingUnit *cu = lc->cu; + const PredWeightTable *w = pps->wp_info_in_ph_flag ? &fc->ps.ph->pwt : &sh->pwt; + + *dmvr_flag = 0; + *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->bdof_disabled_flag && + mi->motion_model_idc == MOTION_TRANSLATION && + !pu->merge_subblock_flag && + !pu->sym_mvd_flag) + *bdof_flag = 1; + if (!ph->dmvr_disabled_flag && + pu->general_merge_flag && + !pu->mmvd_merge_flag) + *dmvr_flag = 1; + + } +} + +void ff_vvc_ctu_apply_dmvr_info(VVCFrameContext *fc, const int x0, const int y0) +{ + const VVCPPS *pps = fc->ps.pps; + const int ctb_size = fc->ps.sps->ctb_size_y; + const int x_end = FFMIN(x0 + ctb_size, pps->width); + const int y_end = FFMIN(y0 + ctb_size, pps->height); + + for (int y = y0; y < y_end; y += MIN_PU_SIZE) { + for (int x = x0; x < x_end; x += MIN_PU_SIZE) { + const int off = pps->min_pu_width * (y >> MIN_PU_LOG2) + (x >> MIN_PU_LOG2); + const DMVRInfo *di = &fc->tab.dmvr[off]; + if (di->dmvr_enabled) { + MvField *mvf = &fc->ref->tab_mvf[off]; + if (mvf->pred_flag & PF_L0) + mvf->mv[L0] = di->mv[L0]; + if (mvf->pred_flag & PF_L1) + mvf->mv[L1] = di->mv[L1]; + } + } + } +} + +// 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 *mv, 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; + ptrdiff_t src0_stride = ref0->linesize[0]; + ptrdiff_t src1_stride = ref1->linesize[0]; + Mv *mv0 = mv->mv + L0; + Mv *mv1 = mv->mv + L1; + const int sr_range = 2; + const int mx0 = mv0->x & 0xf; + const int my0 = mv0->y & 0xf; + const int mx1 = mv1->x & 0xf; + const int my1 = mv1->y & 0xf; + const int x_off0 = x_off + (mv0->x >> 4) - sr_range; + const int y_off0 = y_off + (mv0->y >> 4) - sr_range; + const int x_off1 = x_off + (mv1->x >> 4) - sr_range; + const int y_off1 = y_off + (mv1->y >> 4) - sr_range; + const int pred_w = block_w + 2 * sr_range; + const int pred_h = block_h + 2 * sr_range; + + uint8_t *src0 = ref0->data[0] + y_off0 * src0_stride + (int)((unsigned)x_off0 << fc->ps.sps->pixel_shift); + uint8_t *src1 = ref1->data[0] + y_off1 * src1_stride + (int)((unsigned)x_off1 << fc->ps.sps->pixel_shift); + + int sad[SAD_ARRAY_SIZE][SAD_ARRAY_SIZE]; + int min_dx, min_dy, min_sad, dx, dy; + + *orig_mv = *mv; + min_dx = min_dy = dx = dy = 2; + + EMULATED_EDGE_BILINEAR(lc->edge_emu_buffer, &src0, &src0_stride, x_off0, y_off0); + EMULATED_EDGE_BILINEAR(lc->edge_emu_buffer2, &src1, &src1_stride, x_off1, y_off1); + fc->vvcdsp.inter.dmvr[!!my0][!!mx0](lc->tmp, src0, src0_stride, pred_h, mx0, my0, pred_w); + fc->vvcdsp.inter.dmvr[!!my1][!!mx1](lc->tmp1, src1, src1_stride, pred_h, mx1, my1, pred_w); + + min_sad = fc->vvcdsp.inter.sad(lc->tmp, lc->tmp1, 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); + } + mv0->x += dmv[0]; + mv0->y += dmv[1]; + mv1->x += -dmv[0]; + mv1->y += -dmv[1]; + ff_vvc_clip_mv(mv0); + ff_vvc_clip_mv(mv1); + } + 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) { + DMVRInfo *di = &fc->tab.dmvr[pps->min_pu_width * (y >> MIN_PU_LOG2) + (x >> MIN_PU_LOG2)]; + di->dmvr_enabled = 1; + if (mvf->pred_flag & PF_L0) + di->mv[L0] = mvf->mv[L0]; + if (mvf->pred_flag & PF_L1) + di->mv[L1] = mvf->mv[L1]; + } + } +} + +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, const int dmvr_flag, const int bdof_flag) +{ + VVCFrameContext *fc = lc->fc; + + *orig_mv = *mv = *ff_vvc_get_mvf(fc, x0, y0); + if (bdof_flag) + *sb_bdof_flag = 1; + if (dmvr_flag) { + VVCFrame* ref[2]; + if (pred_await_progress(fc, ref, mv, y0, sbh) < 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; + const PredictionUnit *pu = &cu->pu; + const MotionInfo *mi = &pu->mi; + MvField mv, orig_mv; + int sbw, sbh, num_sb_x, num_sb_y, sb_bdof_flag = 0; + int dmvr_flag, bdof_flag; + + if (cu->ciip_flag && skip_ciip) + return; + + derive_dmvr_bdof_flag(lc, &dmvr_flag, &bdof_flag, pu); + num_sb_x = mi->num_sb_x; + num_sb_y = mi->num_sb_y; + if (dmvr_flag || bdof_flag) { + num_sb_x = (cu->cb_width > 16) ? (cu->cb_width >> 4) : 1; + num_sb_y = (cu->cb_height > 16) ? (cu->cb_height >> 4) : 1; + } + sbw = cu->cb_width / num_sb_x; + sbh = cu->cb_height / num_sb_y; + + for (int sby = 0; sby < num_sb_y; sby++) { + for (int sbx = 0; sbx < 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, dmvr_flag, bdof_flag); + pred_regular_luma(lc, mi->hpel_if_idx, mi->hpel_if_idx, &mv, x0, y0, sbw, sbh, &orig_mv, dmvr_flag, sb_bdof_flag); + if (fc->ps.sps->chroma_format_idc) + pred_regular_chroma(lc, &mv, x0, y0, sbw, sbh, &orig_mv, 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_await_progress(fc, ref, mv, y, sbh) < 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->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 (lc->sc->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.ph->lmcs_fwd_lut); + } +} + +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 (cu->pred_mode != MODE_INTRA && cu->pred_mode != MODE_PLT && cu->tree_type != DUAL_TREE_CHROMA) + 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..03422fbcfe --- /dev/null +++ b/libavcodec/vvc/vvc_inter.h @@ -0,0 +1,50 @@ +/* + * 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); + +/** + * apply DMVR(Decoder-Side Motion Vector Refinement) 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_ctu_apply_dmvr_info(VVCFrameContext *fc, int x0, const int y0); + +#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..6c4b353b1c --- /dev/null +++ b/libavcodec/vvc/vvc_inter_template.c @@ -0,0 +1,1510 @@ +/* + * 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_vvc_pel_pixels)(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 int hf_idx, const int vf_idx) +{ + 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_vvc_pel_uni_pixels)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int height, + const intptr_t mx, const intptr_t my, const int width, const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + 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_vvc_pel_bi_pixels)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + + const int shift = 14 + 1 - 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(((src[x] << (14 - BIT_DEPTH)) + src0[x] + offset) >> shift); + src += src_stride; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_pel_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 intptr_t mx, const intptr_t my, + const int width, const int hf_idx, const int vf_idx) +{ + int x, y; + pixel *src = (pixel *)_src; + ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + int offset = 1 << (shift - 1); +#else + int offset = 0; +#endif + const int ox = _ox * (1 << (BIT_DEPTH - 8)); + + for (y = 0; y < height; y++) { + for (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; + } +} + +static void FUNC(put_vvc_pel_bi_w_pixels)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const int denom, const int wx0, const int wx1, + int ox0, int ox1, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + + const int shift = 14 + 1 - BIT_DEPTH; + const int log2Wd = denom + shift - 1; + + ox0 = ox0 * (1 << (BIT_DEPTH - 8)); + ox1 = ox1 * (1 << (BIT_DEPTH - 8)); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(( (src[x] << (14 - BIT_DEPTH)) * wx1 + src0[x] * wx0 + (ox0 + ox1 + 1) * (1 << log2Wd)) >> (log2Wd + 1)); + src += src_stride; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +#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_vvc_luma_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 int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[hf_idx][mx]; + 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_vvc_luma_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 int hf_idx, const int vf_idx) +{ + const pixel *src = (pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[vf_idx][my]; + 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_vvc_luma_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, + const int hf_idx, const int vf_idx) +{ + int x, y; + const int8_t *filter; + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + + src -= LUMA_EXTRA_BEFORE * src_stride; + filter = ff_vvc_luma_filters[hf_idx][mx]; + for (y = 0; y < height + LUMA_EXTRA; y++) { + for (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 = ff_vvc_luma_filters[vf_idx][my]; + for (y = 0; y < height; y++) { + for (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_vvc_luma_uni_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[hf_idx][mx]; + 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_vvc_luma_bi_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + pixel *src = (pixel*)_src; + ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + + const int8_t *filter = ff_vvc_luma_filters[hf_idx][mx]; + + const int shift = 14 + 1 - BIT_DEPTH; +#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(((LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8)) + src0[x] + offset) >> shift); + src += src_stride; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_luma_uni_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[vf_idx][my]; + 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_vvc_luma_bi_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + + const int8_t *filter = ff_vvc_luma_filters[vf_idx][my]; + + const int shift = 14 + 1 - 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)) + src0[x] + offset) >> shift); + src += src_stride; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_luma_uni_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + int x, y; + const int8_t *filter; + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + 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; + filter = ff_vvc_luma_filters[hf_idx][mx]; + for (y = 0; y < height + LUMA_EXTRA; y++) { + for (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 = ff_vvc_luma_filters[vf_idx][my]; + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + 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_vvc_luma_bi_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + int x, y; + const int8_t *filter; + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const int shift = 14 + 1 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + src -= LUMA_EXTRA_BEFORE * src_stride; + filter = ff_vvc_luma_filters[hf_idx][mx]; + for (y = 0; y < height + LUMA_EXTRA; y++) { + for (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 = ff_vvc_luma_filters[vf_idx][my]; + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) + dst[x] = av_clip_pixel(((LUMA_FILTER(tmp, MAX_PB_SIZE) >> 6) + src0[x] + offset) >> shift); + tmp += MAX_PB_SIZE; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_luma_uni_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, + intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx) +{ + int x, y; + pixel *src = (pixel*)_src; + ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[hf_idx][mx]; + int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + int offset = 1 << (shift - 1); +#else + int offset = 0; +#endif + + ox = ox * (1 << (BIT_DEPTH - 8)); + for (y = 0; y < height; y++) { + for (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_vvc_luma_bi_w_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, const int height, + const int denom, const int w0, const int w1, const int o0, int o1, + const intptr_t mx, const intptr_t my, const int width, const int hf_idx, const int vf_idx) +{ + const pixel *src = (pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[hf_idx][mx]; + const int shift = denom + FFMAX(2, 14 - BIT_DEPTH) + 1; + 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++) { + const int src1 = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + dst[x] = av_clip_pixel((src1 * w1 + src0[x] * w0 + offset) >> shift); + } + src += src_stride; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_luma_uni_w_v)(uint8_t *_dst, ptrdiff_t _dst_stride, + const uint8_t *_src, ptrdiff_t _src_stride, int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width, const int hf_idx, const int vf_idx) +{ + int x, y; + pixel *src = (pixel*)_src; + ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_luma_filters[vf_idx][my]; + int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + int offset = 1 << (shift - 1); +#else + int offset = 0; +#endif + + ox = ox * (1 << (BIT_DEPTH - 8)); + for (y = 0; y < height; y++) { + for (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_vvc_luma_bi_w_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const int denom, const int wx0, const int wx1, + int ox0, int ox1, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + + const int8_t *filter = ff_vvc_luma_filters[vf_idx][my]; + + const int shift = 14 + 1 - BIT_DEPTH; + const int log2Wd = denom + shift - 1; + + ox0 = ox0 * (1 << (BIT_DEPTH - 8)); + ox1 = ox1 * (1 << (BIT_DEPTH - 8)); + 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)) * wx1 + src0[x] * wx0 + + ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); + src += src_stride; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_luma_uni_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, + int wx, int ox, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + int x, y; + const int8_t *filter; + pixel *src = (pixel*)_src; + ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + int offset = 1 << (shift - 1); +#else + int offset = 0; +#endif + + src -= LUMA_EXTRA_BEFORE * src_stride; + filter = ff_vvc_luma_filters[hf_idx][mx]; + for (y = 0; y < height + LUMA_EXTRA; y++) { + for (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 = ff_vvc_luma_filters[vf_idx][my]; + + ox = ox * (1 << (BIT_DEPTH - 8)); + for (y = 0; y < height; y++) { + for (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; + } +} + +static void FUNC(put_vvc_luma_bi_w_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const int denom, const int wx0, const int wx1, + int ox0, int ox1, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + int x, y; + const int8_t *filter; + const pixel *src = (pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const int shift = 14 + 1 - BIT_DEPTH; + const int log2Wd = denom + shift - 1; + + src -= LUMA_EXTRA_BEFORE * src_stride; + filter = ff_vvc_luma_filters[hf_idx][mx]; + for (y = 0; y < height + LUMA_EXTRA; y++) { + for (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 = ff_vvc_luma_filters[vf_idx][my]; + + ox0 = ox0 * (1 << (BIT_DEPTH - 8)); + ox1 = ox1 * (1 << (BIT_DEPTH - 8)); + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) + dst[x] = av_clip_pixel(((LUMA_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx1 + src0[x] * wx0 + + ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); + tmp += MAX_PB_SIZE; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +#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_vvc_chroma_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 int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + 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_vvc_chroma_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 int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[vf_idx][my]; + + 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_vvc_chroma_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, + const int hf_idx, const int vf_idx) +{ + int x, y; + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + + src -= CHROMA_EXTRA_BEFORE * src_stride; + + for (y = 0; y < height + CHROMA_EXTRA; y++) { + for (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 = ff_vvc_chroma_filters[vf_idx][my]; + + for (y = 0; y < height; y++) { + for (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_vvc_chroma_uni_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + 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_vvc_chroma_bi_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + const int shift = 14 + 1 - 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)) + src0[x] + offset) >> shift); + dst += dst_stride; + src += src_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_chroma_uni_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[vf_idx][my]; + 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_vvc_chroma_bi_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + pixel *src = (pixel *)_src; + ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[vf_idx][my]; + pixel *dst = (pixel *)_dst; + ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + int shift = 14 + 1 - BIT_DEPTH; +#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)) + src0[x] + offset) >> shift); + dst += dst_stride; + src += src_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_chroma_uni_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + int x, y; + pixel *src = (pixel *)_src; + ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + int offset = 1 << (shift - 1); +#else + int offset = 0; +#endif + + src -= CHROMA_EXTRA_BEFORE * src_stride; + + for (y = 0; y < height + CHROMA_EXTRA; y++) { + for (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 = ff_vvc_chroma_filters[vf_idx][my]; + + for (y = 0; y < height; y++) { + for (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_vvc_chroma_bi_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + int x, y; + const pixel *src = (pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const int shift = 14 + 1 - 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 (y = 0; y < height + CHROMA_EXTRA; y++) { + for (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 = ff_vvc_chroma_filters[vf_idx][my]; + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) + dst[x] = av_clip_pixel(((CHROMA_FILTER(tmp, MAX_PB_SIZE) >> 6) + src0[x] + offset) >> shift); + tmp += MAX_PB_SIZE; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_chroma_uni_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, + intptr_t mx, intptr_t my, int width, const int hf_idx, const int vf_idx) +{ + int x, y; + pixel *src = (pixel *)_src; + ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + int offset = 1 << (shift - 1); +#else + int offset = 0; +#endif + + ox = ox * (1 << (BIT_DEPTH - 8)); + for (y = 0; y < height; y++) { + for (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_vvc_chroma_bi_w_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const int denom, const int w0, const int w1, + int o0, int o1, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + const int shift = denom + FFMAX(2, 14 - BIT_DEPTH) + 1; + 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++) { + const int src1 = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + dst[x] = av_clip_pixel((src1 * w1 + src0[x] * w0 + offset) >> shift); + } + src += src_stride; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_chroma_uni_w_v)(uint8_t *_dst, ptrdiff_t _dst_stride, + const uint8_t *_src, ptrdiff_t _src_stride, int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width, const int hf_idx, const int vf_idx) +{ + int x, y; + pixel *src = (pixel *)_src; + ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[vf_idx][my]; + int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + int offset = 1 << (shift - 1); +#else + int offset = 0; +#endif + + ox = ox * (1 << (BIT_DEPTH - 8)); + for (y = 0; y < height; y++) { + for (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_vvc_chroma_bi_w_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const int denom, const int wx0, const int wx1, + int ox0, int ox1, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + const pixel *src = (pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[vf_idx][my]; + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int shift = 14 + 1 - BIT_DEPTH; + const int log2Wd = denom + shift - 1; + + ox0 = ox0 * (1 << (BIT_DEPTH - 8)); + ox1 = ox1 * (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, src_stride) >> (BIT_DEPTH - 8)) * wx1 + src0[x] * wx0 + + ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); + src += src_stride; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_chroma_uni_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, intptr_t mx, intptr_t my, int width, const int hf_idx, const int vf_idx) +{ + int x, y; + pixel *src = (pixel *)_src; + ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + int offset = 1 << (shift - 1); +#else + int offset = 0; +#endif + + src -= CHROMA_EXTRA_BEFORE * src_stride; + + for (y = 0; y < height + CHROMA_EXTRA; y++) { + for (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 = ff_vvc_chroma_filters[vf_idx][my]; + + ox = ox * (1 << (BIT_DEPTH - 8)); + for (y = 0; y < height; y++) { + for (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(put_vvc_chroma_bi_w_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src0, + const int height, const int denom, const int wx0, const int wx1, + int ox0, int ox1, const intptr_t mx, const intptr_t my, const int width, + const int hf_idx, const int vf_idx) +{ + int x, y; + const pixel *src = (pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx]; + int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const int shift = 14 + 1 - BIT_DEPTH; + const int log2Wd = denom + shift - 1; + + src -= CHROMA_EXTRA_BEFORE * src_stride; + + for (y = 0; y < height + CHROMA_EXTRA; y++) { + for (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 = ff_vvc_chroma_filters[vf_idx][my]; + + ox0 = ox0 * (1 << (BIT_DEPTH - 8)); + ox1 = ox1 * (1 << (BIT_DEPTH - 8)); + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) + dst[x] = av_clip_pixel(((CHROMA_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx1 + src0[x] * wx0 + + ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); + tmp += MAX_PB_SIZE; + dst += dst_stride; + src0 += MAX_PB_SIZE; + } +} + +static void FUNC(put_vvc_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 int dst_stride = _dst_stride / sizeof(pixel); + const int 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_vvc_gpm)(uint8_t *_dst, ptrdiff_t dst_stride, int width, int height, + const int16_t *tmp, const int16_t *tmp1, const ptrdiff_t tmp_stride, + 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((tmp[x] * w + tmp1[x] * (8 - w) + offset) >> shift); + } + dst += dst_stride; + tmp += tmp_stride; + tmp1 += tmp_stride; + 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 + + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + pixel *dst = (pixel*)_dst; + + 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, int ox) +{ + const int limit = (1 << FFMAX(13, BIT_DEPTH + 1)); ///< dILimit + + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + pixel *dst = (pixel*)_dst; + + const int shift = denom + FFMAX(2, 14 - BIT_DEPTH); + const int offset = 1 << (shift - 1); + + int16_t gradient_h[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + int16_t gradient_v[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + + ox = ox * (1 << (BIT_DEPTH - 8)); + + 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(apply_prof_bi)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const int16_t *src0, const int16_t *src1, const int16_t *diff_mv_x, const int16_t *diff_mv_y) +{ + const int limit = (1 << FFMAX(13, BIT_DEPTH + 1)); ///< dILimit + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + pixel *dst = (pixel*)_dst; + + + const int shift = 14 + 1 - 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, src1, 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 = src1[x] + av_clip(di, -limit, limit - 1); + dst[x] = av_clip_pixel((val + src0[x] + offset) >> shift); + + } + src0 += MAX_PB_SIZE; + src1 += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(apply_prof_bi_w)(uint8_t *_dst, const ptrdiff_t _dst_stride, const int16_t *src0, const int16_t *src1, + const int16_t *diff_mv_x, const int16_t *diff_mv_y, const int denom, const int w0, const int w1, int o0, int o1) +{ + const int limit = (1 << FFMAX(13, BIT_DEPTH + 1)); ///< dILimit + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + pixel *dst = (pixel*)_dst; + + + const int shift = 14 + 1 - BIT_DEPTH; + const int log2Wd = denom + shift - 1; + + int16_t gradient_h[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + int16_t gradient_v[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + + o0 = o0 * (1 << (BIT_DEPTH - 8)); + o1 = o1 * (1 << (BIT_DEPTH - 8)); + + FUNC(prof_grad_filter)(gradient_h, gradient_v, AFFINE_MIN_BLOCK_SIZE, src1, 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 = src1[x] + av_clip(di, -limit, limit - 1); + dst[x] = av_clip_pixel((val * w1 + src0[x] * w0 + ((o0 + o1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); + } + src0 += MAX_PB_SIZE; + src1 += 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_vvc_luma)(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); +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { +#if BIT_DEPTH > 10 + dst[x] = (src[x] + offset4) >> shift4; +#else + dst[x] = src[x] << (10 - BIT_DEPTH); +#endif + } + src += src_stride; + dst += MAX_PB_SIZE; + } + +} + +//8.5.3.2.2 Luma sample bilinear interpolation process +static void FUNC(dmvr_vvc_luma_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_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_vvc_luma_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_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_vvc_luma_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) +{ + int x, y; + const int8_t *filter; + const pixel *src = (pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + int16_t tmp_array[(MAX_PB_SIZE + BILINEAR_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + 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; + filter = ff_vvc_dmvr_filters[mx]; + for (y = 0; y < height + BILINEAR_EXTRA; y++) { + for (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_dmvr_filters[my]; + for (y = 0; y < height; y++) { + for (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, idx1, idx2, idx3, a) \ + inter->dst[idx1][idx2][idx3] = FUNC(a) \ + +#define DIR_FUNCS(d, C, c) \ + PEL_FUNC(put_##d, C, 0, 0, put_vvc_pel_##d##_pixels); \ + PEL_FUNC(put_##d, C, 0, 1, put_vvc_##c##_##d##_h); \ + PEL_FUNC(put_##d, C, 1, 0, put_vvc_##c##_##d##_v); \ + PEL_FUNC(put_##d, C, 1, 1, put_vvc_##c##_##d##_hv); \ + PEL_FUNC(put_##d##_w, C, 0, 0, put_vvc_pel_##d##_w_pixels); \ + PEL_FUNC(put_##d##_w, C, 0, 1, put_vvc_##c##_##d##_w_h); \ + PEL_FUNC(put_##d##_w, C, 1, 0, put_vvc_##c##_##d##_w_v); \ + PEL_FUNC(put_##d##_w, C, 1, 1, put_vvc_##c##_##d##_w_hv); + +#define FUNCS(C, c) \ + PEL_FUNC(put, C, 0, 0, put_vvc_pel_pixels); \ + PEL_FUNC(put, C, 0, 1, put_vvc_##c##_h); \ + PEL_FUNC(put, C, 1, 0, put_vvc_##c##_v); \ + PEL_FUNC(put, C, 1, 1, put_vvc_##c##_hv); \ + DIR_FUNCS(uni, C, c); \ + DIR_FUNCS(bi, C, c); \ + +static void FUNC(ff_vvc_inter_dsp_init)(VVCInterDSPContext *const inter) +{ + FUNCS(LUMA, luma); + FUNCS(CHROMA, chroma); + + inter->dmvr[0][0] = FUNC(dmvr_vvc_luma); + inter->dmvr[0][1] = FUNC(dmvr_vvc_luma_h); + inter->dmvr[1][0] = FUNC(dmvr_vvc_luma_v); + inter->dmvr[1][1] = FUNC(dmvr_vvc_luma_hv); + + inter->put_ciip = FUNC(put_vvc_ciip); + inter->put_gpm = FUNC(put_vvc_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_prof_bi = FUNC(apply_prof_bi); + inter->apply_prof_bi_w = FUNC(apply_prof_bi_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 9dacd81dd3..d004742447 100644 --- a/libavcodec/vvc/vvcdec.h +++ b/libavcodec/vvc/vvcdec.h @@ -31,6 +31,7 @@ #include "vvc_executor.h" #include "vvc_ps.h" +#include "vvcdsp.h" #define LUMA 0 #define CHROMA 1 @@ -188,6 +189,7 @@ struct VVCFrameContext { VVCFrame *ref; + VVCDSPContext vvcdsp; VideoDSPContext vdsp; VVCFrameThread *frame_thread; diff --git a/libavcodec/vvc/vvcdsp.h b/libavcodec/vvc/vvcdsp.h new file mode 100644 index 0000000000..fdc9f35756 --- /dev/null +++ b/libavcodec/vvc/vvcdsp.h @@ -0,0 +1,183 @@ +/* + * 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 */][2 /* int, frac */][2 /* int, frac */])( + int16_t *dst, const uint8_t *src, ptrdiff_t src_stride, + int height, intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx); + + void (*put_uni[2 /* luma, chroma */][2 /* int, frac */][2 /* int, frac */])( + uint8_t *dst, ptrdiff_t dst_stride, + const uint8_t *src, ptrdiff_t src_stride, int height, + intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx); + void (*put_uni_w[2 /* luma, chroma */][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, + intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx); + + void (*put_bi[2 /* luma, chroma */][2 /* int, frac */][2 /* int, frac */])( + uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, const int16_t *src2, + int height, intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx); + void (*put_bi_w[2 /* luma, chroma */][2 /* int, frac */][2 /* int, frac */])( + uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, const int16_t *src2, + int height, int denom, int wx0, int wx1, int ox0, int ox1, + intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx); + + 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 *tmp, const int16_t *tmp1, const ptrdiff_t tmp_stride, + 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_prof_bi)(uint8_t *dst, ptrdiff_t dst_stride, const int16_t *src0, const int16_t *src1, + const int16_t *diff_mv_x, const int16_t *diff_mv_y); + void (*apply_prof_bi_w)(uint8_t *dst, ptrdiff_t dst_stride, const int16_t *src0, const int16_t *src1, + const int16_t *diff_mv_x, const int16_t *diff_mv_y, int denom, int w0, int w1, int o0, int o1); + + 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 depth); +} 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, int beta, int32_t tc, + uint8_t no_p, uint8_t no_q, uint8_t max_len_p, uint8_t max_len_q, int hor_ctu_edge); + void (*filter_chroma[2 /* h, v */])(uint8_t *pix, ptrdiff_t stride, int beta, int32_t tc, + uint8_t no_p, uint8_t no_q, int shift, int max_len_p, int max_len_q); +} VVCLFDSPContext; + +struct SAOParams; +typedef struct VVCSAODSPContext { + void (*band_filter[9])(uint8_t *dst, uint8_t *src, ptrdiff_t dst_stride, ptrdiff_t src_stride, + 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 */, uint8_t *src /* align 32 */, ptrdiff_t dst_stride, + int16_t *sao_offset_val, int sao_eo_class, int width, int height); + void (*edge_restore[2])(uint8_t *dst, uint8_t *src, ptrdiff_t dst_stride, ptrdiff_t src_stride, + struct SAOParams *sao, int *borders, int width, int height, int c_idx, + uint8_t *vert_edge, uint8_t *horiz_edge, 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 int8_t *filter, const int16_t *clip); + void (*filter_vb[2 /* luma, chroma */])(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, + int width, int height, const int8_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 int8_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)(int8_t *coeff, int16_t *clip, const int *class_idx, const int *transpose_idx, int size, + const int8_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); + +extern const int8_t ff_vvc_chroma_filters[3][32][4]; +extern const int8_t ff_vvc_luma_filters[3][16][8]; +extern const int8_t ff_vvc_dmvr_filters[16][2]; + +#endif /* AVCODEC_VVCDSP_H */ From patchwork Sun May 21 13:03: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: 41764 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:ba91:b0:105:feb:71f2 with SMTP id fb17csp1090898pzb; Sun, 21 May 2023 06:04:55 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ6VqqAF/rk/tsUzr9iMK3c7UFZnuqzdRa6MsW12/Zf06SjApy/SyrpYZioYh+adNiSDrYuF X-Received: by 2002:aa7:d045:0:b0:510:86cc:f687 with SMTP id n5-20020aa7d045000000b0051086ccf687mr6347970edo.20.1684674294818; Sun, 21 May 2023 06:04:54 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684674294; cv=none; d=google.com; s=arc-20160816; b=e+ZLna/NKzHrL2tg7OvUE1hLlPWdbXeyFY6cI1jhJ0zqiycNrAu/as02wVEaAOX6W4 nHKsXmi83GkCJGHreLk9nzpxVNsh8PRfL/MrrZ0Wh+gPrOBIIF4xc3C9vmR8pHL39N/P WxveFLcmcXjLVyvMqb/4b5Ua/U7dpdIiOqsnFciaQ4B//zUEx8xdwH+eQw8ccS6KNTeU bqShSpXpxtHH0QJR0b5XUzhNe6+xi7ESFX1asYVsc+5azeLyIPHGtQz8ur/Y2zRzKDUp gsGDl/JhW1GJx61q+QQucd36u5S5b29TO4RV4wIG3ifrtkZvddPdmsaCmjM3BU7LkoqR Hm/w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=iJ25CpEQYXSWcQAqGZhdKxe5DGQXsI8FqmjjJCyM4Ds=; b=T8IOTpeL4IXTbhbGkPWx121zRwIU3XTdXRpOEpnmI/Bcr8Vgy80w2V9lr9Y/GByY8U yoFTI9WjEekEDmxGJd5+VfFDeavSQN5I70NE4DtTBGVkxAkCYY+67xv+CHuyLM8glt2z 1J2d0rq2VW1uTgoAe4H7dkgq6X6UasUR0/bL6LEri2JfzVABzO6IxpOEP0DODhG2P7Eb /d2iJQpuUNgfo34JLWZ7iaWrqUI+EWPjZgjVDxEcj2tj4hGT/xhaej09gNQ5X2/vKoxf zKQ+miqBLSUt1PpzAmYiRaikXOivejCDZ9s7l2Wdm8F7IKFQ11JtQpoLl0l0uQ8lVt2y EZ0g== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20221208 header.b=etwBbeZ9; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id w19-20020a056402129300b0051064a9f901si1518784edv.595.2023.05.21.06.04.54; Sun, 21 May 2023 06:04:54 -0700 (PDT) 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=@gmail.com header.s=20221208 header.b=etwBbeZ9; 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 E043468C167; Sun, 21 May 2023 16:03:47 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f177.google.com (mail-pf1-f177.google.com [209.85.210.177]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 6618D68C122 for ; Sun, 21 May 2023 16:03:45 +0300 (EEST) Received: by mail-pf1-f177.google.com with SMTP id d2e1a72fcca58-64d3fdcadb8so1271112b3a.3 for ; Sun, 21 May 2023 06:03:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1684674224; x=1687266224; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=jQZdlDPifv/6GSV+j16u4pAGABBfxEiMRiiRAV3YqOQ=; b=etwBbeZ9tlUR+XiISlUQp39J08BWfgkDIJfuKt6m3aK2MhRTapmfzsx9mdFbd4Z78J bwFyR3A7V8VspofTXFKMGuEey6J8FKtIi0SoxdibY3c4bhB/H4Rb0yxfsUfBTuVdJn0g XzB0lVMyp5Dfpf6hwQpvg4V3QX3fvIxOE28yd5MUxzVL4N8sbKZ6j5SiIkmPgoFZ1sz0 xnWgAHcSFx8K6v9O6nVbRkyBB8fQx1rW74ZOj82H5QnvPGL1dIohpZnBOHLwVDhafMUZ SfXY0/vwiAN0LIISCTr8cj5OsQXaTeKzLRjTXP3jIIPZrgG9bJITCNk7zrjDTzsm+bgs vMwg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684674224; x=1687266224; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=jQZdlDPifv/6GSV+j16u4pAGABBfxEiMRiiRAV3YqOQ=; b=VCcJc1jIRIZDLxo9879932Wa6q+1/NDWeG8pjppsP2Y+nf2qwxcPycJqSEF4psgmzo JDKOOi/LWn7S/n2hPVFNpwAgvGS+NB8alIRjKjT4+/tHkHSrLJi8yoD5qA2WcXe4VinG xLwTtnHmVRiipXK2j/61FUTPu9us3LnSbkBT6B8ChqZogXuUd/VMImi3sw3U52MsvIqp lM4IykloTMNN9AJH5uaTqqN7pBgMJIs9d+ymyk8fszrvXI2qatQ2Jv/5poqeaBN/jLkU dv2tjzUA4ZdMA5q5DrPUx1XfNeawSjGqfkiP/YoFRVvGnoJlmc6URS1P3wnkwJHMNOuF AbGw== X-Gm-Message-State: AC+VfDxlcV9rBvzD4HOBqMTXZ+XORTuKQjTfLdGOeUF+xFAYm1k1kgiA lY17i1LRUmi4gBl7BseP09a87rr4bUmPjw== X-Received: by 2002:a05:6a00:c8d:b0:63c:1be4:5086 with SMTP id a13-20020a056a000c8d00b0063c1be45086mr10049799pfv.6.1684674222573; Sun, 21 May 2023 06:03:42 -0700 (PDT) Received: from NuoMi.localdomain ([112.64.8.97]) by smtp.gmail.com with ESMTPSA id t9-20020aa79389000000b0064d45eea573sm1671872pfe.41.2023.05.21.06.03.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 21 May 2023 06:03:42 -0700 (PDT) From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 21 May 2023 21:03:13 +0800 Message-Id: <20230521130319.13813-9-nuomi2021@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230521130319.13813-1-nuomi2021@gmail.com> References: <20230521130319.13813-1-nuomi2021@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 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: YGDdRSGiHIEZ --- libavcodec/vvc/Makefile | 3 +- libavcodec/vvc/vvc_itx_1d.c | 712 ++++++++++++++++++++++++++++++++++++ libavcodec/vvc/vvc_itx_1d.h | 51 +++ 3 files changed, 765 insertions(+), 1 deletion(-) 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 f60a85ac52..d536a6b1ea 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -9,4 +9,5 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ vvc/vvc_refs.o \ vvc/vvc_mvs.o \ vvc/vvc_ctu.o \ - vvc/vvc_inter.o + vvc/vvc_inter.o \ + vvc/vvc_itx_1d.o diff --git a/libavcodec/vvc/vvc_itx_1d.c b/libavcodec/vvc/vvc_itx_1d.c new file mode 100644 index 0000000000..7e1974c179 --- /dev/null +++ b/libavcodec/vvc/vvc_itx_1d.c @@ -0,0 +1,712 @@ +/* + * 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 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((t + 64) >> 7 , -(1 << 15), (1 << 15) - 1); + } +} diff --git a/libavcodec/vvc/vvc_itx_1d.h b/libavcodec/vvc/vvc_itx_1d.h new file mode 100644 index 0000000000..06991c3159 --- /dev/null +++ b/libavcodec/vvc/vvc_itx_1d.h @@ -0,0 +1,51 @@ +/* + * 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); + +#endif // AVCODEC_VVC_TRANSFORM_H From patchwork Sun May 21 13:03: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: 41766 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:ba91:b0:105:feb:71f2 with SMTP id fb17csp1091179pzb; Sun, 21 May 2023 06:05:19 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ65e8IhLSP+Dl7ISERf+zCF06C6Kpkj+8PklG+gu/qFHfC9xJMMFwAdZqqG75fGfTFvr0bA X-Received: by 2002:a17:907:c20:b0:96f:9963:81ee with SMTP id ga32-20020a1709070c2000b0096f996381eemr5451601ejc.50.1684674318737; Sun, 21 May 2023 06:05:18 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684674318; cv=none; d=google.com; s=arc-20160816; b=HANIWFsZ1MGDg5j+zi21jXvvvFliSvVYAMmi+CwlTrmjjbfm49dQ4q0V0PmW21ychO Jpq6vGY6Tar3rxR3jfzNZ6RF0yQVy1QgEV1YBulT/HYEUzo4ZZ6YvcMTg1MWxHAOwaMZ 86nPwC5aIpAdhJxptBG67FQks5nn9205qP2iH4XcSukcOVjx77y7hJS9i48B1dhKakr7 Y2fh8nt3IZm2sTdu9XNs05FLSy8IQSSh10PmKPtsfm3wfTww753aU3YpWGjPoSsEPeom t0uEMe1ycBnXQ3fpeGPVii55vEAnFI7fATTQWK4WaX/UFQgQWgjKKDoGfsUAMY6GxUC3 3HvA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=PYrk2tEOxd/MJAJw7T++0lgg9ZDN0hezj0RRt30PSbA=; b=gBup6iDubqUQm1Jib8dTj9ZYXgXQXaOLB3XbFX5jYhufDPU+A3aTLMx5JLFAUsvPBo iz+9zp6SwdazGruySattghAYBLqzs6rGO46gs/+EH/GpSQZ6ZLU3JuM7RHXxpfYqK59L Gp8mEkZymE/CAhyRibpgm/o+4bQ+MRf9TfSvg4CA5NPbXFBETGK3yfdmvLTszlFC+F3l jE9BaeAS9wuJF7P1jm/ujAvQP6ZTERVr54ba+ovi6LaMbPkZqfosdIDvXqfmsTxUoR2o L90JU6qj/fz1NHHNfVhhbJ4pwMTctbtBUUZtT+geHAJ1FfzvE/Ok8kzG3hM7YbTI/OaK u2GA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20221208 header.b=Ja0H0v4I; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id f11-20020a1709062c4b00b0096f8fea9a2csi2788251ejh.55.2023.05.21.06.05.18; Sun, 21 May 2023 06:05:18 -0700 (PDT) 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=@gmail.com header.s=20221208 header.b=Ja0H0v4I; 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 B5D7168C16B; Sun, 21 May 2023 16:03:51 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f181.google.com (mail-pf1-f181.google.com [209.85.210.181]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 579AA68C163 for ; Sun, 21 May 2023 16:03:48 +0300 (EEST) Received: by mail-pf1-f181.google.com with SMTP id d2e1a72fcca58-64d3fbb8c1cso2106566b3a.3 for ; Sun, 21 May 2023 06:03:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1684674226; x=1687266226; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=1lRmsq1V9gKOTqdK1bT4cLagNHzvsjR7+QsllGM/6oE=; b=Ja0H0v4I8LeSIIHbz0fIxg6P+JrOf440eN/oofRKq+vjKCQot8YcOHuckXWDRiR2ez j5MiDIKXiGKzEJL82UwmPwf71e9hPJZDhJOEDj3DX2MOuDer8n6f3KxhezIez/Y2C2HG WlQF3BCG6v2EWWOKjIQV1EccV/FYWGfxHQr6qeaOKiRFFe1DW8uUgxGfOmxUjvQPeyZq CXMYhKK36agzkk0kXRGKe8IkKF18in3VCb5Z3D6JNczPnfW+THStfYqqaErIpmKOB7Ia zkuW5nDu6g1ZiG2ojhDfbnfWboG9WM8sNrHPB8ewVwOX+8MtXz+bqxSnMOWFzngVoxDG A9XA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684674226; x=1687266226; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=1lRmsq1V9gKOTqdK1bT4cLagNHzvsjR7+QsllGM/6oE=; b=PaeKvJDODla37/PDxE4ecpG2rfGJMNymoeSxEBNiX9/7scbuti77PbhiwVTPSTUh6L Gn9Texo/vRIYlQ33XlfV0oU6wVQ9R45FFjysPPAJRrTj2rQ0QFdA7pEKC4uFteGhNxsH n0n02ZFB1GYqD+mkEqXaDJU3nELT+kB4sD3p5pNMXpOSQu+E2K6Y2I7oX0BB/Wm/h0OU zzmodT6o8enob3k6oeiEg8IvUCnxEPeHMod5w17I2eM/2gi6T9AQo69sWBpbWeUTh5b2 lsLO142F+7nShof4n+1J9wBz5tRnWvWhIlj+AHGtR7/gpGiNbTi6IXuUkFCMrN488NPM 0+sg== X-Gm-Message-State: AC+VfDxHu5neOErBs4PXtysr/Zcup0TESwtXmxrS7OldqctetmBMcoro qAiPn8OmXJI43NQuSyD70T7E/TVrqyUvcA== X-Received: by 2002:a05:6a20:1590:b0:101:8b:43a5 with SMTP id h16-20020a056a20159000b00101008b43a5mr9287974pzj.8.1684674224489; Sun, 21 May 2023 06:03:44 -0700 (PDT) Received: from NuoMi.localdomain ([112.64.8.97]) by smtp.gmail.com with ESMTPSA id t9-20020aa79389000000b0064d45eea573sm1671872pfe.41.2023.05.21.06.03.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 21 May 2023 06:03:44 -0700 (PDT) From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 21 May 2023 21:03:14 +0800 Message-Id: <20230521130319.13813-10-nuomi2021@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230521130319.13813-1-nuomi2021@gmail.com> References: <20230521130319.13813-1-nuomi2021@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 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: XKcn7c4q71G/ --- libavcodec/vvc/Makefile | 3 +- libavcodec/vvc/vvc_ctu.c | 40 ++ libavcodec/vvc/vvc_ctu.h | 2 + libavcodec/vvc/vvc_intra.c | 763 ++++++++++++++++++++ libavcodec/vvc/vvc_intra.h | 49 ++ libavcodec/vvc/vvc_intra_template.c | 1018 +++++++++++++++++++++++++++ 6 files changed, 1874 insertions(+), 1 deletion(-) 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 d536a6b1ea..9d95a15424 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -10,4 +10,5 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ vvc/vvc_mvs.o \ vvc/vvc_ctu.o \ vvc/vvc_inter.o \ - vvc/vvc_itx_1d.o + vvc/vvc_itx_1d.o \ + vvc/vvc_intra.o diff --git a/libavcodec/vvc/vvc_ctu.c b/libavcodec/vvc/vvc_ctu.c index 4e6e582718..a5fb788f6e 100644 --- a/libavcodec/vvc/vvc_ctu.c +++ b/libavcodec/vvc/vvc_ctu.c @@ -25,6 +25,35 @@ #include "vvc_data.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,3 +68,14 @@ void ff_vvc_set_neighbour_available(VVCLocalContext *lc, (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_ctu_free_cus(CTU *ctu) +{ + while (ctu->cus) { + CodingUnit *cu = ctu->cus; + AVBufferRef *buf = cu->buf; + + ctu->cus = ctu->cus->next; + av_buffer_unref(&buf); + } +} diff --git a/libavcodec/vvc/vvc_ctu.h b/libavcodec/vvc/vvc_ctu.h index 02c757559e..92a4fcd539 100644 --- a/libavcodec/vvc/vvc_ctu.h +++ b/libavcodec/vvc/vvc_ctu.h @@ -388,4 +388,6 @@ 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); #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..ec5be8fef5 --- /dev/null +++ b/libavcodec/vvc/vvc_intra.c @@ -0,0 +1,763 @@ +/* + * 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 "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 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); + 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->mts_enabled_flag) { + if (cu->isp_split_type != ISP_NO_SPLIT || + (cu->sbt_flag && FFMAX(tb->tb_width, tb->tb_height) <= 32) || + (!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->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 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_int16((*p + add) >> shift); + 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 VVCSH *sh = &lc->sc->sh; + 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->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) - 5 + 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 VVCSH *sh = &lc->sc->sh; + const CodingUnit *cu = lc->cu; + const AVBufferRef *ref; + const VVCScalingList *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->scaling_matrix_for_alternative_colour_space_disabled_flag); + + if (!sh->explicit_scaling_list_used_flag || tb->ts || + sps->scaling_matrix_for_lfnst_disabled_flag && cu->apply_lfnst_flag[tb->c_idx]) + return ff_vvc_default_scale_m; + + ref = ps->scaling_list[ps->ph->scaling_list_aps_id]; + if (!ref || !ref->data) { + av_log(lc->fc->avctx, AV_LOG_WARNING, "bug: no scaling list aps, id = %d", ps->ph->scaling_list_aps_id); + return ff_vvc_default_scale_m; + } + + sl = (const VVCScalingList *)ref->data; + 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) +{ + coeff = (coeff * scale * scale_m + tb->bd_offset) >> tb->bd_shift; + coeff = av_clip(coeff, -(1<<15), (1<<15) - 1); + return coeff; +} + +static void dequant(const VVCLocalContext *lc, const TransformUnit *tu, TransformBlock *tb) +{ + uint8_t tmp[MAX_TB_SIZE * MAX_TB_SIZE]; + const VVCSH *sh = &lc->sc->sh; + const uint8_t *scale_m = derive_scale_m(lc, tb, tmp); + int scale; + + derive_qp(lc, tu, tb); + scale = derive_scale(tb, 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); + 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); + + 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, 20 - 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, 21 - sps->bit_depth); +} + +static void transform_bdpcm(TransformBlock *tb, const VVCLocalContext *lc, const CodingUnit *cu) +{ + 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, 15); + 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->lmcs_used_flag && fc->ps.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->chroma_format_idc && (cu->tree_type != DUAL_TREE_LUMA); + + for (int ch_type = start; ch_type <= end; ch_type++) { + for (int i = 0; i < cu->num_tus; i++) { + TransformUnit *tu = &cu->tus[i]; + + predict_intra(lc, tu, i, ch_type); + itransform(lc, tu, i, ch_type); + } + } + 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->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..f4e42bf799 --- /dev/null +++ b/libavcodec/vvc/vvc_intra_template.c @@ -0,0 +1,1018 @@ +/* + * 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_data.h" +#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->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->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->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 VVCPH *ph = fc->ps.ph; + 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 = ph->lmcs_min_bin_idx; i <= ph->lmcs_max_bin_idx; i++) { + if (luma < ph->lmcs_pivot[i + 1]) + break; + } + i = FFMIN(i, LMCS_MAX_BIN_SIZE - 1); + + lc->lmcs.chroma_scale = ph->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); + } + } +} + +//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; + const pixel pred = p[0]; + POS(x, y) = pred; + } + } else { + if (!c_idx) { + const int8_t* f = filter_flag ? ff_vvc_filter_g[fact] : ff_vvc_filter_c[fact]; + for (int x = 0; x < w; x++) { + const pixel *p = top + x + idx; + const int pred = (p[0] * f[0] + p[1] * f[1] + + p[2] * f[2] + p[3] * f[3] + 32) >> 6; + POS(x, y) = av_clip_pixel(pred); + } + } else { + for (int x = 0; x < w; x++) { + const pixel *p = top + x + idx + 1; + const pixel pred = ((32 - fact) * p[0] + fact * p[1] + 16) >> 5; + POS(x, y) = pred; + } + } + } + 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 = filter_flag ? ff_vvc_filter_g[fact] : ff_vvc_filter_c[fact] ; + pred = (p[0] * f[0] + p[1] * f[1] + p[2] * f[2] + p[3] * f[3] + 32) >> 6; + pred = CLIP(pred); + } else { + pred = ((32 - fact) * p[1] + fact * p[2] + 16) >> 5; + } + } + 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 May 21 13:03: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: 41768 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:ba91:b0:105:feb:71f2 with SMTP id fb17csp1091448pzb; Sun, 21 May 2023 06:05:44 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ4TjwmA7kr2B6zpIV1Yt/1WxfWVccJbuf+7kNVDvNPIpWe5//jCOc8vZOny9ie9N3diIPYH X-Received: by 2002:a17:907:1689:b0:96a:4654:9a49 with SMTP id hc9-20020a170907168900b0096a46549a49mr7984242ejc.67.1684674343800; Sun, 21 May 2023 06:05:43 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684674343; cv=none; d=google.com; s=arc-20160816; b=luUqSk8fW9baRgcPbG0Q+17ViD7p5a4HqCaAz8CWv0y1gWr4l/3hD0zgljOjU3NPZ2 ZOEE8D2BBoM28GHBJhnerc/uHXIplmtbBn7gfV1QjtL4op/OfXP/FOGNR1wNaRzk5kmL lbUn//6I02Fc2f2ST7kl7K7tgUXEKQriWSNaDWTRxu0ZggXNGr5l3543K4HDdeH7La8j tyBY3audlIn+f0E5BsLon8VDIqeGvGIZqm6ghz/jC5/6Un3DgBzYWDFZvG3SQX2p966V mOq57GAOOpMPdiKAmwkAxX8A6q1XTZ68eA9D4t1ncqRxx1+fQcab5PPjBwggSBG5B6pj OcNA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=xhe1l0AYbj/rdMWanMX2HFps9nOzYI0q0bp1prFNy4Q=; b=p4OK0+pFxVdJOBrgW534FHgXr6iyeVHDk7LCiBULzgb8y1I6TGbNKgQArSzTuTbMRs YvmPrGf5Jr6ldaaweIv+bGxZ/xOBv7cNTsqwUehvFw5kCZeft+MHjf0Yizh4RA1fBjTK wG7ttZb/27afGEfGHmaHBKQrkZOS8LVSXfNEdnmI5mZSroLtWwN5s9pmdypGwWLy+i6n 4xX0xQMJ4mEz0KgFhP+DLYFleY6XpX0I5ej8FcrZTN87gxxx1wjjbetieX6WIMdxd+Ec 1uCB+wOJFvajL68oKyX7+qRhkVKRTR2Xc9MFrqQEd2nBvSpekmDRf5vqRnWhnEXBOIXv 7nuA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20221208 header.b=OyNlJRhS; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id az13-20020a170907904d00b0096f40b0433bsi2468807ejc.396.2023.05.21.06.05.43; Sun, 21 May 2023 06:05:43 -0700 (PDT) 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=@gmail.com header.s=20221208 header.b=OyNlJRhS; 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 1C6DD68C191; Sun, 21 May 2023 16:03:54 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f171.google.com (mail-pf1-f171.google.com [209.85.210.171]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id A427C68C16A for ; Sun, 21 May 2023 16:03:50 +0300 (EEST) Received: by mail-pf1-f171.google.com with SMTP id d2e1a72fcca58-64d2da69fdfso3185551b3a.0 for ; Sun, 21 May 2023 06:03:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1684674228; x=1687266228; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Gj57g2p3cPRiLMrca4s+6eu53V1m67uJpbu52Jg0zhw=; b=OyNlJRhSHwOTzmodavdXDwtyIuTu8dkvu6nXmMvxTne2xkUgy8yvys/HYTrpR36T0J 09Bbybu0MqsSGEM0z26Kcs1S4/6abX3WrHtGcm8XdYVp5VtfNipdFIqfIRBTZYH94Jg0 3solVBJZ8NjD0FFUIxIgALj3Cgj5bjMLoDrAaOZ7IAhRYmBbuCNEANGtnZSre9EnQ/05 lobwltQmaMY6STNl4JE4aTTXJf28KUKtHE968hp2SZGUWtJXvX+B/fzoL58f/AobAwwt XFMhF+Ny1HOcio3e+FGuN8uCPo1Au/MKqqbW8CRgtHmAeA7L6UZqvaMbZGF1qqVp/45a L8bQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684674228; x=1687266228; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Gj57g2p3cPRiLMrca4s+6eu53V1m67uJpbu52Jg0zhw=; b=HciUQAnuI6hFGq7XGScRgfDwPCjpWIkH87Xe6J2VwoIo7sWAJhZxWLFDg8HE8T5yc1 eUZQIrWDB4Py0JQl5V8OGoqxgHVujXUH9Kwhb/CzcGFFfeoa96Vm7pIZyHqSaeA4IHae z0yEWghckKwbLL89q+tut/TlkucqXvED8gZrIbF2qCB6CtAEY2THltcp2ZTb5Mi3MSQN dYl/t5ThdQHx9JE7NYMFQlV0PdzVzRVZkEwyYzaRMqBV/ikLaqwLCUdWNHiR/2gj5UX9 GSOlaB8DewbFOjlmIrMdr7+68qbA05HneFBCQDukjVP4XXBidD1WwqvanXfeBBqeG+6u KkWw== X-Gm-Message-State: AC+VfDxmHKhq3db4Wf3MoDpwDfuuILuRZy7XiVv7P14hje9bCjxuedQs +MxVX0fi5qlmaAOBUMSq2x7lz8cHw1bhYg== X-Received: by 2002:a05:6a00:14c4:b0:647:e45f:1a49 with SMTP id w4-20020a056a0014c400b00647e45f1a49mr10749893pfu.4.1684674226808; Sun, 21 May 2023 06:03:46 -0700 (PDT) Received: from NuoMi.localdomain ([112.64.8.97]) by smtp.gmail.com with ESMTPSA id t9-20020aa79389000000b0064d45eea573sm1671872pfe.41.2023.05.21.06.03.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 21 May 2023 06:03:46 -0700 (PDT) From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 21 May 2023 21:03:15 +0800 Message-Id: <20230521130319.13813-11-nuomi2021@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230521130319.13813-1-nuomi2021@gmail.com> References: <20230521130319.13813-1-nuomi2021@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 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: NwoR7pf3N5O8 --- libavcodec/vvc/Makefile | 3 +- libavcodec/vvc/vvc_ctu.h | 2 + libavcodec/vvc/vvc_filter.c | 1356 ++++++++++++++++++++++++++ libavcodec/vvc/vvc_filter.h | 70 ++ libavcodec/vvc/vvc_filter_template.c | 1216 +++++++++++++++++++++++ 5 files changed, 2646 insertions(+), 1 deletion(-) 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 9d95a15424..e596659ba9 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -11,4 +11,5 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ vvc/vvc_ctu.o \ vvc/vvc_inter.o \ vvc/vvc_itx_1d.o \ - vvc/vvc_intra.o + vvc/vvc_intra.o \ + vvc/vvc_filter.o diff --git a/libavcodec/vvc/vvc_ctu.h b/libavcodec/vvc/vvc_ctu.h index 92a4fcd539..e491933f85 100644 --- a/libavcodec/vvc/vvc_ctu.h +++ b/libavcodec/vvc/vvc_ctu.h @@ -390,4 +390,6 @@ 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); + #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..d8987d26a3 --- /dev/null +++ b/libavcodec/vvc/vvc_filter.c @@ -0,0 +1,1356 @@ +/* + * 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 "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 + +//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) +{ + int i, j; + + if (((intptr_t)dst | (intptr_t)src | dst_stride | src_stride) & 15) { + for (i = 0; i < height; i++) { + for (j = 0; j < width; j+=8) + AV_COPY64U(dst+j, src+j); + dst += dst_stride; + src += src_stride; + } + } else { + for (i = 0; i < height; i++) { + for (j = 0; j < width; j+=16) + AV_COPY128(dst+j, src+j); + 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) +{ + 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]; + + /* copy horizontal edges */ + memcpy(fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb) * w + x) << ps), + src, width << ps); + 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); +} + +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->loop_filter_across_slices_enabled_flag; + uint8_t no_tile_filter = fc->ps.pps->num_tiles_in_pic > 1 && + !fc->ps.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->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: + copy_CTB_to_hv(fc, src, src_stride, x0, y0, width, height, c_idx, x_ctb, y_ctb); + fc->vvcdsp.sao.band_filter[tab](src, src, src_stride, src_stride, + sao->offset_val[c_idx], sao->band_position[c_idx], width, height); + sao->type_idx[c_idx] = SAO_APPLIED; + 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; + int left_pixels, right_pixels; + + 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[2]; + uint8_t *dst1; + int src_idx, pos; + + dst1 = dst - dst_stride - (left << sh); + src1[0] = src - src_stride - (left << sh); + src1[1] = fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb - 1) * w + x0 - left) << sh); + pos = 0; + if (left) { + src_idx = (CTB(fc->tab.sao, x_ctb-1, y_ctb-1).type_idx[c_idx] == + SAO_APPLIED); + copy_pixel(dst1, src1[src_idx], sh); + pos += (1 << sh); + } + src_idx = (CTB(fc->tab.sao, x_ctb, y_ctb-1).type_idx[c_idx] == + SAO_APPLIED); + memcpy(dst1 + pos, src1[src_idx] + pos, width << sh); + if (right) { + pos += width << sh; + src_idx = (CTB(fc->tab.sao, x_ctb+1, y_ctb-1).type_idx[c_idx] == + SAO_APPLIED); + copy_pixel(dst1 + pos, src1[src_idx] + pos, sh); + } + } + if (!edges[BOTTOM]) { + int left = 1 - edges[LEFT]; + int right = 1 - edges[RIGHT]; + const uint8_t *src1[2]; + uint8_t *dst1; + int src_idx, pos; + + dst1 = dst + height * dst_stride - (left << sh); + src1[0] = src + height * src_stride - (left << sh); + src1[1] = fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb + 2) * w + x0 - left) << sh); + pos = 0; + if (left) { + src_idx = (CTB(fc->tab.sao, x_ctb-1, y_ctb+1).type_idx[c_idx] == + SAO_APPLIED); + copy_pixel(dst1, src1[src_idx], sh); + pos += (1 << sh); + } + src_idx = (CTB(fc->tab.sao, x_ctb, y_ctb+1).type_idx[c_idx] == + SAO_APPLIED); + memcpy(dst1 + pos, src1[src_idx] + pos, width << sh); + if (right) { + pos += width << sh; + src_idx = (CTB(fc->tab.sao, x_ctb+1, y_ctb+1).type_idx[c_idx] == + SAO_APPLIED); + copy_pixel(dst1 + pos, src1[src_idx] + pos, sh); + } + } + left_pixels = 0; + if (!edges[LEFT]) { + if (CTB(fc->tab.sao, x_ctb-1, y_ctb).type_idx[c_idx] == SAO_APPLIED) { + 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); + } else { + left_pixels = 1; + } + } + right_pixels = 0; + if (!edges[RIGHT]) { + if (CTB(fc->tab.sao, x_ctb+1, y_ctb).type_idx[c_idx] == SAO_APPLIED) { + 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); + } else { + right_pixels = 1; + } + } + + copy_CTB(dst - (left_pixels << sh), + src - (left_pixels << sh), + (width + left_pixels + right_pixels) << sh, + height, dst_stride, src_stride); + + copy_CTB_to_hv(fc, src, src_stride, x0, y0, width, height, c_idx, + x_ctb, y_ctb); + 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); + sao->type_idx[c_idx] = SAO_APPLIED; + 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)] +#define DEBLOCK_STEP 4 +#define MAX_FILTER_LEN 8 +#define LUMA_GRID 4 +#define CHROMA_GRID 8 + +static int boundary_strength(const VVCFrameContext *fc, MvField *curr, MvField *neigh, + const RefPicList *neigh_refPicList) +{ + if (curr->pred_flag == PF_BI && neigh->pred_flag == PF_BI) { + // same L0 and L1 + if (fc->ref->refPicList[0].list[curr->ref_idx[0]] == neigh_refPicList[0].list[neigh->ref_idx[0]] && + fc->ref->refPicList[0].list[curr->ref_idx[0]] == fc->ref->refPicList[1].list[curr->ref_idx[1]] && + neigh_refPicList[0].list[neigh->ref_idx[0]] == neigh_refPicList[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_refPicList[0].list[neigh->ref_idx[0]] == fc->ref->refPicList[0].list[curr->ref_idx[0]] && + neigh_refPicList[1].list[neigh->ref_idx[1]] == fc->ref->refPicList[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_refPicList[1].list[neigh->ref_idx[1]] == fc->ref->refPicList[0].list[curr->ref_idx[0]] && + neigh_refPicList[0].list[neigh->ref_idx[0]] == fc->ref->refPicList[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 = fc->ref->refPicList[0].list[curr->ref_idx[0]]; + } else { + A = curr->mv[1]; + ref_A = fc->ref->refPicList[1].list[curr->ref_idx[1]]; + } + + if (neigh->pred_flag & 1) { + B = neigh->mv[0]; + ref_B = neigh_refPicList[0].list[neigh->ref_idx[0]]; + } else { + B = neigh->mv[1]; + ref_B = neigh_refPicList[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 VVCFrameContext *fc, + const int cb_x, const int cb_y, const int x0, const int y0, const int width, const int height) +{ + MvField* tab_mvf = fc->ref->tab_mvf; + RefPicList* rpl = fc->ref->refPicList; + 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(fc, 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 VVCFrameContext *fc, + const int cb_x, const int cb_y, const int x0, const int y0, const int width, const int height) +{ + MvField* tab_mvf = fc->ref->tab_mvf; + RefPicList* rpl = fc->ref->refPicList; + 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(fc, 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->ref->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->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->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) : + fc->ref->refPicList; + 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(fc, 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(fc, 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->ref->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->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->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) : + fc->ref->refPicList; + 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(fc, 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(fc, 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->ref->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->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->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->ref->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->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->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->ladf_enabled_flag) + return qp; + + level = fc->vvcdsp.lf.ladf_level[vertical](src, fc->frame->linesize[LUMA]); + qp_offset = 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->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 = fc->ps.sps->chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; + uint8_t *src; + int x, y, beta, tc, qp; + uint8_t no_p = 0; + uint8_t no_q = 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) { + for (x = x0 ? x0 : grid; x < x_end; x += grid) { + const int bs = TAB_BS(fc->tab.vertical_bs[c_idx], x, y); + if (bs) { + uint8_t max_len_p, max_len_q; + + src = &fc->frame->data[c_idx][(y >> vs) * fc->frame->linesize[c_idx] + ((x >> hs) << fc->ps.sps->pixel_shift)]; + qp = get_qp(fc, src, x, y, c_idx, 1); + + beta = betatable[av_clip(qp + beta_offset, 0, MAX_QP)]; + + tc = TC_CALC(qp, bs); + + max_filter_length(fc, x, y, c_idx, 1, 0, bs, &max_len_p, &max_len_q); + + 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, vs, max_len_p, max_len_q); + } + } + } + } + } +} + +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->chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; + uint8_t* src; + int x, y, beta, tc, qp; + uint8_t no_p = 0; + uint8_t no_q = 0; + + int ctb_log2_size_y = fc->ps.sps->ctb_log2_size_y; + int x_end, x_end2, 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; + int tc_offset, beta_offset; + + 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; + + x_end2 = x_end == fc->ps.sps->width ? x_end : x_end - (MAX_FILTER_LEN << hs); + + for (y = y0; y < y_end; y += grid) { + if (!y) + continue; + + // horizontal filtering luma + for (x = x0 ? x0 - (MAX_FILTER_LEN << hs) : 0; x < x_end2; x += DEBLOCK_STEP) { + const int bs = TAB_BS(fc->tab.horizontal_bs[c_idx], x, y); + if (bs) { + const DBParams *params = fc->tab.deblock + ctb - (x < x0); + const int horizontal_ctu_edge = !(y % fc->ps.sps->ctb_size_y); + uint8_t max_len_p, max_len_q; + + src = &fc->frame->data[c_idx][(y >> vs) * fc->frame->linesize[c_idx] + ((x >> hs) << fc->ps.sps->pixel_shift)]; + qp = get_qp(fc, src, x, y, c_idx, 0); + + tc_offset = params->tc_offset[c_idx]; + beta_offset = params->beta_offset[c_idx]; + + beta = betatable[av_clip(qp + beta_offset, 0, MAX_QP)]; + tc = TC_CALC(qp, bs); + + max_filter_length(fc, x, y, c_idx, 0, horizontal_ctu_edge, bs, &max_len_p, &max_len_q); + + 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, hs, max_len_p, max_len_q); + } + } + } + } + } +} + +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, int8_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 VVCSH *sh = &lc->sc->sh; + uint8_t fixed_clip_set[ALF_NUM_FILTERS_LUMA * ALF_NUM_COEFF_LUMA] = { 0 }; + const int8_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; + class_to_filt = ff_vvc_alf_class_to_filt_map[alf->ctb_filt_set_idx_y]; + } else { + const int id = sh->alf.aps_id_luma[alf->ctb_filt_set_idx_y - 16]; + const VVCALF *aps = (VVCALF *)fc->ps.alf_list[id]->data; + coeff_set = aps->luma_coeff; + clip_idx_set = aps->luma_clip_idx; + 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; + const int no_vb_height = height > vb_pos ? vb_pos - ALF_VB_POS_ABOVE_LUMA : height; + const int h = height - no_vb_height; + int8_t *coeff = (int8_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, no_vb_height, vb_pos, alf); + fc->vvcdsp.alf.filter[LUMA](dst, dst_stride, src, src_stride, width, no_vb_height, coeff, clip); + + if (h > 0) { + vb_pos -= no_vb_height; + dst += dst_stride * no_vb_height; + src += src_stride * no_vb_height; + alf_get_coeff_and_clip(lc, coeff, clip, src, src_stride, width, h, vb_pos, alf); + fc->vvcdsp.alf.filter_vb[LUMA](dst, dst_stride, src, src_stride, width, h, 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 VVCSH *sh = &lc->sc->sh; + const VVCALF *aps = (VVCALF *)fc->ps.alf_list[sh->alf.aps_id_chroma]->data; + const int off = alf->alf_ctb_filter_alt_idx[c_idx - 1] * ALF_NUM_COEFF_CHROMA; + const int8_t *coeff = aps->chroma_coeff + off; + const int no_vb_height = height > vb_pos ? vb_pos - ALF_VB_POS_ABOVE_CHROMA : height; + 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[off + i]); + + fc->vvcdsp.alf.filter[CHROMA](dst, dst_stride, src, src_stride, width, no_vb_height, coeff, clip); + if (no_vb_height < height) { + dst += dst_stride * no_vb_height; + src += src_stride * no_vb_height; + fc->vvcdsp.alf.filter_vb[CHROMA](dst, dst_stride, src, src_stride, width, height - no_vb_height, coeff, clip, vb_pos - no_vb_height); + } +} + +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 VVCSH *sh = &lc->sc->sh; + const int idx = c_idx - 1; + AVBufferRef *aps_buf = fc->ps.alf_list[sh->alf.cc_aps_id[idx]]; + + if (aps_buf) { + const VVCALF *aps = (VVCALF *)aps_buf->data; + const int off = (alf->ctb_cc_idc[idx] - 1)* ALF_NUM_COEFF_CC; + const int8_t *coeff = aps->cc_coeff[idx] + off; + + 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->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->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->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->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.lmcs_used_flag) + fc->vvcdsp.lmcs.filter(data, fc->frame->linesize[LUMA], width, height, fc->ps.ph->lmcs_inv_lut); +} diff --git a/libavcodec/vvc/vvc_filter.h b/libavcodec/vvc/vvc_filter.h new file mode 100644 index 0000000000..2fbfaa23b1 --- /dev/null +++ b/libavcodec/vvc/vvc_filter.h @@ -0,0 +1,70 @@ +/* + * 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_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..93059577a5 --- /dev/null +++ b/libavcodec/vvc/vvc_filter_template.c @@ -0,0 +1,1216 @@ +/* + * 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, uint8_t *_src, ptrdiff_t dst_stride, ptrdiff_t src_stride, + int16_t *sao_offset_val, const int sao_left_class, const int width, const int height) +{ + pixel *dst = (pixel *)_dst; + 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, uint8_t *_src, ptrdiff_t dst_stride, + 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; + 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, uint8_t *_src, + ptrdiff_t dst_stride, ptrdiff_t src_stride, SAOParams *sao, + int *borders, const int _width, const int _height, const int c_idx, + uint8_t *vert_edge, uint8_t *horiz_edge, uint8_t *diag_edge) +{ + int x, y; + pixel *dst = (pixel *)_dst; + pixel *src = (pixel *)_src; + int16_t *sao_offset_val = sao->offset_val[c_idx]; + 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, uint8_t *_src, + ptrdiff_t dst_stride, ptrdiff_t src_stride, SAOParams *sao, + int *borders, int _width, int _height, int c_idx, + uint8_t *vert_edge, uint8_t *horiz_edge, uint8_t *diag_edge) +{ + int x, y; + pixel *dst = (pixel *)_dst; + pixel *src = (pixel *)_src; + int16_t *sao_offset_val = sao->offset_val[c_idx]; + 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 int8_t *filter, const int16_t *clip) +{ + const pixel *src = (pixel *)_src; + const int shift = 7; + const int offset = 1 << ( shift - 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; + + 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]); + + sum = (sum + offset) >> shift; + 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_luma_vb)(uint8_t *_dst, ptrdiff_t dst_stride, const uint8_t *_src, ptrdiff_t src_stride, + const int width, const int height, const int8_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 int8_t* filter, const int16_t* clip) +{ + const pixel *src = (pixel *)_src; + const int shift = 7; + const int offset = 1 << ( shift - 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; + + 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]); + + sum = (sum + offset) >> shift; + sum += curr; + dst[j] = CLIP(sum); + + p0++; + p1++; + p2++; + p3++; + p4++; + p5++; + p6++; + } + } + } + } +} + +static void FUNC(alf_filter_chroma_vb)(uint8_t* _dst, ptrdiff_t dst_stride, const uint8_t* _src, ptrdiff_t src_stride, + const int width, const int height, const int8_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 int8_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)(int8_t *coeff, int16_t *clip, + const int *class_idx, const int *transpose_idx, const int size, + const int8_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 int8_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(vvc_loop_filter_luma)(uint8_t* _pix, + ptrdiff_t _xstride, ptrdiff_t _ystride, + int _beta, int _tc, + uint8_t _no_p, uint8_t _no_q, + uint8_t max_len_p, uint8_t max_len_q, + int hor_ctu_edge) +{ + int d; + pixel* pix = (pixel*)_pix; + ptrdiff_t xstride = _xstride / sizeof(pixel); + ptrdiff_t ystride = _ystride / sizeof(pixel); + + 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 + (1 << (9 - BIT_DEPTH))) >> (10 - BIT_DEPTH); +#else + const int tc = _tc << (BIT_DEPTH - 10); +#endif + const int no_p = _no_p; + const int no_q = _no_q; + + const int large_p = (max_len_p > 3 && !hor_ctu_edge); + const int large_q = max_len_q > 3; + const int beta = _beta << BIT_DEPTH - 8; + + const int beta_3 = beta >> 3; + const int beta_2 = beta >> 2; + const int tc25 = ((tc * 5 + 1) >> 1); + + + 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) { + for (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; + } + return; + + } + } + } + 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) { + // strong filtering + const int tc2 = tc << 1; + const int tc3 = tc * 3; + for (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; + } + } else { // weak filtering + int nd_p = 1; + int nd_q = 1; + const int tc_2 = tc >> 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; + } + + for (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_chroma)(uint8_t *_pix, const ptrdiff_t _xstride, + const ptrdiff_t _ystride, const int _beta, const int _tc, const uint8_t no_p, const uint8_t no_q, + const int shift, int max_len_p, int max_len_q) +{ + pixel *pix = (pixel *)_pix; + const ptrdiff_t xstride = _xstride / sizeof(pixel); + const ptrdiff_t ystride = _ystride / sizeof(pixel); + const int end = shift ? 2 : 4; + const int beta = _beta << (BIT_DEPTH - 8); + const int beta_3 = beta >> 3; + const int beta_2 = beta >> 2; + +#if BIT_DEPTH < 10 + const int tc = (_tc + (1 << (9 - BIT_DEPTH))) >> (10 - BIT_DEPTH); +#else + const int tc = _tc << (BIT_DEPTH - 10); +#endif + const int tc25 = ((tc * 5 + 1) >> 1); + + if (!max_len_p || !max_len_q) + return; + + 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) { + //strong + for (int d = 0; d < end; 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; + } + } else if (max_len_q == 3) { + for (int d = 0; d < end; 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; + } + } else { + //weak + for (int d = 0; d < end; 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_h_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride, + int beta, int32_t tc, uint8_t no_p, uint8_t no_q, int shift, int max_len_p, int max_len_q) +{ + FUNC(vvc_loop_filter_chroma)(pix, stride, sizeof(pixel), beta, tc, no_p, no_q, shift, max_len_p, max_len_q); +} + +static void FUNC(vvc_v_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride, + int beta, int32_t tc, uint8_t no_p, uint8_t no_q, int shift, int max_len_p, int max_len_q) +{ + FUNC(vvc_loop_filter_chroma)(pix, sizeof(pixel), stride, beta, tc, no_p, no_q, shift, max_len_p, max_len_q); +} + +static void FUNC(vvc_h_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride, + int beta, int32_t tc, uint8_t no_p, + uint8_t no_q, uint8_t max_len_p, 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, + int beta, int32_t tc, uint8_t no_p, + uint8_t no_q, uint8_t max_len_p, 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_vb[LUMA] = FUNC(alf_filter_luma_vb); + alf->filter_vb[CHROMA] = FUNC(alf_filter_chroma_vb); + 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 May 21 13:03:16 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 41769 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:ba91:b0:105:feb:71f2 with SMTP id fb17csp1091600pzb; Sun, 21 May 2023 06:05:55 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5YJjHqG75Vb8FDdOY6a2dWE3pXWa484W55QN86yy3rlVmqmnPysrh1LRbKu6vCTeTveBOv X-Received: by 2002:a17:907:6da9:b0:96f:bab6:4db5 with SMTP id sb41-20020a1709076da900b0096fbab64db5mr2666711ejc.28.1684674355279; Sun, 21 May 2023 06:05:55 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684674355; cv=none; d=google.com; s=arc-20160816; b=OKhu51e/TBIJ+nYWlzZVNt19+F4mUA5TDX3R4RrpBv5h5SMbpJbbpR/ZmrZLHR9t5e z0m2EJssoFcudYgcJoYHVKS677DRQwmrDSHDSFDJznLjpLpKRZlBqM6nKD/9suPyusfg BIodchx60CEC2AtKjWarEa+CUzbkRWNbKQBozPyca5ws16wo82e5QMHnduLo5239IrTt tPzqwS4nvjBCkvFFaKFaJtZuOZjy9bmrposUWmcqMOYDdn2XcGIwnqWACfqQXIfXR0oa ocdAcXbtM7w2/NS0AOEpZcCjcoP5PpSi85flAsIU6FLM3A61U9lUs4XvVHdVynPFcMvL 2iFQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=ttBErTxDrpRCJidMho0iMgRjwYWMs2amvpqiQpbRoyw=; b=jQ+4/ld734W0EdRO+P1ieYQE7xdn1LvOHbIASk7PrHDgMEDKKgyb6yGR+XkiPLPeeo fD60xtnJoyl96vAYPZX+MdcGkbZAhHusFwDlD0Hb6GwYJ7DtLMnlFDITCn+3lkE9BFb1 0idga1fyirm3veQW/47EJIrmu/dRZuc/D0+OltAodOyXMAJY4VlpR5G589j791782CRk LqPmewZHTFA0v6qakYnNcuX+ZR7Dsh8o41MJ9O5BR5rgoYoWB+wqDBXsJwl4Za4/5kQn R194rPUkJWkD1eTiqSrbC2bUKaBvwtpzEpWi0SQysKXddA8cteyiMWjSV+g6OMYFAITV xSXg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20221208 header.b="os/ieGmr"; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id e10-20020a50ec8a000000b0050bca41879csi1113804edr.89.2023.05.21.06.05.54; Sun, 21 May 2023 06:05:55 -0700 (PDT) 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=@gmail.com header.s=20221208 header.b="os/ieGmr"; 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 2D26968C18B; Sun, 21 May 2023 16:03:55 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f169.google.com (mail-pf1-f169.google.com [209.85.210.169]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 384E868C14A for ; Sun, 21 May 2023 16:03:50 +0300 (EEST) Received: by mail-pf1-f169.google.com with SMTP id d2e1a72fcca58-64d2467d640so3839679b3a.1 for ; Sun, 21 May 2023 06:03:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1684674228; x=1687266228; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=KwoCXM4XGnRzOrZbIHZbWj3uGNXKDdqMywN5I30fnkI=; b=os/ieGmrXZ9epb4z2zFpq+bzeISossLhxOGGor60fe/KYEt6P3rzKBoXYq4OOc+TMq RXrKGMcaGfqn5wvGhk/3xfrXGbpRCeX2YLAe4VEMRRrOUq8cNonD13Xwt2eWZfYt2zsa FGLD3OGICiROX1YRizgGizDV9xwpyS7JhYZDriENZbxvrfqdHYq6gib4W9Ja/EXa/NCj hUAdly8AA2uLfcNnTnyVnmUqXR2yOuQTHKhiBdjhxSse0VPHkyKyegVfrYfs+tThoWhq c/ThjllidCsOQcENUDQcQmpu5A73jwfeT2LXG+M6hVdN8/RaU6XPv7ybmAfi3VB6Fg0D k0NA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684674228; x=1687266228; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=KwoCXM4XGnRzOrZbIHZbWj3uGNXKDdqMywN5I30fnkI=; b=lDYlbJd32sUU0q+U/K6TjtWQZ4OyTmn9GKGaSQmN1FLdhVOXHGjrA23DJNFQfea9Cg yVBdSdtncsv0uffazmojCUoNc2gfdkoBXoGy4nf/PAiG/Gz4sAiaOrZBi8eaU2Lm5srR cMbZ8GVKXuhfsYOfjxoLczMyZD06Pr/GmTTCMHmvCPHe5yWuQR+GwNLOD4tvX3iQdkXD Fcb5a8IKkY3f1EsxFUv2o68MV6l3Fx8Pz/tgj1WmMf0SPFuGID1zhyfiZKDlBIaFxLIX Ec/2iHZQyNJ8Ehu3C/tRMl65TNDtg9wpCpnJCD8qgTURWbxuASNyl5TLj/ROKsKVcnnN le4A== X-Gm-Message-State: AC+VfDzuNSi/SgIrniQt8ggzCzjl1fSWh9qBzQuMhKw8f/sxKLahVagA GtuUYsV2btEG/hHc4uBFIAtTPqln8wDecw== X-Received: by 2002:a05:6a00:1783:b0:64d:2841:8380 with SMTP id s3-20020a056a00178300b0064d28418380mr10650137pfg.22.1684674228465; Sun, 21 May 2023 06:03:48 -0700 (PDT) Received: from NuoMi.localdomain ([112.64.8.97]) by smtp.gmail.com with ESMTPSA id t9-20020aa79389000000b0064d45eea573sm1671872pfe.41.2023.05.21.06.03.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 21 May 2023 06:03:48 -0700 (PDT) From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 21 May 2023 21:03:16 +0800 Message-Id: <20230521130319.13813-12-nuomi2021@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230521130319.13813-1-nuomi2021@gmail.com> References: <20230521130319.13813-1-nuomi2021@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 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: OdegGxay6O5d --- libavcodec/vvc/Makefile | 3 +- libavcodec/vvc/vvcdsp.c | 316 +++++++++++++++++++++++++++++++ libavcodec/vvc/vvcdsp_template.c | 119 ++++++++++++ 3 files changed, 437 insertions(+), 1 deletion(-) 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 e596659ba9..0f5c3daab0 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -12,4 +12,5 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ vvc/vvc_inter.o \ vvc/vvc_itx_1d.o \ vvc/vvc_intra.o \ - vvc/vvc_filter.o + vvc/vvc_filter.o \ + vvc/vvcdsp.o diff --git a/libavcodec/vvc/vvcdsp.c b/libavcodec/vvc/vvcdsp.c new file mode 100644 index 0000000000..9ca52eb3d5 --- /dev/null +++ b/libavcodec/vvc/vvcdsp.c @@ -0,0 +1,316 @@ +/* + * 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) + +DECLARE_ALIGNED(16, const int8_t, ff_vvc_chroma_filters)[3][32][4] = { + { + //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 }, + }, +}; + +DECLARE_ALIGNED(16, const int8_t, ff_vvc_luma_filters)[3][16][8] = { + { + //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 }, + }, + +}; + +DECLARE_ALIGNED(16, const int8_t, ff_vvc_dmvr_filters)[16][2] = { + { 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}, +}; + +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 BIT_DEPTH 8 +#include "vvcdsp_template.c" +#undef BIT_DEPTH + +#define BIT_DEPTH 10 +#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 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..28a0027425 --- /dev/null +++ b/libavcodec/vvc/vvcdsp_template.c @@ -0,0 +1,119 @@ +/* + * 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 "libavcodec/bit_depth_template.c" + +#include "vvcdec.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 depth) +{ + 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], depth); + 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], depth); + 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 May 21 13:03:17 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 41770 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:ba91:b0:105:feb:71f2 with SMTP id fb17csp1091714pzb; Sun, 21 May 2023 06:06:06 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ7+nEy/4ORaSzi2Mf+o4t+5+X2KJuDsWwMY473eqCzBR5VSXrCpCNA4QreIXmdcz9P2eY0Y X-Received: by 2002:a17:907:3ea9:b0:947:335f:5a0d with SMTP id hs41-20020a1709073ea900b00947335f5a0dmr9272915ejc.62.1684674366081; Sun, 21 May 2023 06:06:06 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684674366; cv=none; d=google.com; s=arc-20160816; b=MGJqtHRwjE8VBL+nyzizoJJdR4e8gfBPpJGyEISNImJS/r4e61so4njgoTwk0IeZpa QTGVbE4RQ54wl6eYg/6xxAiCVJlG9GoyR/Cl67eSHiOl8FTNLYgA74/NqT78imNlEfAL 7basqszP/qhbFrEy1abU9DYowBlVBdH0VQLzllN6EEWaw5pWO7kvcDqxKyBICcsbQGdn 5+vW4gik4CgTOuWU7jUTotYwhW3x2bIY6/Q5LnUjX62VM0aiev2O/ONSgymqogkUq9CB DMfwtEzHOEQd3xEaTMHlXxnGdmHllQ7LvzE/h8etx9QT7N6YtqFLkRBD5QNRXXuPn0AL M1Yg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=QtEAfDhqO8fNlfmjcxHUc2Q0LsS6Y5uhX7OD3vPOEI0=; b=xGGUFnhZSdLw92SQimWa8A7k/2QvmOkYD7derjL6tE0tRzuzi8SKRXfvp1n+3fFujk 0mZrb54NH2FTi060esb+XtdQOPpLFWmp9YEu2j94lFn4tyk5CKVbQWSK/njjoHF1KiOQ AgV6HrjeWhUb9RCUMXFCvQwy0FuQbMXpYtGrEpf9vRdGQIsyZtvMNTxVpdbv09qW4AGO ZAC7rprRWfTZZF5qwRKpMesb7i/U+dURnlXj8xZYRv8375F1hSPHWsy1zVGafcxtgGya 80N4PuOc28JpUwJ8ySDnIIE79NwIF9YIhUoToKMBZ/dsSIgGfnMtYkkWlJzTd2oc7cIf nTsA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20221208 header.b=bKYkbSst; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id c25-20020a170906155900b0094f1d0ab6d2si2866556ejd.515.2023.05.21.06.06.05; Sun, 21 May 2023 06:06:06 -0700 (PDT) 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=@gmail.com header.s=20221208 header.b=bKYkbSst; 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 256A468C1A0; Sun, 21 May 2023 16:03:56 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f169.google.com (mail-pf1-f169.google.com [209.85.210.169]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id B89D968C183 for ; Sun, 21 May 2023 16:03:52 +0300 (EEST) Received: by mail-pf1-f169.google.com with SMTP id d2e1a72fcca58-64d2467d640so3839706b3a.1 for ; Sun, 21 May 2023 06:03:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1684674231; x=1687266231; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=x5Aq8YxafTzqvPb9VNx5CWGIcB92b1VAdKpA6z+h0YI=; b=bKYkbSstyfG2RYdp8tijk0nHIEsO+LNU0jB8VcXqCGtoiKnEb8lqOTmSLOU5cHh2Jn Fpg9UUCTeGwBY0vZKOA3GAopHHdKulXLT0Mu+GOvKAzI9O11aSBsMvL4tQf/jnqzNAwH YvK00O7e0tHl25XkVakGAK8VuPc2WQ36NVJAEvSTHyfVh/8B4sJdh6vq0BnkcjZTjXO9 laktGBHyiL5a7AWMQBcdVWQIz2j4uTpk5K3aZDHV+QHHZphvRRtLuLD29qtZMFub4dHF sIA9zJoPYF4cSkEoheneFV+epjZEAy2clIUxGF6Up4pc9twsReXVOy4C6tBNTOJ+FW5R 2ngQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684674231; x=1687266231; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=x5Aq8YxafTzqvPb9VNx5CWGIcB92b1VAdKpA6z+h0YI=; b=O/KOj5NosCwIOo25yxyDATbwXvS3LGr8z4Wnp6F7EDCTKTGbKkIRhrYtFsuIqkm52H 8oXP8xc7pwdEkpVKgUFuzP95mEhXMNNO2dHs76A/za7yqsM8p6TTpNByCmHxsP6sV7NU 9CSWFfGrpborXLDmWvuTaID634n5NNujlCgfMkbKufEKMOgs9FFvmrFiX7lW+MVyTKNR p+e/XScbNuRWlD2yPWEWpmxKxgxisgNJJsxFZAOH6YsyElbxVxdtFzPSrASU6nDXxVBS hZKPky0vTXkmeBpZTowh5FtNFq2fH0CUtKALZdUNb7EB1lCXwhq+362u3aIWaUihju1t eGoA== X-Gm-Message-State: AC+VfDyYS7ahSoLNBkcmCfPGdjoPQvj81WPQV5Juj863ZZdnOp51RVYF sZEiRaqUTQWihlGZFCMvqFN2mb+IJb7GJw== X-Received: by 2002:a05:6a00:1803:b0:63a:ea82:b7b7 with SMTP id y3-20020a056a00180300b0063aea82b7b7mr9730460pfa.28.1684674230430; Sun, 21 May 2023 06:03:50 -0700 (PDT) Received: from NuoMi.localdomain ([112.64.8.97]) by smtp.gmail.com with ESMTPSA id t9-20020aa79389000000b0064d45eea573sm1671872pfe.41.2023.05.21.06.03.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 21 May 2023 06:03:49 -0700 (PDT) From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 21 May 2023 21:03:17 +0800 Message-Id: <20230521130319.13813-13-nuomi2021@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230521130319.13813-1-nuomi2021@gmail.com> References: <20230521130319.13813-1-nuomi2021@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 12/14] vvcdec: add CTU(Coding Tree Unit) 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: o6mtEZ+5JifS --- libavcodec/vvc/vvc_ctu.c | 2123 +++++++++++++++++++++++++++++++++++++- libavcodec/vvc/vvc_ctu.h | 11 + 2 files changed, 2133 insertions(+), 1 deletion(-) diff --git a/libavcodec/vvc/vvc_ctu.c b/libavcodec/vvc/vvc_ctu.c index a5fb788f6e..20f25f7ef8 100644 --- a/libavcodec/vvc/vvc_ctu.c +++ b/libavcodec/vvc/vvc_ctu.c @@ -22,9 +22,2121 @@ #include "vvc_cabac.h" #include "vvc_ctu.h" -#include "vvc_data.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->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 VVCSH *sh = &lc->sc->sh; + 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); + int qp; + + for (int i = CB - 1; i < CR + 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* add_tu(CodingUnit *cu, const int x0, const int y0, const int tu_width, const int tu_height) +{ + TransformUnit *tu; + + if (cu->num_tus >= FF_ARRAY_ELEMS(cu->tus)) + return NULL; + + tu = &cu->tus[cu->num_tus]; + 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; + cu->num_tus++; + + 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 VVCSH *sh = &lc->sc->sh; + + if ((is_128 || is_chroma_coded) && + 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->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(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->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->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->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->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) +{ + const 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(lc->cu, x0, y0, tu_width, tu_height); + const int c_end = 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->cclm_enabled_flag) + return 0; + if (!sps->qtbtt_dual_tree_intra_flag || !IS_I(&lc->sc->sh) || 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; + int lfnst_width, lfnst_height, min_lfnst; + int lfnst_idx = 0; + + memset(cu->apply_lfnst_flag, 0, sizeof(cu->apply_lfnst_flag)); + + if (!sps->lfnst_enabled_flag || cu->pred_mode != MODE_INTRA || FFMAX(cb_width, cb_height) > sps->max_tb_size_y) + return 0; + + for (int i = 0; i < cu->num_tus; i++) { + const TransformUnit *tu = &cu->tus[i]; + 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; + } + } + + 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[0].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->explicit_mts_inter_enabled_flag) || + (cu->pred_mode == MODE_INTRA && 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) + 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->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; + + 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->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->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->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->mrl_enabled_flag && ((y0 % sps->ctb_size_y) > 0)) + cu->intra_luma_ref_idx = ff_vvc_intra_luma_ref_idx(lc); + if (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->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 VVCSH *sh = &lc->sc->sh; + 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(sh) || 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->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(sh)) { + 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(sh) && !cu->skip_flag) || + (!IS_I(sh) && (pred_mode != MODE_INTRA || + ((is_4x4 || mode_type == MODE_TYPE_INTRA) && !cu->skip_flag)))) && + !is_128 && mode_type != MODE_TYPE_INTER && 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(sh)) ? 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(sh); + 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->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* 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; + 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; + + AVBufferRef *buf = av_buffer_pool_get(fc->cu_pool); + if (!buf) + return NULL; + cu = (CodingUnit *)buf->data; + cu->next = NULL; + cu->buf = buf; + + if (ctu->cus) + lc->cu->next = cu; + else + ctu->cus = cu; + lc->cu = cu; + + 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->num_tus = 0; + 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; + + 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); + + for (int i = 0; i < cu->num_tus; i++) { + const TransformUnit *tu = cu->tus + i; + 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); + } + } +} + +//8.5.2.7 Derivation process for merge motion vector difference +static void derive_mmvd(MvField *mvf, const VVCFrameContext *fc, const Mv *mmvd_offset) +{ + Mv mmvd[2]; + + if (mvf->pred_flag == PF_BI) { + const RefPicList *refPicList = fc->ref->refPicList; + const int poc = fc->ps.ph->poc; + const int diff[] = { + poc - refPicList[0].list[mvf->ref_idx[0]], + poc - refPicList[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 (!refPicList[0].isLongTerm[mvf->ref_idx[0]] && !refPicList[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; + } +} + +static int hls_merge_data(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPH *ph = fc->ps.ph; + const VVCSPS *sps = fc->ps.sps; + const VVCSH *sh = &lc->sc->sh; + CodingUnit *cu = lc->cu; + PredictionUnit *pu = &cu->pu; + MotionInfo *mi = &pu->mi; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + MvField mvf; + int merge_idx = 0; + + pu->merge_gpm_flag = 0; + mi->num_sb_x = 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 && cb_width >= 8 && cb_height >= 8) { + pu->merge_subblock_flag = ff_vvc_merge_subblock_flag(lc); + } + if (pu->merge_subblock_flag) { + 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); + } else { + int regular_merge_flag = 1; + const int is_128 = cb_width == 128 || cb_height == 128; + const int ciip_avaiable = sps->ciip_enabled_flag && + !cu->skip_flag && (cb_width * cb_height >= 64); + const int gpm_avaiable = sps->gpm_enabled_flag && IS_B(sh) && + (cb_width >= 8) && (cb_height >=8) && + (cb_width < 8 * cb_height) && (cb_height < 8 *cb_width); + if (!is_128 && (ciip_avaiable || gpm_avaiable)) { + regular_merge_flag = ff_vvc_regular_merge_flag(lc, cu->skip_flag); + } + if (regular_merge_flag) { + Mv mmvd_offset; + if (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->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(&mvf, fc, &mmvd_offset); + mv_merge_refine_pred_flag(&mvf, cb_width, cb_height); + ff_vvc_store_mvf(lc, &mvf); + mvf_to_mi(&mvf, mi); + } else { + + if (ciip_avaiable && gpm_avaiable) { + cu->ciip_flag = ff_vvc_ciip_flag(lc); + } else { + cu->ciip_flag = sps->ciip_enabled_flag && !cu->skip_flag && + !is_128 && (cb_width * cb_height >= 64); + } + if (cu->ciip_flag && sps->max_num_merge_cand > 1) { + merge_idx = ff_vvc_merge_idx(lc); + } + if (!cu->ciip_flag) { + 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); + } else { + ff_vvc_luma_mv_merge_mode(lc, merge_idx, 1, &mvf); + mv_merge_refine_pred_flag(&mvf, cb_width, 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; + } + } + } + } + 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->wp_info_in_ph_flag ? &ph->pwt : &sh->pwt; + int bcw_idx = 0; + + if (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(fc)); + } + return bcw_idx; +} + +static int8_t ref_idx_decode(VVCLocalContext *lc, const VVCSH *sh, const int sym_mvd_flag, const int lx) +{ + int ref_idx = 0; + if (sh->nb_refs[lx] > 1 && !sym_mvd_flag) + ref_idx = ff_vvc_ref_idx_lx(lc, sh->nb_refs[lx]); + else if (sym_mvd_flag) + ref_idx = sh->ref_idx_sym[lx]; + return ref_idx; +} + +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; + 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 i, 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(sh)); + if (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->six_param_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->smvd_enabled_flag && !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); + if (i == L1 && ph->mvd_l1_zero_flag && mi->pred_flag == PF_BI) { + for (int j = 0; j < num_cp_mv; j++) + AV_ZERO64(&mvds[i][j]); + } else { + Mv *mvd0 = &mvds[i][0]; + if (i == 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[i][j]; + hls_mvd_coding(lc, mvd); + mvd->x += mvd0->x; + mvd->y += mvd0->y; + has_no_zero_mvd |= (mvd->x || mvd->y); + } + } + mvp_lx_flag[i] = ff_vvc_mvp_lx_flag(lc); + } + } + + amvr_enabled = mi->motion_model_idc == MOTION_TRANSLATION ? + sps->amvr_enabled_flag : 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); + + for (i = 0; i < 2; i++) { + const PredFlag lx = i + 1; + if (mi->pred_flag & lx) { + 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; + } + } + } + if (mi->motion_model_idc) + ff_vvc_store_sb_mvs(lc, pu); + else + ff_vvc_store_mv(lc, &pu->mi); + + return 0; +} + +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) + 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 VVCSH *sh = &lc->sc->sh; + 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(sh) && 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->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->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->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->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 VVCSH *sh = &lc->sc->sh; + const VVCSPS *sps = lc->fc->ps.sps; + const int area = cb_width * cb_height; + + if ((IS_I(sh) && sps->qtbtt_dual_tree_intra_flag) || + mode_type_curr != MODE_TYPE_ALL || !sps->chroma_format_idc || + 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->chroma_format_idc == CHROMA_FORMAT_420) || + (area == 128 && (split == SPLIT_TT_HOR || split == SPLIT_TT_VER) && 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(sh); + + 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 int ch_type = tree_type_curr == DUAL_TREE_CHROMA; + int ret; + VVCAllowedSplit allowed; + + if (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 (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 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->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 (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 VVCSH *sh = &lc->sc->sh; + 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 (sh->sao_used_flag[0] || sh->sao_used_flag[1]) { + 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->chroma_format_idc ? 3 : 1); c_idx++) { + if (!sh->sao_used_flag[c_idx]) { + 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 VVCSH *sh = &lc->sc->sh; + ALFParams *alf = &CTB(fc->tab.alf, rx, ry); + + alf->ctb_flag[LUMA] = alf->ctb_flag[CB] = alf->ctb_flag[CR] = 0; + if (sh->alf.enabled_flag[LUMA]) { + alf->ctb_flag[LUMA] = ff_vvc_alf_ctb_flag(lc, rx, ry, LUMA); + if (alf->ctb_flag[LUMA]) { + int alf_use_aps_flag = 0; + if (sh->alf.num_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->alf.num_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++) { + if (sh->alf.enabled_flag[c_idx]) { + const VVCALF *aps = (VVCALF*)fc->ps.alf_list[sh->alf.aps_id_chroma]->data; + 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); + } + } + } + for (int i = 0; i < 2; i++) { + alf->ctb_cc_idc[i] = 0; + if (sh->alf.cc_enabled_flag[i]) { + const VVCALF *aps = (VVCALF*)fc->ps.alf_list[sh->alf.cc_aps_id[i]]->data; + alf->ctb_cc_idc[i] = ff_vvc_alf_ctb_cc_idc(lc, rx, ry, i, aps->cc_filters_signalled[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, int x0, int y0) +{ + int ret = 0; + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCSH *sh = &lc->sc->sh; + const unsigned int ctb_size = sps->ctb_size_y; + + 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(sh) && 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; + + return 0; +} + +int ff_vvc_coding_tree_unit(VVCLocalContext *lc, const int ctb_addr, 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 VVCSH *sh = &lc->sc->sh; + 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] || !ctb_addr; + } + + lc->coeffs = fc->tab.coeffs + (ry * pps->ctb_width + rx) * ctb_size * VVC_MAX_SAMPLE_ARRAYS; + + ff_vvc_cabac_init(lc, ctb_addr, rx, ry); + fc->tab.slice_idx[rs] = lc->sc->slice_idx; + ff_vvc_decode_neighbour(lc, x_ctb, y_ctb, rx, ry, rs); + ret = hls_coding_tree_unit(lc, x_ctb, y_ctb); + if (ret < 0) + return ret; + + if (rx == pps->ctb_to_col_bd[rx + 1] - 1) { + if (ctb_addr == 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->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; +} + void ff_vvc_decode_neighbour(VVCLocalContext *lc, const int x_ctb, const int y_ctb, const int rx, const int ry, const int rs) { @@ -79,3 +2191,12 @@ void ff_vvc_ctu_free_cus(CTU *ctu) av_buffer_unref(&buf); } } + +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]; +} + diff --git a/libavcodec/vvc/vvc_ctu.h b/libavcodec/vvc/vvc_ctu.h index e491933f85..e761624b3d 100644 --- a/libavcodec/vvc/vvc_ctu.h +++ b/libavcodec/vvc/vvc_ctu.h @@ -386,6 +386,17 @@ struct ALFParams { uint8_t applied[3]; }; +/** + * 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 ctb_addr, 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 May 21 13:03: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: 41771 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:ba91:b0:105:feb:71f2 with SMTP id fb17csp1091850pzb; Sun, 21 May 2023 06:06:17 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ4+5scSeqpefXrLN5K+uzWwBgMeNK5/3OPPY3h3+ptDUHKkBIc4xYXpGUTOGxEcs8T25xIi X-Received: by 2002:a05:6402:148d:b0:504:80d8:a034 with SMTP id e13-20020a056402148d00b0050480d8a034mr5452300edv.40.1684674377292; Sun, 21 May 2023 06:06:17 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684674377; cv=none; d=google.com; s=arc-20160816; b=YD7JnmeWS4w7PgekaNn7Jk5ovXwSE7+RstxEYOks5vfFOl75gonOUTun6EyHhWbp9W YwyiLeVHuoXanhoH3TOgZfh2gXa6ORQyaGj0wbeSx4FVp9Kfua/NbMD5OldeikQBtO75 IXKJ5Uudb0RMgsqaezl99Zlt9CW0fTWvtSk9YIhBT/dkWEfPnrP4JTLDA5vjPt10k2b6 G5GVHWToBsvpUmEw6PcShSS7f/yw2XOAZXusiA0IwV7sRBubavO4sz2OSFiuPTpjnORF maI2rm6eKxBQIXDhcOMwy/qyjyJ8peIQdjR+jCTbDXbrVhbE6X6wyAvg8miabym/aP9o MPPQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=9VjbcVcDuL7Q4y4ziFITFjPr6h2cZNrQ87EL2KXhHIM=; b=IWd84VvxUkSsx17xwy5AbcwwRi5PxYnd2yaA/HIE0NmkB7pySGRfFfKgsw1GXNsbJL CcF/DubAU05V8N6XMQ6g1YJ1ixJxXajF5Iyio+PqbEUOqCRXDOmAcxKU8HMn1PGSWxFu zOfcmVFnoZHr3fcGqiVEAA5H/eZN5DUEPGhs4QDq9QrM4+tE9+97sXVCTVs0fpetQ0aH 2ukN0bn0u+9pui3VegqNO5K1vdP1EejCPioLB1t6XHuF+/kDYbn524srfrYL7GLTp/Ob 5YAvbCn2jeL2PHejny2sGjZyvw9Lbh4Iphbk4KJ01e4qvOUJar2FkbWboAaRQhDa6YdK 9PLg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20221208 header.b="D+/scTgb"; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id d9-20020a056402516900b00510e0156b13si1308848ede.505.2023.05.21.06.06.16; Sun, 21 May 2023 06:06:17 -0700 (PDT) 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=@gmail.com header.s=20221208 header.b="D+/scTgb"; 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 01C6868C19B; Sun, 21 May 2023 16:03:57 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f173.google.com (mail-pf1-f173.google.com [209.85.210.173]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id EA8D668C18B for ; Sun, 21 May 2023 16:03:54 +0300 (EEST) Received: by mail-pf1-f173.google.com with SMTP id d2e1a72fcca58-64d577071a6so911663b3a.1 for ; Sun, 21 May 2023 06:03:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1684674232; x=1687266232; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=5VrDUjLIM+UQN7k2NJmatYuVCNnsJKkHq7LTqjKao2E=; b=D+/scTgbbtvyhFEic/6RC1/TrMtXdhtZu4qqzZbiNc+iWFlU67xnhF3+BpjFBJ9Ju0 06yQcxckrzhFmBRIbT6/06BRSy3lO8xwV5e0bGyy/zTB7tbzWWyy5/QzwJ5LZl+ttaPP B+MFAH6lJQc1qbtgvAyj6IQp92yk89lxVJSZvyebWKyzWW8kOvZ4xjqTgmu8PPsSyYzc h4nTork5XpdM/z9atOr8PuUH6GpEx7e0smyj7z6uJ/RB7Nif4ApJS/1Pl1TF6zoeixPI ksJSbzUFQzXMd31eDWMASU89byApKf1WQd4a45woQcvl10Eew/R5aNcM50DjvWzVjLd6 4Hgw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684674232; x=1687266232; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=5VrDUjLIM+UQN7k2NJmatYuVCNnsJKkHq7LTqjKao2E=; b=dg7L7uPMXmdJxbT00iY/zIPcFNfAKk3Bub6Q9aLVlk3t8qwh9N4ABMkx6JjogMA35C CIKRs5ctKlZsseLoIYRU8e8g6rcCUJ/APh0W+wd4Bxdb3fAuxKYQDsztcCtDGFGdZapo GOWFEegn5L0+K/q9uM+VLwYiod1VSF4rz6La1j8EZmfpXsxR7JSuijUDJOx/9UFe1RMD 0nIEAA7s8HAPQpixkhUiM1OWCpKt21BvdFMoggenIg1Ifwcl2uLBtZuoFR3Ui5Cq+VUw HQQGhJ45UvKrCBxLn/njPkz3WfrGdq9zW8puYnI8L1r/WeEaLX/vyDgzkS+JnTDlDtBR 6Zag== X-Gm-Message-State: AC+VfDwOhpL+td4btNWWmLU4DIWDbSWKAVOregUW9qdYSBv+rgntTcul n2c7OrrCKSrMxYS5l91YrwfHMZkGDEgs2A== X-Received: by 2002:a05:6a00:804:b0:645:b13e:e674 with SMTP id m4-20020a056a00080400b00645b13ee674mr10692447pfk.26.1684674232132; Sun, 21 May 2023 06:03:52 -0700 (PDT) Received: from NuoMi.localdomain ([112.64.8.97]) by smtp.gmail.com with ESMTPSA id t9-20020aa79389000000b0064d45eea573sm1671872pfe.41.2023.05.21.06.03.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 21 May 2023 06:03:51 -0700 (PDT) From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 21 May 2023 21:03:18 +0800 Message-Id: <20230521130319.13813-14-nuomi2021@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230521130319.13813-1-nuomi2021@gmail.com> References: <20230521130319.13813-1-nuomi2021@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 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: aoeKaNqJPbdM 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 | 3 +- libavcodec/vvc/vvc_thread.c | 761 ++++++++++++++++++++++++++++++++++++ libavcodec/vvc/vvc_thread.h | 72 ++++ 3 files changed, 835 insertions(+), 1 deletion(-) 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 0f5c3daab0..df8462328f 100644 --- a/libavcodec/vvc/Makefile +++ b/libavcodec/vvc/Makefile @@ -13,4 +13,5 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ vvc/vvc_itx_1d.o \ vvc/vvc_intra.o \ vvc/vvc_filter.o \ - vvc/vvcdsp.o + vvc/vvcdsp.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..e1dbc7f0e6 --- /dev/null +++ b/libavcodec/vvc/vvc_thread.c @@ -0,0 +1,761 @@ +/* + * 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/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 VVCRowThread { + VVCTask reconstruct_task; + VVCTask deblock_v_task; + VVCTask sao_task; + atomic_int alf_progress; +} VVCRowThread; + +typedef struct VVCColThread { + VVCTask deblock_h_task; +} VVCColThread; + +struct VVCFrameThread { + // error return for tasks + atomic_int ret; + + atomic_uchar *avails; + + VVCRowThread *rows; + VVCColThread *cols; + VVCTask *tasks; + + int ctu_size; + int ctu_width; + int ctu_height; + int ctu_count; + + //protected by lock + int nb_scheduled_tasks; + int nb_parse_tasks; + int alf_row_progress; + + pthread_mutex_t lock; + pthread_cond_t cond; +}; + +static int get_avail(const VVCFrameThread *ft, const int rx, const int ry, const VVCTaskType type) +{ + atomic_uchar *avail; + if (rx < 0 || ry < 0) + return 1; + avail = ft->avails + FFMIN(ry, ft->ctu_height - 1)* ft->ctu_width + FFMIN(rx, ft->ctu_width - 1); + return atomic_load(avail) & (1 << type); +} + +static void set_avail(const VVCFrameThread *ft, const int rx, const int ry, const VVCTaskType type) +{ + atomic_uchar *avail = ft->avails + ry * ft->ctu_width + rx; + if (rx < 0 || rx >= ft->ctu_width || ry < 0 || ry >= ft->ctu_height) + return; + atomic_fetch_or(avail, 1 << type); +} + +static void vvc_task_init(VVCTask *task, VVCTaskType type, VVCFrameContext *fc) +{ + memset(task, 0, sizeof(*task)); + task->type = type; + task->fc = fc; + task->decode_order = fc->decode_order; +} + +void ff_vvc_parse_task_init(VVCTask *t, VVCTaskType type, VVCFrameContext *fc, + SliceContext *sc, EntryPoint *ep, const int ctu_addr) +{ + const VVCFrameThread *ft = fc->frame_thread; + const int rs = sc->sh.ctb_addr_in_curr_slice[ctu_addr]; + + vvc_task_init(t, type, fc); + t->sc = sc; + t->ep = ep; + t->ctb_addr_in_slice = ctu_addr; + t->rx = rs % ft->ctu_width; + t->ry = rs / ft->ctu_width; +} + +VVCTask* ff_vvc_task_alloc(void) +{ + return av_malloc(sizeof(VVCTask)); +} + +static int is_parse_ready(const VVCFrameContext *fc, const VVCTask *t) +{ + av_assert0(t->type == VVC_TASK_TYPE_PARSE); + if (fc->ps.sps->entropy_coding_sync_enabled_flag && t->ry != fc->ps.pps->ctb_to_row_bd[t->ry]) + return get_avail(fc->frame_thread, t->rx, t->ry - 1, VVC_TASK_TYPE_PARSE); + return 1; +} + +static int is_inter_ready(const VVCFrameContext *fc, const VVCTask *t) +{ + av_assert0(t->type == VVC_TASK_TYPE_INTER); + return 1; +} + +static int is_recon_ready(const VVCFrameContext *fc, const VVCTask *t) +{ + const VVCFrameThread *ft = fc->frame_thread; + + av_assert0(t->type == VVC_TASK_TYPE_RECON); + return get_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_INTER) && + get_avail(ft, t->rx + 1, t->ry - 1, VVC_TASK_TYPE_RECON) && + get_avail(ft, t->rx - 1, t->ry, VVC_TASK_TYPE_RECON); +} + +static int is_lmcs_ready(const VVCFrameContext *fc, const VVCTask *t) +{ + const VVCFrameThread *ft = fc->frame_thread; + + av_assert0(t->type == VVC_TASK_TYPE_LMCS); + return get_avail(ft, t->rx + 1, t->ry + 1, VVC_TASK_TYPE_RECON) && + get_avail(ft, t->rx, t->ry + 1, VVC_TASK_TYPE_RECON) && + get_avail(ft, t->rx + 1, t->ry, VVC_TASK_TYPE_RECON); +} + +static int is_deblock_v_ready(const VVCFrameContext *fc, const VVCTask *t) +{ + const VVCFrameThread *ft = fc->frame_thread; + + av_assert0(t->type == VVC_TASK_TYPE_DEBLOCK_V); + return get_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_LMCS) && + get_avail(ft, t->rx + 1, t->ry, VVC_TASK_TYPE_LMCS); +} + +static int is_deblock_h_ready(const VVCFrameContext *fc, const VVCTask *t) +{ + const VVCFrameThread *ft = fc->frame_thread; + + av_assert0(t->type == VVC_TASK_TYPE_DEBLOCK_H); + return get_avail(ft, t->rx - 1, t->ry, VVC_TASK_TYPE_DEBLOCK_H) && + get_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_DEBLOCK_V); +} + +static int is_sao_ready(const VVCFrameContext *fc, const VVCTask *t) +{ + av_assert0(t->type == VVC_TASK_TYPE_SAO); + return get_avail(fc->frame_thread, t->rx + 1, t->ry - 1, VVC_TASK_TYPE_SAO) && + get_avail(fc->frame_thread, t->rx - 1, t->ry + 1, VVC_TASK_TYPE_DEBLOCK_H) && + get_avail(fc->frame_thread, t->rx, t->ry + 1, VVC_TASK_TYPE_DEBLOCK_H) && + get_avail(fc->frame_thread, t->rx + 1, t->ry + 1, VVC_TASK_TYPE_DEBLOCK_H); +} + +static int is_alf_ready(const VVCFrameContext *fc, const VVCTask *t) +{ + av_assert0(t->type == VVC_TASK_TYPE_ALF); + return 1; +} + +typedef int (*is_ready_func)(const VVCFrameContext *fc, const VVCTask *t); + +int ff_vvc_task_ready(const VVCTasklet *_t, void *user_data) +{ + const VVCTask *t = (const VVCTask*)_t; + const VVCFrameThread *ft = t->fc->frame_thread; + int ready; + is_ready_func is_ready[] = { + is_parse_ready, + is_inter_ready, + is_recon_ready, + is_lmcs_ready, + is_deblock_v_ready, + is_deblock_h_ready, + is_sao_ready, + is_alf_ready, + }; + + if (atomic_load(&ft->ret)) + return 1; + ready = is_ready[t->type](t->fc, t); + + return ready; +} + +int ff_vvc_task_priority_higher(const VVCTasklet *_a, const VVCTasklet *_b) +{ + const VVCTask *a = (const VVCTask*)_a; + const VVCTask *b = (const VVCTask*)_b; + //order by frame decoder order + if (a->decode_order != b->decode_order) + return a->decode_order < b->decode_order; + + if (a->type == b->type) { + // order by y + if (a->ry != b->ry) + return a->ry < b->ry; + // order by x + return a->rx < b->rx; + } + if (a->type != VVC_TASK_TYPE_PARSE && b->type != VVC_TASK_TYPE_PARSE) { + // order by y + if (a->ry != b->ry) + return a->ry < b->ry; + // order by x + if (a->rx != b->rx) + return a->rx < b->rx; + } + // order by type + return a->type < b->type; +} + +static void add_task(VVCContext *s, VVCTask *t, const VVCTaskType type) +{ + t->type = type; + ff_vvc_frame_add_task(s, t); +} + +static int run_parse(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + SliceContext *sc = t->sc; + const VVCSH *sh = &sc->sh; + EntryPoint *ep = t->ep; + VVCFrameThread *ft = fc->frame_thread; + int ret, rs, prev_ry; + + lc->sc = sc; + lc->ep = ep; + + //reconstruct one line a time + rs = sh->ctb_addr_in_curr_slice[t->ctb_addr_in_slice]; + do { + + prev_ry = t->ry; + + ret = ff_vvc_coding_tree_unit(lc, t->ctb_addr_in_slice, rs, t->rx, t->ry); + if (ret < 0) + return ret; + + set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_PARSE); + add_task(s, ft->tasks + rs, VVC_TASK_TYPE_INTER); + + if (fc->ps.sps->entropy_coding_sync_enabled_flag && t->rx == pps->ctb_to_col_bd[t->rx]) { + EntryPoint *next = ep + 1; + if (next < sc->eps + sc->nb_eps) { + memcpy(next->cabac_state, ep->cabac_state, sizeof(next->cabac_state)); + av_assert0(!next->parse_task->type); + ff_vvc_frame_add_task(s, next->parse_task); + } + } + + t->ctb_addr_in_slice++; + if (t->ctb_addr_in_slice >= ep->ctu_addr_last) + break; + + rs = sh->ctb_addr_in_curr_slice[t->ctb_addr_in_slice]; + t->rx = rs % ft->ctu_width; + t->ry = rs / ft->ctu_width; + } while (t->ry == prev_ry && is_parse_ready(fc, t)); + + if (t->ctb_addr_in_slice < ep->ctu_addr_last) + ff_vvc_frame_add_task(s, t); + + return 0; +} + +static int run_inter(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->frame_thread; + 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); + if (!t->rx) + ff_vvc_frame_add_task(s, &ft->rows[t->ry].reconstruct_task); + } + set_avail(ft, t->rx, 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->frame_thread; + + do { + 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); + } + + set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_RECON); + add_task(s, ft->tasks + rs, VVC_TASK_TYPE_LMCS); + + t->rx++; + } while (t->rx < ft->ctu_width && is_recon_ready(fc, t)); + + if (t->rx < ft->ctu_width) + ff_vvc_frame_add_task(s, t); + return 0; +} + +static int run_lmcs(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->frame_thread; + 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); + } + set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_LMCS); + if (!t->rx) + add_task(s, &ft->rows[t->ry].deblock_v_task, VVC_TASK_TYPE_DEBLOCK_V); + + return 0; +} + +static int run_deblock_v(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->frame_thread; + int rs = t->ry * ft->ctu_width + t->rx; + const int ctb_size = ft->ctu_size; + + do { + 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.deblocking_filter_disabled_flag) { + ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs); + ff_vvc_deblock_vertical(lc, x0, y0); + } + } + + set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_DEBLOCK_V); + + if (!t->ry) + add_task(s, &ft->cols[t->rx].deblock_h_task , VVC_TASK_TYPE_DEBLOCK_H); + + t->rx++; + rs++; + } while (t->rx < ft->ctu_width && is_deblock_v_ready(fc, t)); + + if (t->rx < ft->ctu_width) + ff_vvc_frame_add_task(s, t); + + return 0; +} + +static int run_deblock_h(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->frame_thread; + const int ctb_size = ft->ctu_size; + int rs = t->ry * ft->ctu_width + t->rx; + + do { + 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.deblocking_filter_disabled_flag) { + ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs); + ff_vvc_deblock_horizontal(lc, x0, y0); + } + } + + set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_DEBLOCK_H); + + if (!t->rx) + add_task(s, &ft->rows[t->ry].sao_task, VVC_TASK_TYPE_SAO); + + rs += ft->ctu_width; + t->ry++; + } while (t->ry < ft->ctu_height && is_deblock_h_ready(fc, t)); + + if (t->ry < ft->ctu_height) + ff_vvc_frame_add_task(s, t); + + return 0; +} + +static void add_alf_tasks(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->frame_thread; + VVCTask *at = ft->tasks + ft->ctu_width * t->ry + t->rx; + if (t->ry > 0) { + VVCTask *top = at - ft->ctu_width; + if (t->rx > 0) + add_task(s, top - 1, VVC_TASK_TYPE_ALF); + if (t->rx == ft->ctu_width - 1) + add_task(s, top, VVC_TASK_TYPE_ALF); + } + if (t->ry == ft->ctu_height - 1) { + if (t->rx > 0) + add_task(s, at - 1, VVC_TASK_TYPE_ALF); + if (t->rx == ft->ctu_width - 1) + add_task(s, at, VVC_TASK_TYPE_ALF); + } + +} + +static int run_sao(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->frame_thread; + int rs = t->ry * fc->ps.pps->ctb_width + t->rx; + const int ctb_size = ft->ctu_size; + + do { + const int x0 = t->rx * ctb_size; + const int y0 = t->ry * ctb_size; + + if (fc->ps.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->alf_enabled_flag) + ff_vvc_alf_copy_ctu_to_hv(lc, x0, y0); + + set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_SAO); + + add_alf_tasks(s, lc, t); + + rs++; + t->rx++; + } while (t->rx < ft->ctu_width && is_sao_ready(fc, t)); + + if (t->rx < ft->ctu_width) + ff_vvc_frame_add_task(s, t); + + return 0; +} + +static void report_frame_progress(VVCFrameContext *fc, VVCTask *t) +{ + VVCFrameThread *ft = fc->frame_thread; + const int ctu_size = ft->ctu_size; + int old; + + if (atomic_fetch_add(&ft->rows[t->ry].alf_progress, 1) == ft->ctu_width - 1) { + pthread_mutex_lock(&ft->lock); + old = ft->alf_row_progress; + while (ft->alf_row_progress < ft->ctu_height && atomic_load(&ft->rows[ft->alf_row_progress].alf_progress) == ft->ctu_width) + ft->alf_row_progress++; + if (old != ft->alf_row_progress) { + const int progress = ft->alf_row_progress == ft->ctu_height ? INT_MAX : ft->alf_row_progress * ctu_size; + ff_vvc_report_progress(fc->ref, progress); + } + pthread_mutex_unlock(&ft->lock); + } +} + +static int run_alf(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->frame_thread; + 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->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); + } + } + ff_vvc_ctu_apply_dmvr_info(fc, x0, y0); + set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_ALF); + report_frame_progress(fc, t); + + return 0; +} + +static void finished_one_task(VVCFrameThread *ft, const VVCTaskType type) +{ + pthread_mutex_lock(&ft->lock); + + av_assert0(ft->nb_scheduled_tasks); + ft->nb_scheduled_tasks--; + + if (type == VVC_TASK_TYPE_PARSE) { + av_assert0(ft->nb_parse_tasks); + ft->nb_parse_tasks--; + } + pthread_cond_broadcast(&ft->cond); + + pthread_mutex_unlock(&ft->lock); +} + + +#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); + +int ff_vvc_task_run(VVCTasklet *_t, void *local_context, void *user_data) +{ + VVCTask *t = (VVCTask*)_t; + VVCContext *s = (VVCContext *)user_data; + VVCLocalContext *lc = local_context; + VVCFrameThread *ft = t->fc->frame_thread; + const VVCTaskType type = t->type; + 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[t->type], t->rx, t->ry); +#endif + + if (!atomic_load(&ft->ret)) { + if ((ret = run[t->type](s, lc, t)) < 0) { +#ifdef WIN32 + intptr_t zero = 0; +#else + int zero = 0; +#endif + atomic_compare_exchange_strong(&ft->ret, &zero, ret); + } + } + + // t->type may changed by run(), we use a local copy of t->type + finished_one_task(ft, type); + + return ret; +} + +void ff_vvc_frame_thread_free(VVCFrameContext *fc) +{ + VVCFrameThread *ft = fc->frame_thread; + + if (!ft) + return; + + pthread_mutex_destroy(&ft->lock); + pthread_cond_destroy(&ft->cond); + av_freep(&ft->avails); + av_freep(&ft->cols); + av_freep(&ft->rows); + av_freep(&ft->tasks); + av_freep(&ft); +} + +int ff_vvc_frame_thread_init(VVCFrameContext *fc) +{ + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + VVCFrameThread *ft = fc->frame_thread; + 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->frame_thread)); + 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; + + for (int y = 0; y < ft->ctu_height; y++) { + VVCRowThread *row = ft->rows + y; + vvc_task_init(&row->deblock_v_task, VVC_TASK_TYPE_DEBLOCK_V, fc); + row->deblock_v_task.ry = y; + vvc_task_init(&row->sao_task, VVC_TASK_TYPE_SAO, fc); + row->sao_task.ry = y; + vvc_task_init(&row->reconstruct_task, VVC_TASK_TYPE_RECON, fc); + row->reconstruct_task.ry = y; + } + + ft->cols = av_calloc(ft->ctu_width, sizeof(*ft->cols)); + if (!ft->cols) + goto fail; + for (int x = 0; x < ft->ctu_width; x++) { + VVCColThread *col = ft->cols + x; + vvc_task_init(&col->deblock_h_task, VVC_TASK_TYPE_DEBLOCK_H, fc); + col->deblock_h_task.rx = x; + } + + ft->avails = av_calloc(ft->ctu_count, sizeof(*ft->avails)); + if (!ft->avails) + goto fail; + + ft->tasks = av_calloc(ft->ctu_count, sizeof(*ft->tasks)); + if (!ft->tasks) + goto fail; + for (int rs = 0; rs < ft->ctu_count; rs++) { + VVCTask *t = ft->tasks + rs; + t->rx = rs % ft->ctu_width; + t->ry = rs / ft->ctu_width; + t->fc = fc; + } + + if ((ret = pthread_cond_init(&ft->cond, NULL))) + goto fail; + + if ((ret = pthread_mutex_init(&ft->lock, NULL))) { + pthread_cond_destroy(&ft->cond); + goto fail; + } + } + + ft->ret = 0; + for (int y = 0; y < ft->ctu_height; y++) { + VVCRowThread *row = ft->rows + y; + + row->reconstruct_task.rx = 0; + row->alf_progress = 0; + row->deblock_v_task.rx = 0; + row->sao_task.rx = 0; + } + + for (int x = 0; x < ft->ctu_width; x++) { + VVCColThread *col = ft->cols + x; + col->deblock_h_task.ry = 0; + } + + for (int rs = 0; rs < ft->ctu_count; rs++) { + ft->avails[rs] = 0; + ft->tasks[rs].decode_order = fc->decode_order; + } + + ft->alf_row_progress = 0; + fc->frame_thread = ft; + + return 0; + +fail: + if (ft) { + av_freep(&ft->avails); + av_freep(&ft->cols); + av_freep(&ft->rows); + av_freep(&ft->tasks); + av_freep(&ft); + } + + return AVERROR(ENOMEM); +} + +void ff_vvc_frame_add_task(VVCContext *s, VVCTask *t) +{ + VVCFrameContext *fc = t->fc; + VVCFrameThread *ft = fc->frame_thread; + + pthread_mutex_lock(&ft->lock); + + ft->nb_scheduled_tasks++; + if (t->type == VVC_TASK_TYPE_PARSE) + ft->nb_parse_tasks++; + + pthread_mutex_unlock(&ft->lock); + + ff_vvc_executor_execute(s->executor, &t->task); +} + +int ff_vvc_frame_wait(VVCContext *s, VVCFrameContext *fc) +{ + VVCFrameThread *ft = fc->frame_thread; + int check_missed_slices = 1; + + pthread_mutex_lock(&ft->lock); + + while (ft->nb_scheduled_tasks) { + if (check_missed_slices && !ft->nb_parse_tasks) { + // abort for missed slices + for (int rs = 0; rs < ft->ctu_count; rs++){ + atomic_uchar mask = 1 << VVC_TASK_TYPE_PARSE; + if (!(atomic_load(ft->avails + rs) & mask)) { + atomic_store(&ft->ret, AVERROR_INVALIDDATA); + // maybe all thread are waiting, let us wake up them + ff_vvc_executor_wakeup(s->executor); + break; + } + } + check_missed_slices = 0; + } + pthread_cond_wait(&ft->cond, &ft->lock); + } + + pthread_mutex_unlock(&ft->lock); + ff_vvc_report_progress(fc->ref, INT_MAX); + +#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..f2bfb7fb93 --- /dev/null +++ b/libavcodec/vvc/vvc_thread.h @@ -0,0 +1,72 @@ +/* + * 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" + +typedef enum VVCTaskType { + 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; + +struct VVCTask { + union { + VVCTask *next; //for executor debug only + VVCTasklet task; + }; + + VVCTaskType type; + uint64_t decode_order; + + // ctu x, y in raster order + int rx, ry; + VVCFrameContext *fc; + + // reconstruct task only + SliceContext *sc; + EntryPoint *ep; + int ctb_addr_in_slice; +}; + +void ff_vvc_parse_task_init(VVCTask *task, VVCTaskType type, VVCFrameContext *fc, + SliceContext *sc, EntryPoint *ep, int ctu_addr); +VVCTask* ff_vvc_task_alloc(void); + +int ff_vvc_task_ready(const VVCTasklet* t, void* user_data); +int ff_vvc_task_priority_higher(const VVCTasklet *a, const VVCTasklet *b); +int ff_vvc_task_run(VVCTasklet *t, void *local_context, void *user_data); + +int ff_vvc_frame_thread_init(VVCFrameContext *fc); +void ff_vvc_frame_thread_free(VVCFrameContext *fc); +void ff_vvc_frame_add_task(VVCContext *s, VVCTask *t); +int ff_vvc_frame_wait(VVCContext *s, VVCFrameContext *fc); + +#endif // AVCODEC_VVC_THREAD_H From patchwork Sun May 21 13:03: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: 41772 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:ba91:b0:105:feb:71f2 with SMTP id fb17csp1091960pzb; Sun, 21 May 2023 06:06:27 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ57hDk8UoU7Lqlf0Ku0QeaEZRXIl1IYizyi8z66zByIu5X3Nm+qwZD1G4haRK6n9fbqHM6F X-Received: by 2002:a17:907:6094:b0:96f:af50:9884 with SMTP id ht20-20020a170907609400b0096faf509884mr3525240ejc.11.1684674387625; Sun, 21 May 2023 06:06:27 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684674387; cv=none; d=google.com; s=arc-20160816; b=q6W2BcTpRFxZ1f68hlqD7lTKnaqKFI6csZMzSgfu3aj6R/wo6X04+wiBtm/MaEk+ZR OZeNM/TjecNg+edXONXvInkBNeJkN+uLqvP/t64yB27QpbgUkGWQ+hj6UEUIuXQtW4W6 v8TpQMGvmND5PhkuXlQvGtnK6I8tRFPjLT9UkrYdd2hnEk2oP+1L3nE/ZiHljU9A+NK7 R0VQ+ndnXN+GibAWk+hokAkNbDAu+1j4N49Wnsimve9jFUD1xVRbC8lSbbuCsKHyZxu/ YVoCl3lpu4MgRc6bBVFfz3HwrFk9lhXK80FgmDSW6m36v8TujVpkcMy8vQyTmPJ6icnm N0xw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=04kZOIrjAFlm6akAi9NxIeS53zY8WQWPQlyqez3I/HE=; b=BvCnq/RTidB0XTaHgmVqzsaGh5r/lZoO48hrzioDDf/N6MLVEzq3BH1P7FxFstaU2F kdjbdOcpeOpAxWNsTMCV8hVW3Lpu8bgzLHSozxib8DheYi3qmNtIg3X+mSvDeNm/XDUZ 9kGiV7+q2HCliplIhHZlL5Q2NlMrryHwDVFD8Hp3fOdH/xYdWKtcm5Aq/Kc/rfTQ5m1d gQzsjrgMzqC/mxFSDEk5BM7Iadw6BcqZ39j6zuD5oHSP3GqAGPvYQ8Dh9rEk1pA4sYf0 e5lCESiIxIoxGpbyYmie+Gf+oPaG5RoZfVnBbgLlO5b60/Ggd49xbp0k17A6CDpq7HUA SDaw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20221208 header.b=mRFUfgyP; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id w18-20020aa7cb52000000b00506bce1794esi2736102edt.526.2023.05.21.06.06.27; Sun, 21 May 2023 06:06:27 -0700 (PDT) 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=@gmail.com header.s=20221208 header.b=mRFUfgyP; 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 E716068C1A9; Sun, 21 May 2023 16:03:58 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f182.google.com (mail-pf1-f182.google.com [209.85.210.182]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 89E9E68C1A2 for ; Sun, 21 May 2023 16:03:56 +0300 (EEST) Received: by mail-pf1-f182.google.com with SMTP id d2e1a72fcca58-64d604cc0aaso283992b3a.2 for ; Sun, 21 May 2023 06:03:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1684674234; x=1687266234; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=UojW0U1RtrlPBLkxH0Dm4BeYtnEppDP++t494TmpM/k=; b=mRFUfgyP6mgRLFyW0XVVaIIjCVQtfq0yuTfU2ATT/JdCW0KHaGvJtpYTIWGDtkCimm DiucPRvSECVmzvKCb4q57wMmotPOnCeJ0XbSbw9p9pOHARZJQzfYxmY4Kn9hx4NLeyEb isLz9/ncF64lIIF7DMceo1nWoIp73mGWOyRYYG3SRw9tgAvSUrPAep3gtbQZb530rOSs 6mL3uJkoMTsKc/HwchjSmJH8A5by/dINAdYXP8W2fSybIdIsGPnwblDbex2J3rGiTS1e +ADPl3zl8pnS1WtwpwfGR0/k1Qekhea59Ilw7pUkNIvca70qWTtQcCbLv1vjZNitLvel g4Cg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684674234; x=1687266234; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=UojW0U1RtrlPBLkxH0Dm4BeYtnEppDP++t494TmpM/k=; b=YYnFmW07SXADR+pq/P5glUiERrxubZrrUz3JeMujYdtvf6OePsHYS1+jQj0n/WXTYB 5RDSESTTe8MseIbHnzpT6wLV+XNKmMISAO18GRCKfpWl2C/hA9bskZIhZYMTv0jwtF7z F5HrVw6eZxdcsSUaqCTftWjCSpm574lRx/JUQM9FdKFNRrePCSZi7R0liQpBHTogvt5O GryR4oBHNCVnbJ8o86SvpzAneXD9O6YX/sJsxk7pp75bJG6kTwJ9owqNaVhhCBYI1Rce 8Cco+Q3gQAZ+tbxDYb5yrjgxoFPWCGm2moIWpWmbTWd7mCXdtjXP1L8524fxqWz5IUJT Hg9g== X-Gm-Message-State: AC+VfDyRkWQYydiry8CAZPq/HADX/ebACH1AG2cGewQa8UnWFXuATGIR Hj9KNSak5kOjAGIO4VpTfViGlR0g8w5Ysg== X-Received: by 2002:a05:6a00:24ca:b0:64a:ede1:13fb with SMTP id d10-20020a056a0024ca00b0064aede113fbmr11664395pfv.9.1684674234022; Sun, 21 May 2023 06:03:54 -0700 (PDT) Received: from NuoMi.localdomain ([112.64.8.97]) by smtp.gmail.com with ESMTPSA id t9-20020aa79389000000b0064d45eea573sm1671872pfe.41.2023.05.21.06.03.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 21 May 2023 06:03:53 -0700 (PDT) From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Sun, 21 May 2023 21:03:19 +0800 Message-Id: <20230521130319.13813-15-nuomi2021@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230521130319.13813-1-nuomi2021@gmail.com> References: <20230521130319.13813-1-nuomi2021@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 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: rEto3KTjkJu6 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: + C code only + Support I, P, B frames, 8/10 bits, chroma 400,420, 422, and 444, + Support VVC new tools like MIP, CCLM, AFFINE, GPM, DMVR, PROF, BDOF, LMCS, ALF + 211 conformace clips passed - Not support RPR, IBC, PALETTE, and other minor features yet. Performance on i7-12700: RitualDance_1920x1080_60_10_420_37_RA.266 122 FPS Tango2_3840x2160_60_10_420_27_LD.266 23 FPS 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 --- libavcodec/vvc/vvcdec.c | 1113 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 1112 insertions(+), 1 deletion(-) diff --git a/libavcodec/vvc/vvcdec.c b/libavcodec/vvc/vvcdec.c index 8d027af0b9..5041d6a35a 100644 --- a/libavcodec/vvc/vvcdec.c +++ b/libavcodec/vvc/vvcdec.c @@ -24,32 +24,1143 @@ #include "libavcodec/codec_internal.h" #include "libavcodec/decode.h" -#include "libavcodec/golomb.h" #include "libavcodec/profiles.h" #include "libavcodec/vvc.h" #include "libavutil/cpu.h" +#include "libavutil/thread.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) +{ + VVCPH *ph = s->ps.ph; + const VVCSH *sh = &sc->sh; + int ret; + if (s->no_output_before_recovery_flag) { + if (IS_GDR(s)) + s->gdr_recovery_point_poc = ph->poc + ph->recovery_poc_cnt; + if (!GDR_IS_RECOVERED(s) && s->gdr_recovery_point_poc <= ph->poc) + GDR_SET_RECOVERED(s); + } + + // 8.3.1 Decoding process for picture order count + if (!s->temporal_id && !ph->non_ref_pic_flag && !(IS_RASL(s) || IS_RADL(s))) + s->pocTid0 = 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, 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); + } + av_buffer_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 = av_buffer_pool_init(ctu_count * sizeof(RefPicListTab), av_buffer_allocz); + 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.msf); + av_freep(&fc->tab.iaf); + av_freep(&fc->tab.mmi); + av_freep(&fc->tab.dmvr); + av_buffer_pool_uninit(&fc->tab_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.dmvr = av_calloc(pic_size_in_min_pu, sizeof(*fc->tab.dmvr)); + if (!fc->tab.msf || !fc->tab.iaf || !fc->tab.mmi || !fc->tab.dmvr) + return AVERROR(ENOMEM); + fc->tab_mvf_pool = av_buffer_pool_init(pic_size_in_min_pu * sizeof(MvField), av_buffer_allocz); + if (!fc->tab_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.dmvr, 0, pic_size_in_min_pu * sizeof(*fc->tab.dmvr)); + } + + 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); + av_buffer_pool_uninit(&fc->cu_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->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 = av_buffer_pool_init(sizeof(CodingUnit), NULL); + if (!fc->cu_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->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 VVCSH *sh, int lx, smvd_find_fxn find) +{ + const RefPicList *rpl = fc->ref->refPicList + lx; + const int poc = fc->ref->poc; + int8_t idx = -1; + int old_diff = -1; + for (int i = 0; i < sh->nb_refs[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, VVCSH *sh) +{ + if (IS_B(sh)) { + sh->ref_idx_sym[0] = smvd_find(fc, sh, 0, min_positive); + sh->ref_idx_sym[1] = smvd_find(fc, sh, 1, max_negtive); + if (sh->ref_idx_sym[0] == -1 || sh->ref_idx_sym[1] == -1) { + sh->ref_idx_sym[0] = smvd_find(fc, sh, 0, max_negtive); + sh->ref_idx_sym[1] = smvd_find(fc, sh, 1, min_positive); + } + } +} + +static void eps_free(SliceContext *slice) +{ + if (slice->eps) { + for (int j = 0; j < slice->nb_eps; j++) + av_free(slice->eps[j].parse_task); + 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) { + 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 VVCSH *sh = &sc->sh; + EntryPoint *ep = sc->eps + index; + int size; + + if (index < sh->num_entry_points) { + int skipped = 0; + int64_t start = (gb->index >> 3); + int64_t end = start + sh->entry_point_offset[index]; + 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, GetBitContext *gb) +{ + const VVCSH *sh = &sc->sh; + int nb_eps = sh->num_entry_points + 1; + int ctu_addr = 0; + + 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; + for (int i = 0; i < sc->nb_eps; i++) { + EntryPoint *ep = sc->eps + i; + ep->parse_task = ff_vvc_task_alloc(); + if (!ep->parse_task) + return AVERROR(ENOMEM); + } + } + + for (int i = 0; i < sc->nb_eps; i++) + { + EntryPoint *ep = sc->eps + i; + ff_vvc_parse_task_init(ep->parse_task, VVC_TASK_TYPE_PARSE, fc, sc, ep, ctu_addr); + ep->ctu_addr_last = (i + 1 == sc->nb_eps ? sh->num_ctus_in_curr_slice : sh->entry_point_start_ctu[i]); + 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 = ff_thread_ref_frame(&dst->tf, &src->tf); + if (ret < 0) + return ret; + + dst->progress_buf = av_buffer_ref(src->progress_buf); + + dst->tab_mvf_buf = av_buffer_ref(src->tab_mvf_buf); + if (!dst->tab_mvf_buf) + goto fail; + dst->tab_mvf = src->tab_mvf; + + dst->rpl_tab_buf = av_buffer_ref(src->rpl_tab_buf); + if (!dst->rpl_tab_buf) + goto fail; + dst->rpl_tab = src->rpl_tab; + + dst->rpl_buf = av_buffer_ref(src->rpl_buf); + if (!dst->rpl_buf) + goto fail; + + dst->poc = src->poc; + dst->ctb_count = src->ctb_count; + dst->flags = src->flags; + dst->sequence = src->sequence; + + return 0; +fail: + ff_vvc_unref_frame(fc, dst, ~0); + return AVERROR(ENOMEM); +} + +static int frame_context_init_params(VVCFrameContext *fc, const VVCContext *s) +{ + int ret; + for (int i = 0; i < FF_ARRAY_ELEMS(fc->ps.alf_list); i++) { + ret = av_buffer_replace(&fc->ps.alf_list[i], s->ps.alf_list[i]); + if (ret < 0) + return ret; + } + for (int i = 0; i < FF_ARRAY_ELEMS(fc->ps.lmcs_list); i++) { + ret = av_buffer_replace(&fc->ps.lmcs_list[i], s->ps.lmcs_list[i]); + if (ret < 0) + return ret; + } + for (int i = 0; i < FF_ARRAY_ELEMS(fc->ps.scaling_list); i++) { + ret = av_buffer_replace(&fc->ps.scaling_list[i], s->ps.scaling_list[i]); + if (ret < 0) + return ret; + } + ret = av_buffer_replace(&fc->ps.ph_buf, s->ps.ph_buf); + if (ret < 0) + return ret; + fc->ps.ph = (VVCPH *)fc->ps.ph_buf->data; + + ret = av_buffer_replace(&fc->ps.pps_buf, s->ps.pps_list[fc->ps.ph->pic_parameter_set_id]); + if (ret < 0) + return ret; + fc->ps.pps = (VVCPPS*)fc->ps.pps_buf->data; + + ret = av_buffer_replace(&fc->ps.sps_buf, s->ps.sps_list[fc->ps.pps->seq_parameter_set_id]); + if (ret < 0) + return ret; + fc->ps.sps = (VVCSPS*)fc->ps.sps_buf->data; + + 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) + return ret; + } + } + } + return 0; +} + +static void export_frame_params(VVCFrameContext *fc) +{ + AVCodecContext *c = fc->avctx; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const VVCWindow *ow = &pps->conf_win; + + c->pix_fmt = sps->pix_fmt; + c->coded_width = pps->width; + c->coded_height = pps->height; + c->width = pps->width - ow->left_offset - ow->right_offset; + c->height = pps->height - ow->top_offset - ow->bottom_offset; +} + +static int decode_slice(VVCContext *s, VVCFrameContext *fc, const H2645NAL *nal, GetBitContext *gb) +{ + 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; + ret = ff_vvc_decode_sh(sh, s, is_first_slice, gb); + if (ret < 0) + goto fail; + + if (is_first_slice) { + ret = frame_context_init_params(fc, s); + if (ret < 0) + goto fail; + } + + if (is_first_slice) { + if (IS_IDR(s)) { + s->seq_decode = (s->seq_decode + 1) & 0xff; + ff_vvc_clear_refs(fc); + } + + if (pic_arrays_init(s, fc)) + goto fail; + export_frame_params(fc); + ff_vvc_dsp_init (&fc->vvcdsp, fc->ps.sps->bit_depth); + ff_videodsp_init (&fc->vdsp, fc->ps.sps->bit_depth); + ret = vvc_frame_start(s, fc, sc); + if (ret < 0) + return ret; + } else if (fc->ref) { + if (!IS_I(sh)) { + 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)) + vvc_smvd_ref_idx(fc, sh); + + ret = init_slice_context(sc, fc, nal, gb); + if (ret < 0) + goto fail; + fc->nb_slices++; + + for (int i = 0; i < (fc->ps.sps->entropy_coding_sync_enabled_flag ? 1 : sc->nb_eps); i++) + ff_vvc_frame_add_task(s, sc->eps[i].parse_task); + +fail: + return ret; +} + +static int decode_nal_unit(VVCContext *s, VVCFrameContext *fc, const H2645NAL *nal) +{ + GetBitContext gb = nal->gb; + int ret; + + s->temporal_id = nal->temporal_id; + + switch (nal->type) { + case VVC_VPS_NUT: + break; + case VVC_SPS_NUT: + if (s->avctx->hwaccel && s->avctx->hwaccel->decode_params) { + ret = s->avctx->hwaccel->decode_params(s->avctx, + nal->type, + nal->raw_data, + nal->raw_size); + if (ret < 0) + goto fail; + } + ret = ff_vvc_decode_sps(&s->ps, &gb, s->apply_defdispwin, nal->nuh_layer_id, s->avctx); + if (ret < 0) + goto fail; + break; + case VVC_PPS_NUT: + if (s->avctx->hwaccel && s->avctx->hwaccel->decode_params) { + ret = s->avctx->hwaccel->decode_params(s->avctx, + nal->type, + nal->raw_data, + nal->raw_size); + if (ret < 0) + goto fail; + } + ret = ff_vvc_decode_pps(&s->ps, &gb, s->avctx); + if (ret < 0) + goto fail; + break; + case VVC_PH_NUT: + if (s->avctx->hwaccel && s->avctx->hwaccel->decode_params) { + ret = s->avctx->hwaccel->decode_params(s->avctx, + nal->type, + nal->raw_data, + nal->raw_size); + if (ret < 0) + goto fail; + } + ret = ff_vvc_decode_ph(&s->ps, s->pocTid0, IS_CLVSS(s), &gb, s->avctx); + if (ret < 0) + goto fail; + 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, &gb); + if (ret < 0) + goto fail; + break; + + case VVC_SUFFIX_APS_NUT: + case VVC_PREFIX_APS_NUT: + ret = ff_vvc_decode_aps(&s->ps, &gb, s->avctx); + if (ret < 0) + goto fail; + break; + + default: + av_log(s->avctx, AV_LOG_INFO, + "Skipping NAL unit %d\n", nal->type); + } + + return 0; +fail: + return ret; +} + + +static int decode_nal_units(VVCContext *s, VVCFrameContext *fc, const uint8_t *buf, int length) +{ + int i, ret = 0; + int eos_at_start = 1; + s->last_eos = s->eos; + s->eos = 0; + + /* split the input packet into NAL units, so we know the upper bound on the + * number of slices in the frame */ + ret = ff_h2645_packet_split(&fc->pkt, buf, length, s->avctx, s->is_nalff, + s->nal_length_size, s->avctx->codec_id, 1, 0); + if (ret < 0) { + av_log(s->avctx, AV_LOG_ERROR, + "Error splitting the input into NAL units.\n"); + return ret; + } + + for (i = 0; i < fc->pkt.nb_nals; i++) { + const H2645NAL *nal = &fc->pkt.nals[i]; + if (nal->type == VVC_EOB_NUT || nal->type == VVC_EOS_NUT) { + if (eos_at_start) { + s->last_eos = 1; + } else { + s->eos = 1; + } + } else { + if (IS_VCL(nal->type)) { + s->vcl_unit_type = nal->type; + 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; + } + eos_at_start = 0; + } + } + + /* decode the NAL units */ + for (i = 0; i < fc->pkt.nb_nals; i++) { + H2645NAL *nal = &fc->pkt.nals[i]; + ret = decode_nal_unit(s, fc, nal); + 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_progress(fc->ref, INT_MAX); + 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++; + 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); + /* clear ph */ + fc->ps.ph = NULL; + fc->nb_slices = 0; + fc->decode_order = s->nb_frames; + + av_packet_unref(fc->avpkt); + ret = av_packet_ref(fc->avpkt, avpkt); + if (ret < 0) + return ret; + + ret = decode_nal_units(s, fc, avpkt->data, avpkt->size); + if (ret < 0) + return ret; + + ret = submit_frame(s, fc, output, got_output); + if (ret < 0) + return ret; + return avpkt->size; } +static av_cold void frame_context_free(VVCFrameContext *fc) +{ + VVCFrameParamSets *ps = &fc->ps; + int i; + + slices_free(fc); + + for (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); + + av_packet_free(&fc->avpkt); + ff_h2645_packet_uninit(&fc->pkt); + + av_buffer_unref(&ps->ph_buf); + av_buffer_unref(&ps->pps_buf); + av_buffer_unref(&ps->sps_buf); + for (i = 0; i < FF_ARRAY_ELEMS(ps->scaling_list); i++) + av_buffer_unref(&ps->scaling_list[i]); + for (i = 0; i < FF_ARRAY_ELEMS(ps->lmcs_list); i++) + av_buffer_unref(&ps->lmcs_list[i]); + for (i = 0; i < FF_ARRAY_ELEMS(ps->alf_list); i++) + av_buffer_unref(&ps->alf_list[i]); + 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; + fc->DPB[j].tf.f = fc->DPB[j].frame; + } + + fc->avpkt = av_packet_alloc(); + if (!fc->avpkt) + goto fail; + return 0; +fail: + return AVERROR(ENOMEM); +} + 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; + + 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); + return 0; } +static pthread_once_t once_control = PTHREAD_ONCE_INIT; + +static void vvc_one_time_init(void) +{ + memset(&ff_vvc_default_scale_m, 16, sizeof(ff_vvc_default_scale_m)); +} + +#define VVC_MAX_FRMAE_DELAY 16 static av_cold int vvc_decode_init(AVCodecContext *avctx) { + VVCContext *s = avctx->priv_data; + int ret; + VVCTaskCallbacks callbacks = { + s, + sizeof(VVCLocalContext), + ff_vvc_task_priority_higher, + ff_vvc_task_ready, + ff_vvc_task_run, + }; + + s->avctx = avctx; + + 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(&callbacks, s->nb_fcs * 3 / 2); + s->eos = 1; + GDR_SET_RECOVERED(s); + pthread_once(&once_control, vvc_one_time_init); + return 0; + +fail: + vvc_decode_free(avctx); + return AVERROR(ENOMEM); } #define OFFSET(x) offsetof(VVCContext, x)