/*
* Copyright(c) 2019 Intel Corporation
* Copyright (c) 2016, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 2 Clause License and
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
* was not distributed with this source code in the LICENSE file, you can
* obtain it at https://www.aomedia.org/license/software-license. If the Alliance for Open
* Media Patent License 1.0 was not distributed with this source code in the
* PATENTS file, you can obtain it at https://www.aomedia.org/license/patent-license.
*/

/*********************************
 * Includes
 *********************************/

#include "pack_unpack_c.h"
#include "utility.h"
#include "intra_prediction.h"
#include "aom_dsp_rtcd.h"
#include "pic_operators.h"

/*******************************************
* Residual Kernel 16bit
Computes the residual data
*******************************************/
void svt_residual_kernel16bit_c(uint16_t* input, uint32_t input_stride, uint16_t* pred, uint32_t pred_stride,
                                int16_t* residual, uint32_t residual_stride, uint32_t area_width,
                                uint32_t area_height) {
    uint32_t row_index = 0;

    while (row_index < area_height) {
        uint32_t column_index = 0;
        while (column_index < area_width) {
            residual[column_index] = ((int16_t)input[column_index]) - ((int16_t)pred[column_index]);
            ++column_index;
        }

        input += input_stride;
        pred += pred_stride;
        residual += residual_stride;
        ++row_index;
    }

    return;
}

/*******************************************
* Residual Kernel
Computes the residual data
*******************************************/
void svt_residual_kernel8bit_c(uint8_t* input, uint32_t input_stride, uint8_t* pred, uint32_t pred_stride,
                               int16_t* residual, uint32_t residual_stride, uint32_t area_width, uint32_t area_height) {
    uint32_t row_index = 0;

    while (row_index < area_height) {
        uint32_t column_index = 0;
        while (column_index < area_width) {
            residual[column_index] = ((int16_t)input[column_index]) - ((int16_t)pred[column_index]);
            ++column_index;
        }

        input += input_stride;
        pred += pred_stride;
        residual += residual_stride;
        ++row_index;
    }

    return;
}

/*******************************************
* Picture Full Distortion
*  Used in the Full Mode Decision Loop for the only case of a MVP-SKIP candidate
*******************************************/

void svt_full_distortion_kernel32_bits_c(int32_t* coeff, int32_t* recon_coeff, uint32_t stride, uint32_t area_width,
                                         uint32_t area_height, uint64_t distortion_result[DIST_CALC_TOTAL]) {
    uint32_t row_index             = 0;
    uint64_t residual_distortion   = 0;
    uint64_t prediction_distortion = 0;

    while (row_index < area_height) {
        uint32_t column_index = 0;
        while (column_index < area_width) {
            residual_distortion += (int64_t)SQR((int64_t)(coeff[column_index]) - (recon_coeff[column_index]));
            prediction_distortion += (int64_t)SQR((int64_t)(coeff[column_index]));
            ++column_index;
        }

        coeff += stride;
        recon_coeff += stride;
        ++row_index;
    }

    distortion_result[DIST_CALC_RESIDUAL]   = residual_distortion;
    distortion_result[DIST_CALC_PREDICTION] = prediction_distortion;
}

uint64_t svt_full_distortion_kernel16_bits_c(uint8_t* input, uint32_t input_offset, uint32_t input_stride,
                                             uint8_t* pred, int32_t pred_offset, uint32_t pred_stride,
                                             uint32_t area_width, uint32_t area_height) {
    uint32_t row_index      = 0;
    uint64_t sse_distortion = 0;

    uint16_t* input_16bit = (uint16_t*)input;
    uint16_t* pred_16bit  = (uint16_t*)pred;
    input_16bit += input_offset;
    pred_16bit += pred_offset;

    while (row_index < area_height) {
        uint32_t column_index = 0;
        while (column_index < area_width) {
            sse_distortion += (int64_t)SQR((int64_t)(input_16bit[column_index]) - (pred_16bit[column_index]));
            ++column_index;
        }
        input_16bit += input_stride;
        pred_16bit += pred_stride;
        ++row_index;
    }

    return sse_distortion;
}

