1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * Copyright (c) 2021 Paul B Mahol
3cabdff1aSopenharmony_ci *
4cabdff1aSopenharmony_ci * This file is part of FFmpeg.
5cabdff1aSopenharmony_ci *
6cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
7cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
8cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
9cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
10cabdff1aSopenharmony_ci *
11cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
12cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
13cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14cabdff1aSopenharmony_ci * Lesser General Public License for more details.
15cabdff1aSopenharmony_ci *
16cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
17cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
18cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19cabdff1aSopenharmony_ci */
20cabdff1aSopenharmony_ci
21cabdff1aSopenharmony_ci/**
22cabdff1aSopenharmony_ci * @file
23cabdff1aSopenharmony_ci * OpenEXR encoder
24cabdff1aSopenharmony_ci */
25cabdff1aSopenharmony_ci
26cabdff1aSopenharmony_ci#include <float.h>
27cabdff1aSopenharmony_ci#include <zlib.h>
28cabdff1aSopenharmony_ci
29cabdff1aSopenharmony_ci#include "libavutil/avassert.h"
30cabdff1aSopenharmony_ci#include "libavutil/opt.h"
31cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h"
32cabdff1aSopenharmony_ci#include "libavutil/imgutils.h"
33cabdff1aSopenharmony_ci#include "libavutil/pixdesc.h"
34cabdff1aSopenharmony_ci#include "avcodec.h"
35cabdff1aSopenharmony_ci#include "bytestream.h"
36cabdff1aSopenharmony_ci#include "codec_internal.h"
37cabdff1aSopenharmony_ci#include "encode.h"
38cabdff1aSopenharmony_ci#include "float2half.h"
39cabdff1aSopenharmony_ci
40cabdff1aSopenharmony_cienum ExrCompr {
41cabdff1aSopenharmony_ci    EXR_RAW,
42cabdff1aSopenharmony_ci    EXR_RLE,
43cabdff1aSopenharmony_ci    EXR_ZIP1,
44cabdff1aSopenharmony_ci    EXR_ZIP16,
45cabdff1aSopenharmony_ci    EXR_NBCOMPR,
46cabdff1aSopenharmony_ci};
47cabdff1aSopenharmony_ci
48cabdff1aSopenharmony_cienum ExrPixelType {
49cabdff1aSopenharmony_ci    EXR_UINT,
50cabdff1aSopenharmony_ci    EXR_HALF,
51cabdff1aSopenharmony_ci    EXR_FLOAT,
52cabdff1aSopenharmony_ci    EXR_UNKNOWN,
53cabdff1aSopenharmony_ci};
54cabdff1aSopenharmony_ci
55cabdff1aSopenharmony_cistatic const char abgr_chlist[4] = { 'A', 'B', 'G', 'R' };
56cabdff1aSopenharmony_cistatic const char bgr_chlist[4] = { 'B', 'G', 'R', 'A' };
57cabdff1aSopenharmony_cistatic const char y_chlist[4] = { 'Y' };
58cabdff1aSopenharmony_cistatic const uint8_t gbra_order[4] = { 3, 1, 0, 2 };
59cabdff1aSopenharmony_cistatic const uint8_t gbr_order[4] = { 1, 0, 2, 0 };
60cabdff1aSopenharmony_cistatic const uint8_t y_order[4] = { 0 };
61cabdff1aSopenharmony_ci
62cabdff1aSopenharmony_citypedef struct EXRScanlineData {
63cabdff1aSopenharmony_ci    uint8_t *compressed_data;
64cabdff1aSopenharmony_ci    unsigned int compressed_size;
65cabdff1aSopenharmony_ci
66cabdff1aSopenharmony_ci    uint8_t *uncompressed_data;
67cabdff1aSopenharmony_ci    unsigned int uncompressed_size;
68cabdff1aSopenharmony_ci
69cabdff1aSopenharmony_ci    uint8_t *tmp;
70cabdff1aSopenharmony_ci    unsigned int tmp_size;
71cabdff1aSopenharmony_ci
72cabdff1aSopenharmony_ci    int64_t actual_size;
73cabdff1aSopenharmony_ci} EXRScanlineData;
74cabdff1aSopenharmony_ci
75cabdff1aSopenharmony_citypedef struct EXRContext {
76cabdff1aSopenharmony_ci    const AVClass *class;
77cabdff1aSopenharmony_ci
78cabdff1aSopenharmony_ci    int compression;
79cabdff1aSopenharmony_ci    int pixel_type;
80cabdff1aSopenharmony_ci    int planes;
81cabdff1aSopenharmony_ci    int nb_scanlines;
82cabdff1aSopenharmony_ci    int scanline_height;
83cabdff1aSopenharmony_ci    float gamma;
84cabdff1aSopenharmony_ci    const char *ch_names;
85cabdff1aSopenharmony_ci    const uint8_t *ch_order;
86cabdff1aSopenharmony_ci    PutByteContext pb;
87cabdff1aSopenharmony_ci
88cabdff1aSopenharmony_ci    EXRScanlineData *scanline;
89cabdff1aSopenharmony_ci
90cabdff1aSopenharmony_ci    uint16_t basetable[512];
91cabdff1aSopenharmony_ci    uint8_t shifttable[512];
92cabdff1aSopenharmony_ci} EXRContext;
93cabdff1aSopenharmony_ci
94cabdff1aSopenharmony_cistatic av_cold int encode_init(AVCodecContext *avctx)
95cabdff1aSopenharmony_ci{
96cabdff1aSopenharmony_ci    EXRContext *s = avctx->priv_data;
97cabdff1aSopenharmony_ci
98cabdff1aSopenharmony_ci    float2half_tables(s->basetable, s->shifttable);
99cabdff1aSopenharmony_ci
100cabdff1aSopenharmony_ci    switch (avctx->pix_fmt) {
101cabdff1aSopenharmony_ci    case AV_PIX_FMT_GBRPF32:
102cabdff1aSopenharmony_ci        s->planes = 3;
103cabdff1aSopenharmony_ci        s->ch_names = bgr_chlist;
104cabdff1aSopenharmony_ci        s->ch_order = gbr_order;
105cabdff1aSopenharmony_ci        break;
106cabdff1aSopenharmony_ci    case AV_PIX_FMT_GBRAPF32:
107cabdff1aSopenharmony_ci        s->planes = 4;
108cabdff1aSopenharmony_ci        s->ch_names = abgr_chlist;
109cabdff1aSopenharmony_ci        s->ch_order = gbra_order;
110cabdff1aSopenharmony_ci        break;
111cabdff1aSopenharmony_ci    case AV_PIX_FMT_GRAYF32:
112cabdff1aSopenharmony_ci        s->planes = 1;
113cabdff1aSopenharmony_ci        s->ch_names = y_chlist;
114cabdff1aSopenharmony_ci        s->ch_order = y_order;
115cabdff1aSopenharmony_ci        break;
116cabdff1aSopenharmony_ci    default:
117cabdff1aSopenharmony_ci        av_assert0(0);
118cabdff1aSopenharmony_ci    }
119cabdff1aSopenharmony_ci
120cabdff1aSopenharmony_ci    switch (s->compression) {
121cabdff1aSopenharmony_ci    case EXR_RAW:
122cabdff1aSopenharmony_ci    case EXR_RLE:
123cabdff1aSopenharmony_ci    case EXR_ZIP1:
124cabdff1aSopenharmony_ci        s->scanline_height = 1;
125cabdff1aSopenharmony_ci        s->nb_scanlines = avctx->height;
126cabdff1aSopenharmony_ci        break;
127cabdff1aSopenharmony_ci    case EXR_ZIP16:
128cabdff1aSopenharmony_ci        s->scanline_height = 16;
129cabdff1aSopenharmony_ci        s->nb_scanlines = (avctx->height + s->scanline_height - 1) / s->scanline_height;
130cabdff1aSopenharmony_ci        break;
131cabdff1aSopenharmony_ci    default:
132cabdff1aSopenharmony_ci        av_assert0(0);
133cabdff1aSopenharmony_ci    }
134cabdff1aSopenharmony_ci
135cabdff1aSopenharmony_ci    s->scanline = av_calloc(s->nb_scanlines, sizeof(*s->scanline));
136cabdff1aSopenharmony_ci    if (!s->scanline)
137cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
138cabdff1aSopenharmony_ci
139cabdff1aSopenharmony_ci    return 0;
140cabdff1aSopenharmony_ci}
141cabdff1aSopenharmony_ci
142cabdff1aSopenharmony_cistatic av_cold int encode_close(AVCodecContext *avctx)
143cabdff1aSopenharmony_ci{
144cabdff1aSopenharmony_ci    EXRContext *s = avctx->priv_data;
145cabdff1aSopenharmony_ci
146cabdff1aSopenharmony_ci    for (int y = 0; y < s->nb_scanlines && s->scanline; y++) {
147cabdff1aSopenharmony_ci        EXRScanlineData *scanline = &s->scanline[y];
148cabdff1aSopenharmony_ci
149cabdff1aSopenharmony_ci        av_freep(&scanline->tmp);
150cabdff1aSopenharmony_ci        av_freep(&scanline->compressed_data);
151cabdff1aSopenharmony_ci        av_freep(&scanline->uncompressed_data);
152cabdff1aSopenharmony_ci    }
153cabdff1aSopenharmony_ci
154cabdff1aSopenharmony_ci    av_freep(&s->scanline);
155cabdff1aSopenharmony_ci
156cabdff1aSopenharmony_ci    return 0;
157cabdff1aSopenharmony_ci}
158cabdff1aSopenharmony_ci
159cabdff1aSopenharmony_cistatic void reorder_pixels(uint8_t *dst, const uint8_t *src, ptrdiff_t size)
160cabdff1aSopenharmony_ci{
161cabdff1aSopenharmony_ci    const ptrdiff_t half_size = (size + 1) / 2;
162cabdff1aSopenharmony_ci    uint8_t *t1 = dst;
163cabdff1aSopenharmony_ci    uint8_t *t2 = dst + half_size;
164cabdff1aSopenharmony_ci
165cabdff1aSopenharmony_ci    for (ptrdiff_t i = 0; i < half_size; i++) {
166cabdff1aSopenharmony_ci        t1[i] = *(src++);
167cabdff1aSopenharmony_ci        t2[i] = *(src++);
168cabdff1aSopenharmony_ci    }
169cabdff1aSopenharmony_ci}
170cabdff1aSopenharmony_ci
171cabdff1aSopenharmony_cistatic void predictor(uint8_t *src, ptrdiff_t size)
172cabdff1aSopenharmony_ci{
173cabdff1aSopenharmony_ci    int p = src[0];
174cabdff1aSopenharmony_ci
175cabdff1aSopenharmony_ci    for (ptrdiff_t i = 1; i < size; i++) {
176cabdff1aSopenharmony_ci        int d = src[i] - p + 384;
177cabdff1aSopenharmony_ci
178cabdff1aSopenharmony_ci        p = src[i];
179cabdff1aSopenharmony_ci        src[i] = d;
180cabdff1aSopenharmony_ci    }
181cabdff1aSopenharmony_ci}
182cabdff1aSopenharmony_ci
183cabdff1aSopenharmony_cistatic int64_t rle_compress(uint8_t *out, int64_t out_size,
184cabdff1aSopenharmony_ci                            const uint8_t *in, int64_t in_size)
185cabdff1aSopenharmony_ci{
186cabdff1aSopenharmony_ci    int64_t i = 0, o = 0, run = 1, copy = 0;
187cabdff1aSopenharmony_ci
188cabdff1aSopenharmony_ci    while (i < in_size) {
189cabdff1aSopenharmony_ci        while (i + run < in_size && in[i] == in[i + run] && run < 128)
190cabdff1aSopenharmony_ci            run++;
191cabdff1aSopenharmony_ci
192cabdff1aSopenharmony_ci        if (run >= 3) {
193cabdff1aSopenharmony_ci            if (o + 2 >= out_size)
194cabdff1aSopenharmony_ci                return -1;
195cabdff1aSopenharmony_ci            out[o++] = run - 1;
196cabdff1aSopenharmony_ci            out[o++] = in[i];
197cabdff1aSopenharmony_ci            i += run;
198cabdff1aSopenharmony_ci        } else {
199cabdff1aSopenharmony_ci            if (i + run < in_size)
200cabdff1aSopenharmony_ci                copy += run;
201cabdff1aSopenharmony_ci            while (i + copy < in_size && copy < 127 && in[i + copy] != in[i + copy - 1])
202cabdff1aSopenharmony_ci                copy++;
203cabdff1aSopenharmony_ci
204cabdff1aSopenharmony_ci            if (o + 1 + copy >= out_size)
205cabdff1aSopenharmony_ci                return -1;
206cabdff1aSopenharmony_ci            out[o++] = -copy;
207cabdff1aSopenharmony_ci
208cabdff1aSopenharmony_ci            for (int x = 0; x < copy; x++)
209cabdff1aSopenharmony_ci                out[o + x] = in[i + x];
210cabdff1aSopenharmony_ci
211cabdff1aSopenharmony_ci            o += copy;
212cabdff1aSopenharmony_ci            i += copy;
213cabdff1aSopenharmony_ci            copy = 0;
214cabdff1aSopenharmony_ci        }
215cabdff1aSopenharmony_ci
216cabdff1aSopenharmony_ci        run = 1;
217cabdff1aSopenharmony_ci    }
218cabdff1aSopenharmony_ci
219cabdff1aSopenharmony_ci    return o;
220cabdff1aSopenharmony_ci}
221cabdff1aSopenharmony_ci
222cabdff1aSopenharmony_cistatic int encode_scanline_rle(EXRContext *s, const AVFrame *frame)
223cabdff1aSopenharmony_ci{
224cabdff1aSopenharmony_ci    const int64_t element_size = s->pixel_type == EXR_HALF ? 2LL : 4LL;
225cabdff1aSopenharmony_ci
226cabdff1aSopenharmony_ci    for (int y = 0; y < frame->height; y++) {
227cabdff1aSopenharmony_ci        EXRScanlineData *scanline = &s->scanline[y];
228cabdff1aSopenharmony_ci        int64_t tmp_size = element_size * s->planes * frame->width;
229cabdff1aSopenharmony_ci        int64_t max_compressed_size = tmp_size * 3 / 2;
230cabdff1aSopenharmony_ci
231cabdff1aSopenharmony_ci        av_fast_padded_malloc(&scanline->uncompressed_data, &scanline->uncompressed_size, tmp_size);
232cabdff1aSopenharmony_ci        if (!scanline->uncompressed_data)
233cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
234cabdff1aSopenharmony_ci
235cabdff1aSopenharmony_ci        av_fast_padded_malloc(&scanline->tmp, &scanline->tmp_size, tmp_size);
236cabdff1aSopenharmony_ci        if (!scanline->tmp)
237cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
238cabdff1aSopenharmony_ci
239cabdff1aSopenharmony_ci        av_fast_padded_malloc(&scanline->compressed_data, &scanline->compressed_size, max_compressed_size);
240cabdff1aSopenharmony_ci        if (!scanline->compressed_data)
241cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
242cabdff1aSopenharmony_ci
243cabdff1aSopenharmony_ci        switch (s->pixel_type) {
244cabdff1aSopenharmony_ci        case EXR_FLOAT:
245cabdff1aSopenharmony_ci            for (int p = 0; p < s->planes; p++) {
246cabdff1aSopenharmony_ci                int ch = s->ch_order[p];
247cabdff1aSopenharmony_ci
248cabdff1aSopenharmony_ci                memcpy(scanline->uncompressed_data + frame->width * 4 * p,
249cabdff1aSopenharmony_ci                       frame->data[ch] + y * frame->linesize[ch], frame->width * 4);
250cabdff1aSopenharmony_ci            }
251cabdff1aSopenharmony_ci            break;
252cabdff1aSopenharmony_ci        case EXR_HALF:
253cabdff1aSopenharmony_ci            for (int p = 0; p < s->planes; p++) {
254cabdff1aSopenharmony_ci                int ch = s->ch_order[p];
255cabdff1aSopenharmony_ci                uint16_t *dst = (uint16_t *)(scanline->uncompressed_data + frame->width * 2 * p);
256cabdff1aSopenharmony_ci                uint32_t *src = (uint32_t *)(frame->data[ch] + y * frame->linesize[ch]);
257cabdff1aSopenharmony_ci
258cabdff1aSopenharmony_ci                for (int x = 0; x < frame->width; x++)
259cabdff1aSopenharmony_ci                    dst[x] = float2half(src[x], s->basetable, s->shifttable);
260cabdff1aSopenharmony_ci            }
261cabdff1aSopenharmony_ci            break;
262cabdff1aSopenharmony_ci        }
263cabdff1aSopenharmony_ci
264cabdff1aSopenharmony_ci        reorder_pixels(scanline->tmp, scanline->uncompressed_data, tmp_size);
265cabdff1aSopenharmony_ci        predictor(scanline->tmp, tmp_size);
266cabdff1aSopenharmony_ci        scanline->actual_size = rle_compress(scanline->compressed_data,
267cabdff1aSopenharmony_ci                                             max_compressed_size,
268cabdff1aSopenharmony_ci                                             scanline->tmp, tmp_size);
269cabdff1aSopenharmony_ci
270cabdff1aSopenharmony_ci        if (scanline->actual_size <= 0 || scanline->actual_size >= tmp_size) {
271cabdff1aSopenharmony_ci            FFSWAP(uint8_t *, scanline->uncompressed_data, scanline->compressed_data);
272cabdff1aSopenharmony_ci            FFSWAP(int, scanline->uncompressed_size, scanline->compressed_size);
273cabdff1aSopenharmony_ci            scanline->actual_size = tmp_size;
274cabdff1aSopenharmony_ci        }
275cabdff1aSopenharmony_ci    }
276cabdff1aSopenharmony_ci
277cabdff1aSopenharmony_ci    return 0;
278cabdff1aSopenharmony_ci}
279cabdff1aSopenharmony_ci
280cabdff1aSopenharmony_cistatic int encode_scanline_zip(EXRContext *s, const AVFrame *frame)
281cabdff1aSopenharmony_ci{
282cabdff1aSopenharmony_ci    const int64_t element_size = s->pixel_type == EXR_HALF ? 2LL : 4LL;
283cabdff1aSopenharmony_ci
284cabdff1aSopenharmony_ci    for (int y = 0; y < s->nb_scanlines; y++) {
285cabdff1aSopenharmony_ci        EXRScanlineData *scanline = &s->scanline[y];
286cabdff1aSopenharmony_ci        const int scanline_height = FFMIN(s->scanline_height, frame->height - y * s->scanline_height);
287cabdff1aSopenharmony_ci        int64_t tmp_size = element_size * s->planes * frame->width * scanline_height;
288cabdff1aSopenharmony_ci        int64_t max_compressed_size = tmp_size * 3 / 2;
289cabdff1aSopenharmony_ci        unsigned long actual_size, source_size;
290cabdff1aSopenharmony_ci
291cabdff1aSopenharmony_ci        av_fast_padded_malloc(&scanline->uncompressed_data, &scanline->uncompressed_size, tmp_size);
292cabdff1aSopenharmony_ci        if (!scanline->uncompressed_data)
293cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
294cabdff1aSopenharmony_ci
295cabdff1aSopenharmony_ci        av_fast_padded_malloc(&scanline->tmp, &scanline->tmp_size, tmp_size);
296cabdff1aSopenharmony_ci        if (!scanline->tmp)
297cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
298cabdff1aSopenharmony_ci
299cabdff1aSopenharmony_ci        av_fast_padded_malloc(&scanline->compressed_data, &scanline->compressed_size, max_compressed_size);
300cabdff1aSopenharmony_ci        if (!scanline->compressed_data)
301cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
302cabdff1aSopenharmony_ci
303cabdff1aSopenharmony_ci        switch (s->pixel_type) {
304cabdff1aSopenharmony_ci        case EXR_FLOAT:
305cabdff1aSopenharmony_ci            for (int l = 0; l < scanline_height; l++) {
306cabdff1aSopenharmony_ci                const int scanline_size = frame->width * 4 * s->planes;
307cabdff1aSopenharmony_ci
308cabdff1aSopenharmony_ci                for (int p = 0; p < s->planes; p++) {
309cabdff1aSopenharmony_ci                    int ch = s->ch_order[p];
310cabdff1aSopenharmony_ci
311cabdff1aSopenharmony_ci                    memcpy(scanline->uncompressed_data + scanline_size * l + p * frame->width * 4,
312cabdff1aSopenharmony_ci                           frame->data[ch] + (y * s->scanline_height + l) * frame->linesize[ch],
313cabdff1aSopenharmony_ci                           frame->width * 4);
314cabdff1aSopenharmony_ci                }
315cabdff1aSopenharmony_ci            }
316cabdff1aSopenharmony_ci            break;
317cabdff1aSopenharmony_ci        case EXR_HALF:
318cabdff1aSopenharmony_ci            for (int l = 0; l < scanline_height; l++) {
319cabdff1aSopenharmony_ci                const int scanline_size = frame->width * 2 * s->planes;
320cabdff1aSopenharmony_ci
321cabdff1aSopenharmony_ci                for (int p = 0; p < s->planes; p++) {
322cabdff1aSopenharmony_ci                    int ch = s->ch_order[p];
323cabdff1aSopenharmony_ci                    uint16_t *dst = (uint16_t *)(scanline->uncompressed_data + scanline_size * l + p * frame->width * 2);
324cabdff1aSopenharmony_ci                    uint32_t *src = (uint32_t *)(frame->data[ch] + (y * s->scanline_height + l) * frame->linesize[ch]);
325cabdff1aSopenharmony_ci
326cabdff1aSopenharmony_ci                    for (int x = 0; x < frame->width; x++)
327cabdff1aSopenharmony_ci                        dst[x] = float2half(src[x], s->basetable, s->shifttable);
328cabdff1aSopenharmony_ci                }
329cabdff1aSopenharmony_ci            }
330cabdff1aSopenharmony_ci            break;
331cabdff1aSopenharmony_ci        }
332cabdff1aSopenharmony_ci
333cabdff1aSopenharmony_ci        reorder_pixels(scanline->tmp, scanline->uncompressed_data, tmp_size);
334cabdff1aSopenharmony_ci        predictor(scanline->tmp, tmp_size);
335cabdff1aSopenharmony_ci        source_size = tmp_size;
336cabdff1aSopenharmony_ci        actual_size = max_compressed_size;
337cabdff1aSopenharmony_ci        compress(scanline->compressed_data, &actual_size,
338cabdff1aSopenharmony_ci                 scanline->tmp, source_size);
339cabdff1aSopenharmony_ci
340cabdff1aSopenharmony_ci        scanline->actual_size = actual_size;
341cabdff1aSopenharmony_ci        if (scanline->actual_size >= tmp_size) {
342cabdff1aSopenharmony_ci            FFSWAP(uint8_t *, scanline->uncompressed_data, scanline->compressed_data);
343cabdff1aSopenharmony_ci            FFSWAP(int, scanline->uncompressed_size, scanline->compressed_size);
344cabdff1aSopenharmony_ci            scanline->actual_size = tmp_size;
345cabdff1aSopenharmony_ci        }
346cabdff1aSopenharmony_ci    }
347cabdff1aSopenharmony_ci
348cabdff1aSopenharmony_ci    return 0;
349cabdff1aSopenharmony_ci}
350cabdff1aSopenharmony_ci
351cabdff1aSopenharmony_cistatic int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
352cabdff1aSopenharmony_ci                        const AVFrame *frame, int *got_packet)
353cabdff1aSopenharmony_ci{
354cabdff1aSopenharmony_ci    EXRContext *s = avctx->priv_data;
355cabdff1aSopenharmony_ci    PutByteContext *pb = &s->pb;
356cabdff1aSopenharmony_ci    int64_t offset;
357cabdff1aSopenharmony_ci    int ret;
358cabdff1aSopenharmony_ci    int64_t out_size = 2048LL + avctx->height * 16LL +
359cabdff1aSopenharmony_ci                      av_image_get_buffer_size(avctx->pix_fmt,
360cabdff1aSopenharmony_ci                                               avctx->width,
361cabdff1aSopenharmony_ci                                               avctx->height, 64) * 3LL / 2;
362cabdff1aSopenharmony_ci
363cabdff1aSopenharmony_ci    if ((ret = ff_get_encode_buffer(avctx, pkt, out_size, 0)) < 0)
364cabdff1aSopenharmony_ci        return ret;
365cabdff1aSopenharmony_ci
366cabdff1aSopenharmony_ci    bytestream2_init_writer(pb, pkt->data, pkt->size);
367cabdff1aSopenharmony_ci
368cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, 20000630);
369cabdff1aSopenharmony_ci    bytestream2_put_byte(pb, 2);
370cabdff1aSopenharmony_ci    bytestream2_put_le24(pb, 0);
371cabdff1aSopenharmony_ci    bytestream2_put_buffer(pb, "channels\0chlist\0", 16);
372cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, s->planes * 18 + 1);
373cabdff1aSopenharmony_ci
374cabdff1aSopenharmony_ci    for (int p = 0; p < s->planes; p++) {
375cabdff1aSopenharmony_ci        bytestream2_put_byte(pb, s->ch_names[p]);
376cabdff1aSopenharmony_ci        bytestream2_put_byte(pb, 0);
377cabdff1aSopenharmony_ci        bytestream2_put_le32(pb, s->pixel_type);
378cabdff1aSopenharmony_ci        bytestream2_put_le32(pb, 0);
379cabdff1aSopenharmony_ci        bytestream2_put_le32(pb, 1);
380cabdff1aSopenharmony_ci        bytestream2_put_le32(pb, 1);
381cabdff1aSopenharmony_ci    }
382cabdff1aSopenharmony_ci    bytestream2_put_byte(pb, 0);
383cabdff1aSopenharmony_ci
384cabdff1aSopenharmony_ci    bytestream2_put_buffer(pb, "compression\0compression\0", 24);
385cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, 1);
386cabdff1aSopenharmony_ci    bytestream2_put_byte(pb, s->compression);
387cabdff1aSopenharmony_ci
388cabdff1aSopenharmony_ci    bytestream2_put_buffer(pb, "dataWindow\0box2i\0", 17);
389cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, 16);
390cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, 0);
391cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, 0);
392cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, avctx->width - 1);
393cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, avctx->height - 1);
394cabdff1aSopenharmony_ci
395cabdff1aSopenharmony_ci    bytestream2_put_buffer(pb, "displayWindow\0box2i\0", 20);
396cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, 16);
397cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, 0);
398cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, 0);
399cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, avctx->width - 1);
400cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, avctx->height - 1);
401cabdff1aSopenharmony_ci
402cabdff1aSopenharmony_ci    bytestream2_put_buffer(pb, "lineOrder\0lineOrder\0", 20);
403cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, 1);
404cabdff1aSopenharmony_ci    bytestream2_put_byte(pb, 0);
405cabdff1aSopenharmony_ci
406cabdff1aSopenharmony_ci    bytestream2_put_buffer(pb, "screenWindowCenter\0v2f\0", 23);
407cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, 8);
408cabdff1aSopenharmony_ci    bytestream2_put_le64(pb, 0);
409cabdff1aSopenharmony_ci
410cabdff1aSopenharmony_ci    bytestream2_put_buffer(pb, "screenWindowWidth\0float\0", 24);
411cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, 4);
412cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, av_float2int(1.f));
413cabdff1aSopenharmony_ci
414cabdff1aSopenharmony_ci    if (avctx->sample_aspect_ratio.num && avctx->sample_aspect_ratio.den) {
415cabdff1aSopenharmony_ci        bytestream2_put_buffer(pb, "pixelAspectRatio\0float\0", 23);
416cabdff1aSopenharmony_ci        bytestream2_put_le32(pb, 4);
417cabdff1aSopenharmony_ci        bytestream2_put_le32(pb, av_float2int(av_q2d(avctx->sample_aspect_ratio)));
418cabdff1aSopenharmony_ci    }
419cabdff1aSopenharmony_ci
420cabdff1aSopenharmony_ci    if (avctx->framerate.num && avctx->framerate.den) {
421cabdff1aSopenharmony_ci        bytestream2_put_buffer(pb, "framesPerSecond\0rational\0", 25);
422cabdff1aSopenharmony_ci        bytestream2_put_le32(pb, 8);
423cabdff1aSopenharmony_ci        bytestream2_put_le32(pb, avctx->framerate.num);
424cabdff1aSopenharmony_ci        bytestream2_put_le32(pb, avctx->framerate.den);
425cabdff1aSopenharmony_ci    }
426cabdff1aSopenharmony_ci
427cabdff1aSopenharmony_ci    bytestream2_put_buffer(pb, "gamma\0float\0", 12);
428cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, 4);
429cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, av_float2int(s->gamma));
430cabdff1aSopenharmony_ci
431cabdff1aSopenharmony_ci    bytestream2_put_buffer(pb, "writer\0string\0", 14);
432cabdff1aSopenharmony_ci    bytestream2_put_le32(pb, 4);
433cabdff1aSopenharmony_ci    bytestream2_put_buffer(pb, "lavc", 4);
434cabdff1aSopenharmony_ci    bytestream2_put_byte(pb, 0);
435cabdff1aSopenharmony_ci
436cabdff1aSopenharmony_ci    switch (s->compression) {
437cabdff1aSopenharmony_ci    case EXR_RAW:
438cabdff1aSopenharmony_ci        /* nothing to do */
439cabdff1aSopenharmony_ci        break;
440cabdff1aSopenharmony_ci    case EXR_RLE:
441cabdff1aSopenharmony_ci        encode_scanline_rle(s, frame);
442cabdff1aSopenharmony_ci        break;
443cabdff1aSopenharmony_ci    case EXR_ZIP16:
444cabdff1aSopenharmony_ci    case EXR_ZIP1:
445cabdff1aSopenharmony_ci        encode_scanline_zip(s, frame);
446cabdff1aSopenharmony_ci        break;
447cabdff1aSopenharmony_ci    default:
448cabdff1aSopenharmony_ci        av_assert0(0);
449cabdff1aSopenharmony_ci    }
450cabdff1aSopenharmony_ci
451cabdff1aSopenharmony_ci    switch (s->compression) {
452cabdff1aSopenharmony_ci    case EXR_RAW:
453cabdff1aSopenharmony_ci        offset = bytestream2_tell_p(pb) + avctx->height * 8LL;
454cabdff1aSopenharmony_ci
455cabdff1aSopenharmony_ci        if (s->pixel_type == EXR_FLOAT) {
456cabdff1aSopenharmony_ci
457cabdff1aSopenharmony_ci            for (int y = 0; y < avctx->height; y++) {
458cabdff1aSopenharmony_ci                bytestream2_put_le64(pb, offset);
459cabdff1aSopenharmony_ci                offset += avctx->width * s->planes * 4 + 8;
460cabdff1aSopenharmony_ci            }
461cabdff1aSopenharmony_ci
462cabdff1aSopenharmony_ci            for (int y = 0; y < avctx->height; y++) {
463cabdff1aSopenharmony_ci                bytestream2_put_le32(pb, y);
464cabdff1aSopenharmony_ci                bytestream2_put_le32(pb, s->planes * avctx->width * 4);
465cabdff1aSopenharmony_ci                for (int p = 0; p < s->planes; p++) {
466cabdff1aSopenharmony_ci                    int ch = s->ch_order[p];
467cabdff1aSopenharmony_ci                    bytestream2_put_buffer(pb, frame->data[ch] + y * frame->linesize[ch],
468cabdff1aSopenharmony_ci                                           avctx->width * 4);
469cabdff1aSopenharmony_ci                }
470cabdff1aSopenharmony_ci            }
471cabdff1aSopenharmony_ci        } else {
472cabdff1aSopenharmony_ci            for (int y = 0; y < avctx->height; y++) {
473cabdff1aSopenharmony_ci                bytestream2_put_le64(pb, offset);
474cabdff1aSopenharmony_ci                offset += avctx->width * s->planes * 2 + 8;
475cabdff1aSopenharmony_ci            }
476cabdff1aSopenharmony_ci
477cabdff1aSopenharmony_ci            for (int y = 0; y < avctx->height; y++) {
478cabdff1aSopenharmony_ci                bytestream2_put_le32(pb, y);
479cabdff1aSopenharmony_ci                bytestream2_put_le32(pb, s->planes * avctx->width * 2);
480cabdff1aSopenharmony_ci                for (int p = 0; p < s->planes; p++) {
481cabdff1aSopenharmony_ci                    int ch = s->ch_order[p];
482cabdff1aSopenharmony_ci                    uint32_t *src = (uint32_t *)(frame->data[ch] + y * frame->linesize[ch]);
483cabdff1aSopenharmony_ci
484cabdff1aSopenharmony_ci                    for (int x = 0; x < frame->width; x++)
485cabdff1aSopenharmony_ci                        bytestream2_put_le16(pb, float2half(src[x], s->basetable, s->shifttable));
486cabdff1aSopenharmony_ci                }
487cabdff1aSopenharmony_ci            }
488cabdff1aSopenharmony_ci        }
489cabdff1aSopenharmony_ci        break;
490cabdff1aSopenharmony_ci    case EXR_ZIP16:
491cabdff1aSopenharmony_ci    case EXR_ZIP1:
492cabdff1aSopenharmony_ci    case EXR_RLE:
493cabdff1aSopenharmony_ci        offset = bytestream2_tell_p(pb) + s->nb_scanlines * 8LL;
494cabdff1aSopenharmony_ci
495cabdff1aSopenharmony_ci        for (int y = 0; y < s->nb_scanlines; y++) {
496cabdff1aSopenharmony_ci            EXRScanlineData *scanline = &s->scanline[y];
497cabdff1aSopenharmony_ci
498cabdff1aSopenharmony_ci            bytestream2_put_le64(pb, offset);
499cabdff1aSopenharmony_ci            offset += scanline->actual_size + 8;
500cabdff1aSopenharmony_ci        }
501cabdff1aSopenharmony_ci
502cabdff1aSopenharmony_ci        for (int y = 0; y < s->nb_scanlines; y++) {
503cabdff1aSopenharmony_ci            EXRScanlineData *scanline = &s->scanline[y];
504cabdff1aSopenharmony_ci
505cabdff1aSopenharmony_ci            bytestream2_put_le32(pb, y * s->scanline_height);
506cabdff1aSopenharmony_ci            bytestream2_put_le32(pb, scanline->actual_size);
507cabdff1aSopenharmony_ci            bytestream2_put_buffer(pb, scanline->compressed_data,
508cabdff1aSopenharmony_ci                                   scanline->actual_size);
509cabdff1aSopenharmony_ci        }
510cabdff1aSopenharmony_ci        break;
511cabdff1aSopenharmony_ci    default:
512cabdff1aSopenharmony_ci        av_assert0(0);
513cabdff1aSopenharmony_ci    }
514cabdff1aSopenharmony_ci
515cabdff1aSopenharmony_ci    av_shrink_packet(pkt, bytestream2_tell_p(pb));
516cabdff1aSopenharmony_ci
517cabdff1aSopenharmony_ci    *got_packet = 1;
518cabdff1aSopenharmony_ci
519cabdff1aSopenharmony_ci    return 0;
520cabdff1aSopenharmony_ci}
521cabdff1aSopenharmony_ci
522cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(EXRContext, x)
523cabdff1aSopenharmony_ci#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
524cabdff1aSopenharmony_cistatic const AVOption options[] = {
525cabdff1aSopenharmony_ci    { "compression", "set compression type", OFFSET(compression), AV_OPT_TYPE_INT,   {.i64=0}, 0, EXR_NBCOMPR-1, VE, "compr" },
526cabdff1aSopenharmony_ci    { "none",        "none",                 0,                   AV_OPT_TYPE_CONST, {.i64=EXR_RAW}, 0, 0, VE, "compr" },
527cabdff1aSopenharmony_ci    { "rle" ,        "RLE",                  0,                   AV_OPT_TYPE_CONST, {.i64=EXR_RLE}, 0, 0, VE, "compr" },
528cabdff1aSopenharmony_ci    { "zip1",        "ZIP1",                 0,                   AV_OPT_TYPE_CONST, {.i64=EXR_ZIP1}, 0, 0, VE, "compr" },
529cabdff1aSopenharmony_ci    { "zip16",       "ZIP16",                0,                   AV_OPT_TYPE_CONST, {.i64=EXR_ZIP16}, 0, 0, VE, "compr" },
530cabdff1aSopenharmony_ci    { "format", "set pixel type", OFFSET(pixel_type), AV_OPT_TYPE_INT,   {.i64=EXR_FLOAT}, EXR_HALF, EXR_UNKNOWN-1, VE, "pixel" },
531cabdff1aSopenharmony_ci    { "half" ,       NULL,                   0,                   AV_OPT_TYPE_CONST, {.i64=EXR_HALF},  0, 0, VE, "pixel" },
532cabdff1aSopenharmony_ci    { "float",       NULL,                   0,                   AV_OPT_TYPE_CONST, {.i64=EXR_FLOAT}, 0, 0, VE, "pixel" },
533cabdff1aSopenharmony_ci    { "gamma", "set gamma", OFFSET(gamma), AV_OPT_TYPE_FLOAT, {.dbl=1.f}, 0.001, FLT_MAX, VE },
534cabdff1aSopenharmony_ci    { NULL},
535cabdff1aSopenharmony_ci};
536cabdff1aSopenharmony_ci
537cabdff1aSopenharmony_cistatic const AVClass exr_class = {
538cabdff1aSopenharmony_ci    .class_name = "exr",
539cabdff1aSopenharmony_ci    .item_name  = av_default_item_name,
540cabdff1aSopenharmony_ci    .option     = options,
541cabdff1aSopenharmony_ci    .version    = LIBAVUTIL_VERSION_INT,
542cabdff1aSopenharmony_ci};
543cabdff1aSopenharmony_ci
544cabdff1aSopenharmony_ciconst FFCodec ff_exr_encoder = {
545cabdff1aSopenharmony_ci    .p.name         = "exr",
546cabdff1aSopenharmony_ci    .p.long_name    = NULL_IF_CONFIG_SMALL("OpenEXR image"),
547cabdff1aSopenharmony_ci    .priv_data_size = sizeof(EXRContext),
548cabdff1aSopenharmony_ci    .p.priv_class   = &exr_class,
549cabdff1aSopenharmony_ci    .p.type         = AVMEDIA_TYPE_VIDEO,
550cabdff1aSopenharmony_ci    .p.id           = AV_CODEC_ID_EXR,
551cabdff1aSopenharmony_ci    .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS,
552cabdff1aSopenharmony_ci    .init           = encode_init,
553cabdff1aSopenharmony_ci    FF_CODEC_ENCODE_CB(encode_frame),
554cabdff1aSopenharmony_ci    .close          = encode_close,
555cabdff1aSopenharmony_ci    .p.pix_fmts     = (const enum AVPixelFormat[]) {
556cabdff1aSopenharmony_ci                                                 AV_PIX_FMT_GRAYF32,
557cabdff1aSopenharmony_ci                                                 AV_PIX_FMT_GBRPF32,
558cabdff1aSopenharmony_ci                                                 AV_PIX_FMT_GBRAPF32,
559cabdff1aSopenharmony_ci                                                 AV_PIX_FMT_NONE },
560cabdff1aSopenharmony_ci    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
561cabdff1aSopenharmony_ci};
562