static int hls_transform_tree(HEVCContext *s, int x0, int y0,
                              int xBase, int yBase, int cb_xBase, int cb_yBase,
                              int log2_cb_size, int log2_trafo_size,
                              int trafo_depth, int blk_idx)
{
    HEVCLocalContext *lc = &s->HEVClc;
    uint8_t split_transform_flag;
    int ret;

    if (trafo_depth > 0 && log2_trafo_size == 2) {
        SAMPLE_CBF(lc->tt.cbf_cb[trafo_depth], x0, y0) =
            SAMPLE_CBF(lc->tt.cbf_cb[trafo_depth - 1], xBase, yBase);
        SAMPLE_CBF(lc->tt.cbf_cr[trafo_depth], x0, y0) =
            SAMPLE_CBF(lc->tt.cbf_cr[trafo_depth - 1], xBase, yBase);
    } else {
        SAMPLE_CBF(lc->tt.cbf_cb[trafo_depth], x0, y0) =
        SAMPLE_CBF(lc->tt.cbf_cr[trafo_depth], x0, y0) = 0;
    }

    if (lc->cu.intra_split_flag) {
        if (trafo_depth == 1)
            lc->tu.cur_intra_pred_mode = lc->pu.intra_pred_mode[blk_idx];
    } else {
        lc->tu.cur_intra_pred_mode = lc->pu.intra_pred_mode[0];
    }

    lc->tt.cbf_luma = 1;

    lc->tt.inter_split_flag = s->sps->max_transform_hierarchy_depth_inter == 0 &&
                              lc->cu.pred_mode == MODE_INTER &&
                              lc->cu.part_mode != PART_2Nx2N &&
                              trafo_depth == 0;

    if (log2_trafo_size <= s->sps->log2_max_trafo_size &&
        log2_trafo_size >  s->sps->log2_min_tb_size    &&
        trafo_depth     < lc->cu.max_trafo_depth       &&
        !(lc->cu.intra_split_flag && trafo_depth == 0)) {
        split_transform_flag = ff_hevc_split_transform_flag_decode(s, log2_trafo_size);
    } else {
        split_transform_flag = log2_trafo_size > s->sps->log2_max_trafo_size ||
                               (lc->cu.intra_split_flag && trafo_depth == 0) ||
                               lc->tt.inter_split_flag;
    }

    if (log2_trafo_size > 2) {
        if (trafo_depth == 0 ||
            SAMPLE_CBF(lc->tt.cbf_cb[trafo_depth - 1], xBase, yBase)) {
            SAMPLE_CBF(lc->tt.cbf_cb[trafo_depth], x0, y0) =
                ff_hevc_cbf_cb_cr_decode(s, trafo_depth);
        }

        if (trafo_depth == 0 ||
            SAMPLE_CBF(lc->tt.cbf_cr[trafo_depth - 1], xBase, yBase)) {
            SAMPLE_CBF(lc->tt.cbf_cr[trafo_depth], x0, y0) =
                ff_hevc_cbf_cb_cr_decode(s, trafo_depth);
        }
    }

    if (split_transform_flag) {
        int x1 = x0 + ((1 << log2_trafo_size) >> 1);
        int y1 = y0 + ((1 << log2_trafo_size) >> 1);

        ret = hls_transform_tree(s, x0, y0, x0, y0, cb_xBase, cb_yBase,
                                 log2_cb_size, log2_trafo_size - 1,
                                 trafo_depth + 1, 0);
        if (ret < 0)
            return ret;
        ret = hls_transform_tree(s, x1, y0, x0, y0, cb_xBase, cb_yBase,
                                 log2_cb_size, log2_trafo_size - 1,
                                 trafo_depth + 1, 1);
        if (ret < 0)
            return ret;
        ret = hls_transform_tree(s, x0, y1, x0, y0, cb_xBase, cb_yBase,
                                 log2_cb_size, log2_trafo_size - 1,
                                 trafo_depth + 1, 2);
        if (ret < 0)
            return ret;
        ret = hls_transform_tree(s, x1, y1, x0, y0, cb_xBase, cb_yBase,
                                 log2_cb_size, log2_trafo_size - 1,
                                 trafo_depth + 1, 3);
        if (ret < 0)
            return ret;
    } else {
        int min_tu_size      = 1 << s->sps->log2_min_tb_size;
        int log2_min_tu_size = s->sps->log2_min_tb_size;
        int min_tu_width     = s->sps->min_tb_width;

        if (lc->cu.pred_mode == MODE_INTRA || trafo_depth != 0 ||
            SAMPLE_CBF(lc->tt.cbf_cb[trafo_depth], x0, y0) ||
            SAMPLE_CBF(lc->tt.cbf_cr[trafo_depth], x0, y0)) {
            lc->tt.cbf_luma = ff_hevc_cbf_luma_decode(s, trafo_depth);
        }

        ret = hls_transform_unit(s, x0, y0, xBase, yBase, cb_xBase, cb_yBase,
                                 log2_cb_size, log2_trafo_size, trafo_depth,
                                 blk_idx);
        if (ret < 0)
            return ret;
        // TODO: store cbf_luma somewhere else
        if (lc->tt.cbf_luma) {
            int i, j;
            for (i = 0; i < (1 << log2_trafo_size); i += min_tu_size)
                for (j = 0; j < (1 << log2_trafo_size); j += min_tu_size) {
                    int x_tu = (x0 + j) >> log2_min_tu_size;
                    int y_tu = (y0 + i) >> log2_min_tu_size;
                    s->cbf_luma[y_tu * min_tu_width + x_tu] = 1;
                }
        }
        if (!s->sh.disable_deblocking_filter_flag) {
            ff_hevc_deblocking_boundary_strengths(s, x0, y0, log2_trafo_size,
                                                  lc->slice_or_tiles_up_boundary,
                                                  lc->slice_or_tiles_left_boundary);
            if (s->pps->transquant_bypass_enable_flag &&
                lc->cu.cu_transquant_bypass_flag)
                set_deblocking_bypass(s, x0, y0, log2_trafo_size);
        }
    }
    return 0;
}
static int hls_transform_tree(HEVCContext *s, int x0, int y0,
                              int xBase, int yBase, int cb_xBase, int cb_yBase,
                              int log2_cb_size, int log2_trafo_size,
                              int trafo_depth, int blk_idx)
{
    HEVCLocalContext *lc = &s->HEVClc;
    uint8_t split_transform_flag;
    int ret;

    if (trafo_depth > 0 && log2_trafo_size == 2) {
        SAMPLE_CBF(lc->tt.cbf_cb[trafo_depth], x0, y0) =
            SAMPLE_CBF(lc->tt.cbf_cb[trafo_depth - 1], xBase, yBase);
        SAMPLE_CBF(lc->tt.cbf_cr[trafo_depth], x0, y0) =
            SAMPLE_CBF(lc->tt.cbf_cr[trafo_depth - 1], xBase, yBase);
    } else {
        SAMPLE_CBF(lc->tt.cbf_cb[trafo_depth], x0, y0) =
        SAMPLE_CBF(lc->tt.cbf_cr[trafo_depth], x0, y0) = 0;
    }

    if (lc->cu.intra_split_flag) {
        if (trafo_depth == 1)
            lc->tu.cur_intra_pred_mode = lc->pu.intra_pred_mode[blk_idx];
    } else {
        lc->tu.cur_intra_pred_mode = lc->pu.intra_pred_mode[0];
    }

    lc->tt.cbf_luma = 1;

    lc->tt.inter_split_flag = s->sps->max_transform_hierarchy_depth_inter == 0 &&
                              lc->cu.pred_mode == MODE_INTER &&
                              lc->cu.part_mode != PART_2Nx2N &&
                              trafo_depth == 0;

    if (log2_trafo_size <= s->sps->log2_max_trafo_size &&
        log2_trafo_size >  s->sps->log2_min_tb_size    &&
        trafo_depth     < lc->cu.max_trafo_depth       &&
        !(lc->cu.intra_split_flag && trafo_depth == 0)) {
        split_transform_flag = ff_hevc_split_transform_flag_decode(s, log2_trafo_size);
    } else {
        split_transform_flag = log2_trafo_size > s->sps->log2_max_trafo_size ||
                               (lc->cu.intra_split_flag && trafo_depth == 0) ||
                               lc->tt.inter_split_flag;
    }

    if (log2_trafo_size > 2) {
        if (trafo_depth == 0 ||
            SAMPLE_CBF(lc->tt.cbf_cb[trafo_depth - 1], xBase, yBase)) {
            SAMPLE_CBF(lc->tt.cbf_cb[trafo_depth], x0, y0) =
                ff_hevc_cbf_cb_cr_decode(s, trafo_depth);
        }

        if (trafo_depth == 0 ||
            SAMPLE_CBF(lc->tt.cbf_cr[trafo_depth - 1], xBase, yBase)) {
            SAMPLE_CBF(lc->tt.cbf_cr[trafo_depth], x0, y0) =
                ff_hevc_cbf_cb_cr_decode(s, trafo_depth);
        }
    }

    if (split_transform_flag) {
        int x1 = x0 + ((1 << log2_trafo_size) >> 1);
        int y1 = y0 + ((1 << log2_trafo_size) >> 1);

        ret = hls_transform_tree(s, x0, y0, x0, y0, cb_xBase, cb_yBase,
                                 log2_cb_size, log2_trafo_size - 1,
                                 trafo_depth + 1, 0);
        if (ret < 0)
            return ret;
        ret = hls_transform_tree(s, x1, y0, x0, y0, cb_xBase, cb_yBase,
                                 log2_cb_size, log2_trafo_size - 1,
                                 trafo_depth + 1, 1);
        if (ret < 0)
            return ret;
        ret = hls_transform_tree(s, x0, y1, x0, y0, cb_xBase, cb_yBase,
                                 log2_cb_size, log2_trafo_size - 1,
                                 trafo_depth + 1, 2);
        if (ret < 0)
            return ret;
        ret = hls_transform_tree(s, x1, y1, x0, y0, cb_xBase, cb_yBase,
                                 log2_cb_size, log2_trafo_size - 1,
                                 trafo_depth + 1, 3);
        if (ret < 0)
            return ret;
    } else {
        int min_tu_size      = 1 << s->sps->log2_min_tb_size;
        int log2_min_tu_size = s->sps->log2_min_tb_size;
        int min_tu_width     = s->sps->min_tb_width;

        if (lc->cu.pred_mode == MODE_INTRA || trafo_depth != 0 ||
            SAMPLE_CBF(lc->tt.cbf_cb[trafo_depth], x0, y0) ||
            SAMPLE_CBF(lc->tt.cbf_cr[trafo_depth], x0, y0)) {
            lc->tt.cbf_luma = ff_hevc_cbf_luma_decode(s, trafo_depth);
        }

        ret = hls_transform_unit(s, x0, y0, xBase, yBase, cb_xBase, cb_yBase,
                                 log2_cb_size, log2_trafo_size, trafo_depth,
                                 blk_idx);
        if (ret < 0)
            return ret;
         
        if (lc->tt.cbf_luma) {
            int i, j;
            for (i = 0; i < (1 << log2_trafo_size); i += min_tu_size)
                for (j = 0; j < (1 << log2_trafo_size); j += min_tu_size) {
                    int x_tu = (x0 + j) >> log2_min_tu_size;
                    int y_tu = (y0 + i) >> log2_min_tu_size;
                    s->cbf_luma[y_tu * min_tu_width + x_tu] = 1;
                }
        }
        if (!s->sh.disable_deblocking_filter_flag) {
            ff_hevc_deblocking_boundary_strengths(s, x0, y0, log2_trafo_size,
                                                  lc->slice_or_tiles_up_boundary,
                                                  lc->slice_or_tiles_left_boundary);
            if (s->pps->transquant_bypass_enable_flag &&
                lc->cu.cu_transquant_bypass_flag)
                set_deblocking_bypass(s, x0, y0, log2_trafo_size);
        }
    }
    return 0;
}