/*******************************************
* Picture Distortion Full Kernel CbfZero
*******************************************/
void svt_full_distortion_kernel_cbf_zero32_bits_c(int32_t* coeff, uint32_t coeff_stride,
                                                  uint64_t distortion_result[DIST_CALC_TOTAL], uint32_t area_width,
                                                  uint32_t area_height) {
    uint32_t row_index             = 0;
    uint64_t prediction_distortion = 0;

    while (row_index < area_height) {
        uint32_t column_index = 0;
        while (column_index < area_width) {
            prediction_distortion += (int64_t)SQR((int64_t)(coeff[column_index]));
            ++column_index;
        }

        coeff += coeff_stride;
        ++row_index;
    }

    distortion_result[DIST_CALC_RESIDUAL]   = prediction_distortion;
    distortion_result[DIST_CALC_PREDICTION] = prediction_distortion;
}

void svt_aom_picture_full_distortion32_bits_single(int32_t* coeff, int32_t* recon_coeff, uint32_t stride,
                                                   uint32_t bwidth, uint32_t bheight, uint64_t* distortion,
                                                   uint32_t cnt_nz_coeff) {
    distortion[0] = 0;
    distortion[1] = 0;

    if (cnt_nz_coeff) {
        svt_full_distortion_kernel32_bits(coeff, recon_coeff, stride, bwidth, bheight, distortion);
    } else {
        svt_full_distortion_kernel_cbf_zero32_bits(coeff, stride, distortion, bwidth, bheight);
    }
}

void svt_aom_un_pack2d(uint16_t* in16_bit_buffer, uint32_t in_stride, uint8_t* out8_bit_buffer, uint32_t out8_stride,
                       uint8_t* outn_bit_buffer, uint32_t outn_stride, uint32_t width, uint32_t height) {
    if (((width & 3) == 0) && ((height & 1) == 0)) {
        svt_aom_un_pack2d_16_bit_src_mul4(
            in16_bit_buffer, in_stride, out8_bit_buffer, outn_bit_buffer, out8_stride, outn_stride, width, height);
    } else {
        svt_enc_msb_un_pack2_d(
            in16_bit_buffer, in_stride, out8_bit_buffer, outn_bit_buffer, out8_stride, outn_stride, width, height);
    }
}

void svt_aom_pack2d_src(uint8_t* in8_bit_buffer, uint32_t in8_stride, uint8_t* inn_bit_buffer, uint32_t inn_stride,
                        uint16_t* out16_bit_buffer, uint32_t out_stride, uint32_t width, uint32_t height) {
    if (((width & 3) == 0) && ((height & 1) == 0)) {
        svt_pack2d_16_bit_src_mul4(
            in8_bit_buffer, in8_stride, inn_bit_buffer, out16_bit_buffer, inn_stride, out_stride, width, height);
    } else {
        svt_enc_msb_pack2_d(
            in8_bit_buffer, in8_stride, inn_bit_buffer, out16_bit_buffer, inn_stride, out_stride, width, height);
    }
}

void svt_aom_compressed_pack_sb(uint8_t* in8_bit_buffer, uint32_t in8_stride, uint8_t* inn_bit_buffer,
                                uint32_t inn_stride, uint16_t* out16_bit_buffer, uint32_t out_stride, uint32_t width,
                                uint32_t height) {
    svt_compressed_packmsb(
        in8_bit_buffer, in8_stride, inn_bit_buffer, inn_stride, out16_bit_buffer, out_stride, width, height);
}

// Copies the source image into the destination image and updates the
// destination's UMV borders.
// Note: The frames are assumed to be identical in size.
void svt_aom_yv12_copy_y_c(const Yv12BufferConfig* src_ybc, Yv12BufferConfig* dst_ybc) {
    int32_t        row;
    const uint8_t* src = src_ybc->y_buffer;
    uint8_t*       dst = dst_ybc->y_buffer;

    if (src_ybc->flags & YV12_FLAG_HIGHBITDEPTH) {
        const uint16_t* src16 = CONVERT_TO_SHORTPTR(src);
        uint16_t*       dst16 = CONVERT_TO_SHORTPTR(dst);
        for (row = 0; row < src_ybc->y_height; ++row) {
            svt_memcpy(dst16, src16, src_ybc->y_width * sizeof(uint16_t));
            src16 += src_ybc->y_stride;
            dst16 += dst_ybc->y_stride;
        }
        return;
    }

    for (row = 0; row < src_ybc->y_height; ++row) {
        svt_memcpy(dst, src, src_ybc->y_width);
        src += src_ybc->y_stride;
        dst += dst_ybc->y_stride;
    }
}

