/src/ffmpeg/libavcodec/qoienc.c
Line | Count | Source |
1 | | /* |
2 | | * QOI image format encoder |
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 "libavutil/imgutils.h" |
22 | | #include "avcodec.h" |
23 | | #include "bytestream.h" |
24 | | #include "codec_internal.h" |
25 | | #include "encode.h" |
26 | | #include "qoi.h" |
27 | | |
28 | | static int qoi_encode_frame(AVCodecContext *avctx, AVPacket *pkt, |
29 | | const AVFrame *pict, int *got_packet) |
30 | 6.81k | { |
31 | 6.81k | const int channels = 3 + (avctx->pix_fmt == AV_PIX_FMT_RGBA); |
32 | 6.81k | uint8_t px_prev[4] = { 0, 0, 0, 255 }; |
33 | 6.81k | uint8_t px[4] = { 0, 0, 0, 255 }; |
34 | 6.81k | uint8_t index[64][4] = { 0 }; |
35 | 6.81k | int64_t packet_size; |
36 | 6.81k | uint8_t *buf; |
37 | 6.81k | const uint8_t *src; |
38 | 6.81k | int ret, run = 0; |
39 | | |
40 | 6.81k | packet_size = avctx->width * avctx->height * (channels + 1LL) + 14LL + 8LL; |
41 | 6.81k | if ((ret = ff_alloc_packet(avctx, pkt, packet_size)) < 0) |
42 | 0 | return ret; |
43 | | |
44 | 6.81k | buf = pkt->data; |
45 | 6.81k | src = pict->data[0]; |
46 | | |
47 | 6.81k | bytestream_put_buffer(&buf, "qoif", 4); |
48 | 6.81k | bytestream_put_be32(&buf, avctx->width); |
49 | 6.81k | bytestream_put_be32(&buf, avctx->height); |
50 | 6.81k | bytestream_put_byte(&buf, channels); |
51 | 6.81k | bytestream_put_byte(&buf, avctx->color_trc == AVCOL_TRC_LINEAR); |
52 | | |
53 | 848k | for (int y = 0; y < avctx->height; y++) { |
54 | 11.2M | for (int x = 0; x < avctx->width; x++) { |
55 | 10.3M | memcpy(px, src + x * channels, channels); |
56 | | |
57 | 10.3M | if (!memcmp(px, px_prev, 4)) { |
58 | 9.83M | run++; |
59 | 9.83M | if (run == 62) { |
60 | 157k | bytestream_put_byte(&buf, QOI_OP_RUN | (run - 1)); |
61 | 157k | run = 0; |
62 | 157k | } |
63 | 9.83M | } else { |
64 | 553k | int index_pos; |
65 | | |
66 | 553k | if (run > 0) { |
67 | 19.5k | bytestream_put_byte(&buf, QOI_OP_RUN | (run - 1)); |
68 | 19.5k | run = 0; |
69 | 19.5k | } |
70 | | |
71 | 553k | index_pos = QOI_COLOR_HASH(px) & 63; |
72 | | |
73 | 553k | if (!memcmp(index[index_pos], px, 4)) { |
74 | 177k | bytestream_put_byte(&buf, QOI_OP_INDEX | index_pos); |
75 | 376k | } else { |
76 | 376k | memcpy(index[index_pos], px, 4); |
77 | | |
78 | 376k | if (px[3] == px_prev[3]) { |
79 | 129k | int8_t vr = px[0] - px_prev[0]; |
80 | 129k | int8_t vg = px[1] - px_prev[1]; |
81 | 129k | int8_t vb = px[2] - px_prev[2]; |
82 | | |
83 | 129k | int8_t vg_r = vr - vg; |
84 | 129k | int8_t vg_b = vb - vg; |
85 | | |
86 | 129k | if (vr > -3 && vr < 2 && |
87 | 35.5k | vg > -3 && vg < 2 && |
88 | 22.3k | vb > -3 && vb < 2) { |
89 | 11.2k | bytestream_put_byte(&buf, QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2)); |
90 | 118k | } else if (vg_r > -9 && vg_r < 8 && |
91 | 38.4k | vg > -33 && vg < 32 && |
92 | 24.8k | vg_b > -9 && vg_b < 8) { |
93 | 11.3k | bytestream_put_byte(&buf, QOI_OP_LUMA | (vg + 32)); |
94 | 11.3k | bytestream_put_byte(&buf, (vg_r + 8) << 4 | (vg_b + 8)); |
95 | 107k | } else { |
96 | 107k | bytestream_put_byte(&buf, QOI_OP_RGB); |
97 | 107k | bytestream_put_byte(&buf, px[0]); |
98 | 107k | bytestream_put_byte(&buf, px[1]); |
99 | 107k | bytestream_put_byte(&buf, px[2]); |
100 | 107k | } |
101 | 246k | } else { |
102 | 246k | bytestream_put_byte(&buf, QOI_OP_RGBA); |
103 | 246k | bytestream_put_byte(&buf, px[0]); |
104 | 246k | bytestream_put_byte(&buf, px[1]); |
105 | 246k | bytestream_put_byte(&buf, px[2]); |
106 | 246k | bytestream_put_byte(&buf, px[3]); |
107 | 246k | } |
108 | 376k | } |
109 | 553k | } |
110 | | |
111 | 10.3M | memcpy(px_prev, px, 4); |
112 | 10.3M | } |
113 | | |
114 | 841k | src += pict->linesize[0]; |
115 | 841k | } |
116 | | |
117 | 6.81k | if (run) |
118 | 2.36k | bytestream_put_byte(&buf, QOI_OP_RUN | (run - 1)); |
119 | | |
120 | 6.81k | bytestream_put_be64(&buf, 0x01); |
121 | | |
122 | 6.81k | pkt->size = buf - pkt->data; |
123 | | |
124 | 6.81k | *got_packet = 1; |
125 | | |
126 | 6.81k | return 0; |
127 | 6.81k | } |
128 | | |
129 | | const FFCodec ff_qoi_encoder = { |
130 | | .p.name = "qoi", |
131 | | CODEC_LONG_NAME("QOI (Quite OK Image format) image"), |
132 | | .p.type = AVMEDIA_TYPE_VIDEO, |
133 | | .p.id = AV_CODEC_ID_QOI, |
134 | | .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | |
135 | | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, |
136 | | FF_CODEC_ENCODE_CB(qoi_encode_frame), |
137 | | CODEC_PIXFMTS(AV_PIX_FMT_RGBA, AV_PIX_FMT_RGB24), |
138 | | }; |