arbc.c 6.55 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
/*
 * Gryphon's Anim Compressor decoder
 * Copyright (c) 2019 Paul B Mahol
 *
 * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "libavutil/imgutils.h"
#include "libavutil/internal.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mem.h"

#include "avcodec.h"
#include "bytestream.h"
#include "internal.h"

typedef struct ARBCContext {
    GetByteContext gb;

    AVFrame *prev_frame;
} ARBCContext;

41
static void fill_tile4(AVCodecContext *avctx, int color, AVFrame *frame)
42 43 44 45 46 47
{
    ARBCContext *s = avctx->priv_data;
    GetByteContext *gb = &s->gb;
    int nb_tiles = bytestream2_get_le16(gb);
    int h = avctx->height - 1;

48 49 50
    if ((avctx->width / 4 + 1) * (avctx->height / 4 + 1) < nb_tiles)
        return;

51 52 53 54 55 56 57 58 59 60 61 62 63 64
    for (int i = 0; i < nb_tiles; i++) {
        int y = bytestream2_get_byte(gb);
        int x = bytestream2_get_byte(gb);
        uint16_t mask = bytestream2_get_le16(gb);
        int start_y = y * 4, start_x = x * 4;
        int end_y = start_y + 4, end_x = start_x + 4;

        for (int j = start_y; j < end_y; j++) {
            for (int k = start_x; k < end_x; k++) {
                if (mask & 0x8000) {
                    if (j >= avctx->height || k >= avctx->width) {
                        mask = mask << 1;
                        continue;
                    }
65
                    AV_WB24(&frame->data[0][frame->linesize[0] * (h - j) + 3 * k], color);
66 67 68 69 70 71 72 73
                }
                mask = mask << 1;
            }
        }
    }
}

static void fill_tileX(AVCodecContext *avctx, int tile_width, int tile_height,
74
                       int color, AVFrame *frame)
75 76 77 78 79 80 81 82
{
    ARBCContext *s = avctx->priv_data;
    GetByteContext *gb = &s->gb;
    const int step_h = tile_height / 4;
    const int step_w = tile_width / 4;
    int nb_tiles = bytestream2_get_le16(gb);
    int h = avctx->height - 1;

83 84 85
    if ((avctx->width / tile_width + 1) * (avctx->height / tile_height + 1) < nb_tiles)
        return;

86 87 88 89 90 91 92 93 94 95 96 97 98 99
    for (int i = 0; i < nb_tiles; i++) {
        int y = bytestream2_get_byte(gb);
        int x = bytestream2_get_byte(gb);
        uint16_t mask = bytestream2_get_le16(gb);
        int start_y = y * tile_height, start_x = x * tile_width;
        int end_y = start_y + tile_height, end_x = start_x + tile_width;

        for (int j = start_y; j < end_y; j += step_h) {
            for (int k = start_x; k < end_x; k += step_w) {
                if (mask & 0x8000U) {
                    for (int m = 0; m < step_h; m++) {
                        for (int n = 0; n < step_w; n++) {
                            if (j + m >= avctx->height || k + n >= avctx->width)
                                continue;
100
                            AV_WB24(&frame->data[0][frame->linesize[0] * (h - (j + m)) + 3 * (k + n)], color);
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
                        }
                    }
                }
                mask = mask << 1;
            }
        }
    }
}

static int decode_frame(AVCodecContext *avctx, void *data,
                        int *got_frame, AVPacket *avpkt)
{
    ARBCContext *s = avctx->priv_data;
    AVFrame *frame = data;
    int ret, nb_segments, keyframe = 1;

    if (avpkt->size < 10)
        return AVERROR_INVALIDDATA;

120 121 122 123 124 125 126 127 128
    bytestream2_init(&s->gb, avpkt->data, avpkt->size);
    bytestream2_skip(&s->gb, 8);
    nb_segments = bytestream2_get_le16(&s->gb);
    if (nb_segments == 0)
        keyframe = 0;

    if (7 * nb_segments > bytestream2_get_bytes_left(&s->gb))
        return AVERROR_INVALIDDATA;

129 130 131 132 133 134 135 136 137 138 139
    if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0)
        return ret;

    if (s->prev_frame->data[0]) {
        ret = av_frame_copy(frame, s->prev_frame);
        if (ret < 0)
            return ret;
    }

    for (int i = 0; i < nb_segments; i++) {
        int resolution_flag;
140
        int fill;
141 142 143 144

        if (bytestream2_get_bytes_left(&s->gb) <= 0)
            return AVERROR_INVALIDDATA;

145
        fill = bytestream2_get_byte(&s->gb) << 16;
146
        bytestream2_skip(&s->gb, 1);
147
        fill |= bytestream2_get_byte(&s->gb) << 8;
148
        bytestream2_skip(&s->gb, 1);
149
        fill |= bytestream2_get_byte(&s->gb) << 0;
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
        bytestream2_skip(&s->gb, 1);
        resolution_flag = bytestream2_get_byte(&s->gb);

        if (resolution_flag & 0x10)
            fill_tileX(avctx, 1024, 1024, fill, frame);
        if (resolution_flag & 0x08)
            fill_tileX(avctx, 256, 256, fill, frame);
        if (resolution_flag & 0x04)
            fill_tileX(avctx, 64, 64, fill, frame);
        if (resolution_flag & 0x02)
            fill_tileX(avctx, 16, 16, fill, frame);
        if (resolution_flag & 0x01)
            fill_tile4(avctx, fill, frame);
    }

    av_frame_unref(s->prev_frame);
    if ((ret = av_frame_ref(s->prev_frame, frame)) < 0)
        return ret;

    frame->pict_type = keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P;
    frame->key_frame = keyframe;
    *got_frame = 1;

    return avpkt->size;
}

static av_cold int decode_init(AVCodecContext *avctx)
{
    ARBCContext *s = avctx->priv_data;

    avctx->pix_fmt = AV_PIX_FMT_RGB24;

    s->prev_frame = av_frame_alloc();
    if (!s->prev_frame)
        return AVERROR(ENOMEM);

    return 0;
}

189 190 191 192 193 194 195
static void decode_flush(AVCodecContext *avctx)
{
    ARBCContext *s = avctx->priv_data;

    av_frame_unref(s->prev_frame);
}

196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
static av_cold int decode_close(AVCodecContext *avctx)
{
    ARBCContext *s = avctx->priv_data;

    av_frame_free(&s->prev_frame);

    return 0;
}

AVCodec ff_arbc_decoder = {
    .name           = "arbc",
    .long_name      = NULL_IF_CONFIG_SMALL("Gryphon's Anim Compressor"),
    .type           = AVMEDIA_TYPE_VIDEO,
    .id             = AV_CODEC_ID_ARBC,
    .priv_data_size = sizeof(ARBCContext),
    .init           = decode_init,
    .decode         = decode_frame,
213
    .flush          = decode_flush,
214 215 216 217
    .close          = decode_close,
    .capabilities   = AV_CODEC_CAP_DR1,
    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
};