void svt_aom_yv12_copy_u_c(const Yv12BufferConfig* src_bc, Yv12BufferConfig* dst_bc) {
    int32_t        row;
    const uint8_t* src = src_bc->u_buffer;
    uint8_t*       dst = dst_bc->u_buffer;

    if (src_bc->flags & YV12_FLAG_HIGHBITDEPTH) {
        const uint16_t* src16 = CONVERT_TO_SHORTPTR(src);
        uint16_t*       dst16 = CONVERT_TO_SHORTPTR(dst);
        for (row = 0; row < src_bc->uv_height; ++row) {
            svt_memcpy(dst16, src16, src_bc->uv_width * sizeof(uint16_t));
            src16 += src_bc->uv_stride;
            dst16 += dst_bc->uv_stride;
        }
        return;
    }

    for (row = 0; row < src_bc->uv_height; ++row) {
        svt_memcpy(dst, src, src_bc->uv_width);
        src += src_bc->uv_stride;
        dst += dst_bc->uv_stride;
    }
}

void svt_aom_yv12_copy_v_c(const Yv12BufferConfig* src_bc, Yv12BufferConfig* dst_bc) {
    int32_t        row;
    const uint8_t* src = src_bc->v_buffer;
    uint8_t*       dst = dst_bc->v_buffer;

    if (src_bc->flags & YV12_FLAG_HIGHBITDEPTH) {
        const uint16_t* src16 = CONVERT_TO_SHORTPTR(src);
        uint16_t*       dst16 = CONVERT_TO_SHORTPTR(dst);
        for (row = 0; row < src_bc->uv_height; ++row) {
            svt_memcpy(dst16, src16, src_bc->uv_width * sizeof(uint16_t));
            src16 += src_bc->uv_stride;
            dst16 += dst_bc->uv_stride;
        }
        return;
    }

    for (row = 0; row < src_bc->uv_height; ++row) {
        svt_memcpy(dst, src, src_bc->uv_width);
        src += src_bc->uv_stride;
        dst += dst_bc->uv_stride;
    }
}

/** svt_aom_generate_padding()
        is used to pad the target picture. The horizontal padding happens first and then the vertical padding.
 */
void svt_aom_generate_padding(EbByte src_pic, uint32_t src_stride, uint32_t original_src_width,
                              uint32_t original_src_height, uint32_t padding_width, uint32_t padding_height) {
    if (!src_pic) {
        SVT_ERROR("padding NULL pointers\n");
        return;
    }

    const uint32_t row_bytes = src_stride;

    /* Horizontal padding: extend each active row left and right */
    EbByte row = src_pic;
    for (uint32_t y = 0; y < original_src_height; ++y) {
        const uint8_t left_pixel  = row[0];
        const uint8_t right_pixel = row[original_src_width - 1];

        svt_memset(row - padding_width, left_pixel, padding_width);
        svt_memset(row + original_src_width, right_pixel, padding_width);

        row += src_stride;
    }

    /* Vertical padding: replicate the fully padded first and last rows */
    EbByte const top_src_row    = src_pic - padding_width;
    EbByte const bottom_src_row = src_pic - padding_width + (original_src_height - 1) * src_stride;

    EbByte top_dst_row    = top_src_row;
    EbByte bottom_dst_row = bottom_src_row;

    for (uint32_t y = 0; y < padding_height; ++y) {
        top_dst_row -= src_stride;
        bottom_dst_row += src_stride;

        svt_memcpy(top_dst_row, top_src_row, row_bytes);
        svt_memcpy(bottom_dst_row, bottom_src_row, row_bytes);
    }
}

