1/* 2 * QOI image format 3 * 4 * This file is part of FFmpeg. 5 * 6 * FFmpeg is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * FFmpeg is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with FFmpeg; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21#include <stdlib.h> 22 23#include "libavutil/imgutils.h" 24#include "avcodec.h" 25#include "internal.h" 26#include "bytestream.h" 27#include "codec_internal.h" 28#include "thread.h" 29#include "qoi.h" 30 31static int qoi_decode_frame(AVCodecContext *avctx, AVFrame *p, 32 int *got_frame, AVPacket *avpkt) 33{ 34 const uint8_t *buf = avpkt->data; 35 int ret, buf_size = avpkt->size; 36 int width, height, channels, space, run = 0; 37 uint8_t index[64][4] = { 0 }; 38 uint8_t px[4] = { 0, 0, 0, 255 }; 39 GetByteContext gb; 40 uint8_t *dst; 41 uint64_t len; 42 43 if (buf_size < 20) 44 return AVERROR_INVALIDDATA; 45 46 bytestream2_init(&gb, buf, buf_size); 47 bytestream2_skip(&gb, 4); 48 width = bytestream2_get_be32(&gb); 49 height = bytestream2_get_be32(&gb); 50 channels = bytestream2_get_byte(&gb); 51 space = bytestream2_get_byte(&gb); 52 switch (space) { 53 case 0: break; 54 case 1: avctx->color_trc = AVCOL_TRC_LINEAR; break; 55 default: return AVERROR_INVALIDDATA; 56 } 57 58 if ((ret = ff_set_dimensions(avctx, width, height)) < 0) 59 return ret; 60 61 switch (channels) { 62 case 3: avctx->pix_fmt = AV_PIX_FMT_RGB24; break; 63 case 4: avctx->pix_fmt = AV_PIX_FMT_RGBA; break; 64 default: return AVERROR_INVALIDDATA; 65 } 66 67 if ((ret = ff_thread_get_buffer(avctx, p, 0)) < 0) 68 return ret; 69 70 dst = p->data[0]; 71 len = width * height * channels; 72 for (int n = 0, off_x = 0; n < len; n += channels, off_x++) { 73 if (off_x >= width) { 74 off_x = 0; 75 dst += p->linesize[0]; 76 } 77 if (run > 0) { 78 run--; 79 } else if (bytestream2_get_bytes_left(&gb) > 4) { 80 int chunk = bytestream2_get_byteu(&gb); 81 82 if (chunk == QOI_OP_RGB) { 83 bytestream2_get_bufferu(&gb, px, 3); 84 } else if (chunk == QOI_OP_RGBA) { 85 bytestream2_get_bufferu(&gb, px, 4); 86 } else if ((chunk & QOI_MASK_2) == QOI_OP_INDEX) { 87 memcpy(px, index[chunk], 4); 88 } else if ((chunk & QOI_MASK_2) == QOI_OP_DIFF) { 89 px[0] += ((chunk >> 4) & 0x03) - 2; 90 px[1] += ((chunk >> 2) & 0x03) - 2; 91 px[2] += ( chunk & 0x03) - 2; 92 } else if ((chunk & QOI_MASK_2) == QOI_OP_LUMA) { 93 int b2 = bytestream2_get_byteu(&gb); 94 int vg = (chunk & 0x3f) - 32; 95 px[0] += vg - 8 + ((b2 >> 4) & 0x0f); 96 px[1] += vg; 97 px[2] += vg - 8 + (b2 & 0x0f); 98 } else if ((chunk & QOI_MASK_2) == QOI_OP_RUN) { 99 run = chunk & 0x3f; 100 } 101 102 memcpy(index[QOI_COLOR_HASH(px) & 63], px, 4); 103 } else { 104 break; 105 } 106 107 memcpy(&dst[off_x * channels], px, channels); 108 } 109 110 p->key_frame = 1; 111 p->pict_type = AV_PICTURE_TYPE_I; 112 113 *got_frame = 1; 114 115 return buf_size; 116} 117 118const FFCodec ff_qoi_decoder = { 119 .p.name = "qoi", 120 .p.long_name = NULL_IF_CONFIG_SMALL("QOI (Quite OK Image format) image"), 121 .p.type = AVMEDIA_TYPE_VIDEO, 122 .p.id = AV_CODEC_ID_QOI, 123 .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, 124 FF_CODEC_DECODE_CB(qoi_decode_frame), 125}; 126