diff options
author | Paul B Mahol <onemda@gmail.com> | 2022-05-31 12:33:54 +0200 |
---|---|---|
committer | Paul B Mahol <onemda@gmail.com> | 2022-06-05 13:06:54 +0200 |
commit | 973fab565378cbdd0712977152a66f5b17938d51 (patch) | |
tree | 720f0b9c6e8f3cf64c4134d0ae78a5e30d97a577 /libavcodec/qoidec.c | |
parent | c6364b711bad1fe2fbd90e5b2798f87080ddf5ea (diff) |
avcodec: add QOI decoder and demuxer and parser and encoder and muxer
Diffstat (limited to 'libavcodec/qoidec.c')
-rw-r--r-- | libavcodec/qoidec.c | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/libavcodec/qoidec.c b/libavcodec/qoidec.c new file mode 100644 index 0000000000..8a119f7606 --- /dev/null +++ b/libavcodec/qoidec.c @@ -0,0 +1,126 @@ +/* + * QOI image format + * + * 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 <stdlib.h> + +#include "libavutil/imgutils.h" +#include "avcodec.h" +#include "internal.h" +#include "bytestream.h" +#include "codec_internal.h" +#include "thread.h" +#include "qoi.h" + +static int qoi_decode_frame(AVCodecContext *avctx, AVFrame *p, + int *got_frame, AVPacket *avpkt) +{ + const uint8_t *buf = avpkt->data; + int ret, buf_size = avpkt->size; + int width, height, channels, space, run = 0; + uint8_t index[64][4] = { 0 }; + uint8_t px[4] = { 0, 0, 0, 255 }; + GetByteContext gb; + uint8_t *dst; + uint64_t len; + + if (buf_size < 20) + return AVERROR_INVALIDDATA; + + bytestream2_init(&gb, buf, buf_size); + bytestream2_skip(&gb, 4); + width = bytestream2_get_be32(&gb); + height = bytestream2_get_be32(&gb); + channels = bytestream2_get_byte(&gb); + space = bytestream2_get_byte(&gb); + switch (space) { + case 0: break; + case 1: avctx->color_trc = AVCOL_TRC_LINEAR; break; + default: return AVERROR_INVALIDDATA; + } + + if ((ret = ff_set_dimensions(avctx, width, height)) < 0) + return ret; + + switch (channels) { + case 3: avctx->pix_fmt = AV_PIX_FMT_RGB24; break; + case 4: avctx->pix_fmt = AV_PIX_FMT_RGBA; break; + default: return AVERROR_INVALIDDATA; + } + + if ((ret = ff_thread_get_buffer(avctx, p, 0)) < 0) + return ret; + + dst = p->data[0]; + len = width * height * channels; + for (int n = 0, off_x = 0, off_y = 0; n < len; n += channels, off_x++) { + if (off_x >= width) { + off_x = 0; + off_y++; + dst += p->linesize[0]; + } + if (run > 0) { + run--; + } else if (bytestream2_get_bytes_left(&gb) > 4) { + int chunk = bytestream2_get_byteu(&gb); + + if (chunk == QOI_OP_RGB) { + bytestream2_get_bufferu(&gb, px, 3); + } else if (chunk == QOI_OP_RGBA) { + bytestream2_get_bufferu(&gb, px, 4); + } else if ((chunk & QOI_MASK_2) == QOI_OP_INDEX) { + memcpy(px, index[chunk], 4); + } else if ((chunk & QOI_MASK_2) == QOI_OP_DIFF) { + px[0] += ((chunk >> 4) & 0x03) - 2; + px[1] += ((chunk >> 2) & 0x03) - 2; + px[2] += ( chunk & 0x03) - 2; + } else if ((chunk & QOI_MASK_2) == QOI_OP_LUMA) { + int b2 = bytestream2_get_byteu(&gb); + int vg = (chunk & 0x3f) - 32; + px[0] += vg - 8 + ((b2 >> 4) & 0x0f); + px[1] += vg; + px[2] += vg - 8 + (b2 & 0x0f); + } else if ((chunk & QOI_MASK_2) == QOI_OP_RUN) { + run = chunk & 0x3f; + } + + memcpy(index[QOI_COLOR_HASH(px) & 63], px, 4); + } else { + break; + } + + memcpy(&dst[off_x * channels], px, channels); + } + + p->key_frame = 1; + p->pict_type = AV_PICTURE_TYPE_I; + + *got_frame = 1; + + return buf_size; +} + +const FFCodec ff_qoi_decoder = { + .p.name = "qoi", + .p.long_name = NULL_IF_CONFIG_SMALL("QOI (Quite OK Image format) image"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_QOI, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + FF_CODEC_DECODE_CB(qoi_decode_frame), +}; |