void svt_aom_generate_padding_compressed_10bit(
    EbByte   src_pic, //output paramter, pointer to the source picture to be padded.
    uint32_t src_stride, //input paramter, the stride of the source picture to be padded.
    uint32_t original_src_width, //input paramter, the width of the source picture which excludes the padding.
    uint32_t original_src_height, //input paramter, the height of the source picture which excludes the padding.
    uint32_t padding_width, //input paramter, the padding width.
    uint32_t padding_height) //input paramter, the padding height.
{
    if (!src_pic) {
        SVT_ERROR("padding NULL pointers\n");
        return;
    }
    EbByte temp_src_pic0 = src_pic;

    for (uint32_t row = 0; row < original_src_height; row++) {
        const uint8_t left_pixel  = (temp_src_pic0[0] >> 6) & 0x03;
        const uint8_t right_pixel = temp_src_pic0[original_src_width / 4 - 1] & 0x03;

        EB_MEMSET(temp_src_pic0 - padding_width / 4, left_pixel * 0b01010101, padding_width / 4);
        EB_MEMSET(temp_src_pic0 + original_src_width / 4, right_pixel * 0b01010101, padding_width / 4);

        temp_src_pic0 += src_stride;
    }

    // vertical padding
    uint32_t vertical_idx = padding_height;
    temp_src_pic0         = src_pic - (padding_width / 4);
    EbByte temp_src_pic1  = src_pic - (padding_width / 4) + (original_src_height - 1) * src_stride;
    EbByte temp_src_pic2  = temp_src_pic0;
    EbByte temp_src_pic3  = temp_src_pic1;
    while (vertical_idx) {
        // top part data copy
        temp_src_pic2 -= src_stride;
        svt_memcpy(temp_src_pic2, temp_src_pic0, sizeof(uint8_t) * src_stride); // uint8_t to be modified
        // bottom part data copy
        temp_src_pic3 += src_stride;
        svt_memcpy(temp_src_pic3, temp_src_pic1, sizeof(uint8_t) * src_stride); // uint8_t to be modified
        --vertical_idx;
    }
}

/** svt_aom_generate_padding16_bit()
is used to pad the target picture. The horizontal padding happens first and then the vertical padding.
*/
// TODO: svt_aom_generate_padding() and generate_padding16() functions are not aligned, inputs according to comments are wrong
void svt_aom_generate_padding16_bit(
    uint16_t* src_pic, //output paramter, pointer to the source picture to be padded.
    uint32_t  src_stride, //input paramter, the stride of the source picture to be padded.
    uint32_t  original_src_width, //input paramter, the width of the source picture which excludes the padding.
    uint32_t  original_src_height, //input paramter, the height of the source picture which excludes the padding.
    uint32_t  padding_width, //input paramter, the padding width.
    uint32_t  padding_height) //input paramter, the padding height.
{
    uint32_t  vertical_idx = original_src_height;
    uint16_t* temp_src_pic0;
    uint16_t* temp_src_pic1;
    uint16_t* temp_src_pic2;
    uint16_t* temp_src_pic3;

    temp_src_pic0 = src_pic;
    while (vertical_idx) {
        // horizontal padding
        //EB_MEMSET(temp_src_pic0 - padding_width, temp_src_pic0, padding_width);
        memset16bit(temp_src_pic0 - padding_width, temp_src_pic0[0], padding_width);
        memset16bit(temp_src_pic0 + original_src_width, (temp_src_pic0 + original_src_width - 1)[0], padding_width);

        temp_src_pic0 += src_stride;
        --vertical_idx;
    }

    // vertical padding
    vertical_idx  = padding_height;
    temp_src_pic0 = src_pic - padding_width;
    temp_src_pic1 = src_pic - padding_width + (original_src_height - 1) * src_stride;
    temp_src_pic2 = temp_src_pic0;
    temp_src_pic3 = temp_src_pic1;
    while (vertical_idx) {
        // top part data copy
        temp_src_pic2 -= src_stride;
        svt_memcpy(temp_src_pic2, temp_src_pic0, sizeof(uint16_t) * src_stride);
        // bottom part data copy
        temp_src_pic3 += src_stride;
        svt_memcpy(temp_src_pic3, temp_src_pic1, sizeof(uint16_t) * src_stride);
        --vertical_idx;
    }
}

/** pad_input_picture()
is used to pad the input picture in order to get . The horizontal padding happens first and then the vertical padding.
*/
void pad_input_picture(
    EbByte   src_pic, //output paramter, pointer to the source picture to be padded.
    uint32_t src_stride, //input paramter, the stride of the source picture to be padded.
    uint32_t original_src_width, //input paramter, the width of the source picture which excludes the padding.
    uint32_t original_src_height, //input paramter, the height of the source picture which excludes the padding.
    uint32_t pad_right, //input paramter, the padding right.
    uint32_t pad_bottom) //input paramter, the padding bottom.
{
    uint32_t vertical_idx;
    EbByte   temp_src_pic0;

    if (!src_pic) {
        SVT_ERROR("padding NULL pointers\n");
        return;
    }

    if (pad_right) {
        // Add padding @ the right
        vertical_idx  = original_src_height;
        temp_src_pic0 = src_pic;

        while (vertical_idx) {
            svt_memset(temp_src_pic0 + original_src_width, *(temp_src_pic0 + original_src_width - 1), pad_right);
            temp_src_pic0 += src_stride;
            --vertical_idx;
        }
    }

    if (pad_bottom) {
        EbByte temp_src_pic1;
        // Add padding @ the bottom
        vertical_idx  = pad_bottom;
        temp_src_pic0 = src_pic + (original_src_height - 1) * src_stride;
        temp_src_pic1 = temp_src_pic0;

        while (vertical_idx) {
            temp_src_pic1 += src_stride;
            svt_memcpy(temp_src_pic1, temp_src_pic0, sizeof(uint8_t) * (original_src_width + pad_right));
            --vertical_idx;
        }
    }

    return;
}

/** svt_aom_pad_input_picture_16bit()
is used to pad the input picture in order to get . The horizontal padding happens first and then the vertical padding.
*/
void svt_aom_pad_input_picture_16bit(
    uint16_t* src_pic, //output paramter, pointer to the source picture to be padded.
    uint32_t  src_stride, //input paramter, the stride of the source picture to be padded.
    uint32_t  original_src_width, //input paramter, the width of the source picture which excludes the padding.
    uint32_t  original_src_height, //input paramter, the height of the source picture which excludes the padding.
    uint32_t  pad_right, //input paramter, the padding right.
    uint32_t  pad_bottom) //input paramter, the padding bottom.
{
    uint32_t  vertical_idx;
    uint16_t* temp_src_pic0;

    if (pad_right) {
        // Add padding @ the right
        vertical_idx  = original_src_height;
        temp_src_pic0 = src_pic;

        while (vertical_idx) {
            memset16bit(temp_src_pic0 + original_src_width, *(temp_src_pic0 + original_src_width - 1), pad_right);
            temp_src_pic0 += src_stride;
            --vertical_idx;
        }
    }

    if (pad_bottom) {
        uint16_t* temp_src_pic1;
        // Add padding @ the bottom
        vertical_idx  = pad_bottom;
        temp_src_pic0 = (uint16_t*)(src_pic + (original_src_height - 1) * src_stride);
        temp_src_pic1 = temp_src_pic0;

        while (vertical_idx) {
            temp_src_pic1 += src_stride;
            svt_memcpy(temp_src_pic1, temp_src_pic0, sizeof(uint16_t) * (original_src_width + pad_right));
            --vertical_idx;
        }
    }

    return;
}

void svt_aom_pack_2d_pic(EbPictureBufferDesc* input_picture, uint16_t* packed[3]) {
    svt_aom_compressed_pack_sb(input_picture->y_buffer,
                               input_picture->y_stride,
                               input_picture->y_buffer_bit_inc,
                               input_picture->y_stride_bit_inc >> 2,
                               (uint16_t*)packed[0],
                               input_picture->y_stride,
                               input_picture->width,
                               input_picture->height);

    svt_aom_compressed_pack_sb(input_picture->u_buffer,
                               input_picture->v_stride,
                               input_picture->u_buffer_bit_inc,
                               input_picture->v_stride_bit_inc >> 2,
                               (uint16_t*)packed[1],
                               input_picture->v_stride,
                               input_picture->width >> 1,
                               input_picture->height >> 1);

    svt_aom_compressed_pack_sb(input_picture->v_buffer,
                               input_picture->v_stride,
                               input_picture->v_buffer_bit_inc,
                               input_picture->v_stride_bit_inc >> 2,
                               (uint16_t*)packed[2],
                               input_picture->v_stride,
                               input_picture->width >> 1,
                               input_picture->height >> 1);
}

void svt_aom_convert_pic_8bit_to_16bit(EbPictureBufferDesc* src_8bit, EbPictureBufferDesc* dst_16bit, uint16_t ss_x,
                                       uint16_t ss_y) {
    //copy input from 8bit to 16bit
    uint8_t*  buffer_8bit;
    int32_t   stride_8bit;
    uint16_t* buffer_16bit;
    int32_t   stride_16bit;
    // Y
    buffer_16bit = (uint16_t*)(dst_16bit->y_buffer);
    stride_16bit = dst_16bit->y_stride;
    buffer_8bit  = src_8bit->y_buffer;
    stride_8bit  = src_8bit->y_stride;

    svt_convert_8bit_to_16bit(buffer_8bit, stride_8bit, buffer_16bit, stride_16bit, src_8bit->width, src_8bit->height);

    // Cb
    buffer_16bit = (uint16_t*)(dst_16bit->u_buffer);
    stride_16bit = dst_16bit->u_stride;
    buffer_8bit  = src_8bit->u_buffer;
    stride_8bit  = src_8bit->u_stride;

    svt_convert_8bit_to_16bit(
        buffer_8bit, stride_8bit, buffer_16bit, stride_16bit, src_8bit->width >> ss_x, src_8bit->height >> ss_y);

    // Cr
    buffer_16bit = (uint16_t*)(dst_16bit->v_buffer);
    stride_16bit = dst_16bit->v_stride;
    buffer_8bit  = src_8bit->v_buffer;
    stride_8bit  = src_8bit->v_stride;

    svt_convert_8bit_to_16bit(
        buffer_8bit, stride_8bit, buffer_16bit, stride_16bit, src_8bit->width >> ss_x, src_8bit->height >> ss_y);

    dst_16bit->width  = src_8bit->width;
    dst_16bit->height = src_8bit->height;
}

void svt_aom_copy_buffer_info(EbPictureBufferDesc* src_ptr, EbPictureBufferDesc* dst_ptr) {
    dst_ptr->width            = src_ptr->width;
    dst_ptr->height           = src_ptr->height;
    dst_ptr->max_width        = src_ptr->max_width;
    dst_ptr->max_height       = src_ptr->max_height;
    dst_ptr->y_stride         = src_ptr->y_stride;
    dst_ptr->u_stride         = src_ptr->u_stride;
    dst_ptr->v_stride         = src_ptr->v_stride;
    dst_ptr->border           = src_ptr->border;
    dst_ptr->y_stride_bit_inc = src_ptr->y_stride_bit_inc;
    dst_ptr->u_stride_bit_inc = src_ptr->u_stride_bit_inc;
    dst_ptr->v_stride_bit_inc = src_ptr->v_stride_bit_inc;
    dst_ptr->luma_size        = src_ptr->luma_size;
    dst_ptr->chroma_size      = src_ptr->chroma_size;
}

void svt_aom_pack_highbd_pic(const EbPictureBufferDesc* pic_ptr, uint16_t* buffer_16bit[3], uint32_t ss_x,
                             uint32_t ss_y) {
    uint16_t width  = pic_ptr->y_stride;
    uint16_t height = (uint16_t)(pic_ptr->border * 2 + pic_ptr->height);

    uint32_t comp_stride_y = pic_ptr->y_stride / 4;

    svt_aom_compressed_pack_sb(pic_ptr->y_buffer - (pic_ptr->border + (pic_ptr->y_stride * pic_ptr->border)),
                               pic_ptr->y_stride,
                               pic_ptr->y_buffer_bit_inc - (pic_ptr->border / 4) - (comp_stride_y * pic_ptr->border),
                               comp_stride_y,
                               buffer_16bit[0 /*Y*/],
                               pic_ptr->y_stride,
                               width,
                               height);

    if (buffer_16bit[1 /*U*/]) {
        uint32_t comp_stride_cb = pic_ptr->u_stride / 4;
        svt_aom_compressed_pack_sb(
            pic_ptr->u_buffer - ((pic_ptr->border >> ss_x) + (pic_ptr->u_stride * (pic_ptr->border >> ss_y))),
            pic_ptr->u_stride,
            pic_ptr->u_buffer_bit_inc - ((pic_ptr->border >> ss_x) / 4 + (comp_stride_cb * (pic_ptr->border >> ss_y))),
            comp_stride_cb,
            buffer_16bit[1 /*U*/],
            pic_ptr->u_stride,
            (width + ss_x) >> ss_x,
            (height + ss_y) >> ss_y);
    }
    if (buffer_16bit[2 /*V*/]) {
        uint32_t comp_stride_cr = pic_ptr->v_stride / 4;
        svt_aom_compressed_pack_sb(
            pic_ptr->v_buffer - ((pic_ptr->border >> ss_x) + (pic_ptr->v_stride * (pic_ptr->border >> ss_y))),
            pic_ptr->v_stride,
            pic_ptr->v_buffer_bit_inc - ((pic_ptr->border >> ss_x) / 4 + (comp_stride_cr * (pic_ptr->border >> ss_y))),
            comp_stride_cr,
            buffer_16bit[2 /*V*/],
            pic_ptr->v_stride,
            (width + ss_x) >> ss_x,
            (height + ss_y) >> ss_y);
    }
}

void svt_aom_unpack_highbd_pic(uint16_t* buffer_highbd[3], EbPictureBufferDesc* pic_ptr, uint32_t ss_x, uint32_t ss_y) {
    uint16_t width  = pic_ptr->y_stride;
    uint16_t height = (uint16_t)(pic_ptr->border * 2 + pic_ptr->height);

    uint32_t comp_stride_y  = pic_ptr->y_stride / 4;
    uint32_t comp_stride_cb = pic_ptr->u_stride / 4;
    uint32_t comp_stride_cr = pic_ptr->v_stride / 4;

    svt_unpack_and_2bcompress(buffer_highbd[0 /*Y*/],
                              pic_ptr->y_stride,
                              pic_ptr->y_buffer - (pic_ptr->border + (pic_ptr->y_stride * pic_ptr->border)),
                              pic_ptr->y_stride,
                              pic_ptr->y_buffer_bit_inc - (pic_ptr->border / 4) - (comp_stride_y * pic_ptr->border),
                              comp_stride_y,
                              width,
                              height);

    if (buffer_highbd[1 /*U*/]) {
        svt_unpack_and_2bcompress(
            buffer_highbd[1 /*U*/],
            pic_ptr->u_stride,
            pic_ptr->u_buffer - ((pic_ptr->border >> ss_x) + (pic_ptr->u_stride * (pic_ptr->border >> ss_y))),
            pic_ptr->u_stride,
            pic_ptr->u_buffer_bit_inc - ((pic_ptr->border >> ss_x) / 4 + (comp_stride_cb * (pic_ptr->border >> ss_y))),
            comp_stride_cb,
            (width + ss_x) >> ss_x,
            (height + ss_y) >> ss_y);
    }

    if (buffer_highbd[2 /*V*/]) {
        svt_unpack_and_2bcompress(
            buffer_highbd[2 /*V*/],
            pic_ptr->v_stride,
            pic_ptr->v_buffer - ((pic_ptr->border >> ss_x) + (pic_ptr->v_stride * (pic_ptr->border >> ss_y))),
            pic_ptr->v_stride,
            pic_ptr->v_buffer_bit_inc - ((pic_ptr->border >> ss_x) / 4 + (comp_stride_cr * (pic_ptr->border >> ss_y))),
            comp_stride_cr,
            (width + ss_x) >> ss_x,
            (height + ss_y) >> ss_y);
    }
}
