1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * DVD subtitle encoding
3cabdff1aSopenharmony_ci * Copyright (c) 2005 Wolfram Gloger
4cabdff1aSopenharmony_ci *
5cabdff1aSopenharmony_ci * This file is part of FFmpeg.
6cabdff1aSopenharmony_ci *
7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
11cabdff1aSopenharmony_ci *
12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15cabdff1aSopenharmony_ci * Lesser General Public License for more details.
16cabdff1aSopenharmony_ci *
17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20cabdff1aSopenharmony_ci */
21cabdff1aSopenharmony_ci#include "avcodec.h"
22cabdff1aSopenharmony_ci#include "bytestream.h"
23cabdff1aSopenharmony_ci#include "codec_internal.h"
24cabdff1aSopenharmony_ci#include "internal.h"
25cabdff1aSopenharmony_ci#include "libavutil/avassert.h"
26cabdff1aSopenharmony_ci#include "libavutil/bprint.h"
27cabdff1aSopenharmony_ci#include "libavutil/imgutils.h"
28cabdff1aSopenharmony_ci#include "libavutil/opt.h"
29cabdff1aSopenharmony_ci
30cabdff1aSopenharmony_citypedef struct {
31cabdff1aSopenharmony_ci    AVClass *class;
32cabdff1aSopenharmony_ci    uint32_t global_palette[16];
33cabdff1aSopenharmony_ci    char *palette_str;
34cabdff1aSopenharmony_ci    int even_rows_fix;
35cabdff1aSopenharmony_ci} DVDSubtitleContext;
36cabdff1aSopenharmony_ci
37cabdff1aSopenharmony_ci// ncnt is the nibble counter
38cabdff1aSopenharmony_ci#define PUTNIBBLE(val)\
39cabdff1aSopenharmony_cido {\
40cabdff1aSopenharmony_ci    if (ncnt++ & 1)\
41cabdff1aSopenharmony_ci        *q++ = bitbuf | ((val) & 0x0f);\
42cabdff1aSopenharmony_ci    else\
43cabdff1aSopenharmony_ci        bitbuf = (val) << 4;\
44cabdff1aSopenharmony_ci} while(0)
45cabdff1aSopenharmony_ci
46cabdff1aSopenharmony_cistatic void dvd_encode_rle(uint8_t **pq,
47cabdff1aSopenharmony_ci                           const uint8_t *bitmap, int linesize,
48cabdff1aSopenharmony_ci                           int w, int h,
49cabdff1aSopenharmony_ci                           const int cmap[256])
50cabdff1aSopenharmony_ci{
51cabdff1aSopenharmony_ci    uint8_t *q;
52cabdff1aSopenharmony_ci    unsigned int bitbuf = 0;
53cabdff1aSopenharmony_ci    int ncnt;
54cabdff1aSopenharmony_ci    int x, y, len, color;
55cabdff1aSopenharmony_ci
56cabdff1aSopenharmony_ci    q = *pq;
57cabdff1aSopenharmony_ci
58cabdff1aSopenharmony_ci    for (y = 0; y < h; ++y) {
59cabdff1aSopenharmony_ci        ncnt = 0;
60cabdff1aSopenharmony_ci        for(x = 0; x < w; x += len) {
61cabdff1aSopenharmony_ci            color = bitmap[x];
62cabdff1aSopenharmony_ci            for (len=1; x+len < w; ++len)
63cabdff1aSopenharmony_ci                if (bitmap[x+len] != color)
64cabdff1aSopenharmony_ci                    break;
65cabdff1aSopenharmony_ci            color = cmap[color];
66cabdff1aSopenharmony_ci            av_assert0(color < 4);
67cabdff1aSopenharmony_ci            if (len < 0x04) {
68cabdff1aSopenharmony_ci                PUTNIBBLE((len << 2)|color);
69cabdff1aSopenharmony_ci            } else if (len < 0x10) {
70cabdff1aSopenharmony_ci                PUTNIBBLE(len >> 2);
71cabdff1aSopenharmony_ci                PUTNIBBLE((len << 2)|color);
72cabdff1aSopenharmony_ci            } else if (len < 0x40) {
73cabdff1aSopenharmony_ci                PUTNIBBLE(0);
74cabdff1aSopenharmony_ci                PUTNIBBLE(len >> 2);
75cabdff1aSopenharmony_ci                PUTNIBBLE((len << 2)|color);
76cabdff1aSopenharmony_ci            } else if (x+len == w) {
77cabdff1aSopenharmony_ci                PUTNIBBLE(0);
78cabdff1aSopenharmony_ci                PUTNIBBLE(0);
79cabdff1aSopenharmony_ci                PUTNIBBLE(0);
80cabdff1aSopenharmony_ci                PUTNIBBLE(color);
81cabdff1aSopenharmony_ci            } else {
82cabdff1aSopenharmony_ci                if (len > 0xff)
83cabdff1aSopenharmony_ci                    len = 0xff;
84cabdff1aSopenharmony_ci                PUTNIBBLE(0);
85cabdff1aSopenharmony_ci                PUTNIBBLE(len >> 6);
86cabdff1aSopenharmony_ci                PUTNIBBLE(len >> 2);
87cabdff1aSopenharmony_ci                PUTNIBBLE((len << 2)|color);
88cabdff1aSopenharmony_ci            }
89cabdff1aSopenharmony_ci        }
90cabdff1aSopenharmony_ci        /* end of line */
91cabdff1aSopenharmony_ci        if (ncnt & 1)
92cabdff1aSopenharmony_ci            PUTNIBBLE(0);
93cabdff1aSopenharmony_ci        bitmap += linesize;
94cabdff1aSopenharmony_ci    }
95cabdff1aSopenharmony_ci
96cabdff1aSopenharmony_ci    *pq = q;
97cabdff1aSopenharmony_ci}
98cabdff1aSopenharmony_ci
99cabdff1aSopenharmony_cistatic int color_distance(uint32_t a, uint32_t b)
100cabdff1aSopenharmony_ci{
101cabdff1aSopenharmony_ci    int r = 0, d, i;
102cabdff1aSopenharmony_ci    int alpha_a = 8, alpha_b = 8;
103cabdff1aSopenharmony_ci
104cabdff1aSopenharmony_ci    for (i = 24; i >= 0; i -= 8) {
105cabdff1aSopenharmony_ci        d = alpha_a * (int)((a >> i) & 0xFF) -
106cabdff1aSopenharmony_ci            alpha_b * (int)((b >> i) & 0xFF);
107cabdff1aSopenharmony_ci        r += d * d;
108cabdff1aSopenharmony_ci        alpha_a = a >> 28;
109cabdff1aSopenharmony_ci        alpha_b = b >> 28;
110cabdff1aSopenharmony_ci    }
111cabdff1aSopenharmony_ci    return r;
112cabdff1aSopenharmony_ci}
113cabdff1aSopenharmony_ci
114cabdff1aSopenharmony_ci/**
115cabdff1aSopenharmony_ci * Count colors used in a rectangle, quantizing alpha and grouping by
116cabdff1aSopenharmony_ci * nearest global palette entry.
117cabdff1aSopenharmony_ci */
118cabdff1aSopenharmony_cistatic void count_colors(AVCodecContext *avctx, unsigned hits[33],
119cabdff1aSopenharmony_ci                         const AVSubtitleRect *r)
120cabdff1aSopenharmony_ci{
121cabdff1aSopenharmony_ci    DVDSubtitleContext *dvdc = avctx->priv_data;
122cabdff1aSopenharmony_ci    unsigned count[256] = { 0 };
123cabdff1aSopenharmony_ci    uint32_t *palette = (uint32_t *)r->data[1];
124cabdff1aSopenharmony_ci    uint32_t color;
125cabdff1aSopenharmony_ci    int x, y, i, j, match, d, best_d, av_uninit(best_j);
126cabdff1aSopenharmony_ci    uint8_t *p = r->data[0];
127cabdff1aSopenharmony_ci
128cabdff1aSopenharmony_ci    for (y = 0; y < r->h; y++) {
129cabdff1aSopenharmony_ci        for (x = 0; x < r->w; x++)
130cabdff1aSopenharmony_ci            count[*(p++)]++;
131cabdff1aSopenharmony_ci        p += r->linesize[0] - r->w;
132cabdff1aSopenharmony_ci    }
133cabdff1aSopenharmony_ci    for (i = 0; i < 256; i++) {
134cabdff1aSopenharmony_ci        if (!count[i]) /* avoid useless search */
135cabdff1aSopenharmony_ci            continue;
136cabdff1aSopenharmony_ci        color = palette[i];
137cabdff1aSopenharmony_ci        /* 0: transparent, 1-16: semi-transparent, 17-33 opaque */
138cabdff1aSopenharmony_ci        match = color < 0x33000000 ? 0 : color < 0xCC000000 ? 1 : 17;
139cabdff1aSopenharmony_ci        if (match) {
140cabdff1aSopenharmony_ci            best_d = INT_MAX;
141cabdff1aSopenharmony_ci            for (j = 0; j < 16; j++) {
142cabdff1aSopenharmony_ci                d = color_distance(0xFF000000 | color,
143cabdff1aSopenharmony_ci                                   0xFF000000 | dvdc->global_palette[j]);
144cabdff1aSopenharmony_ci                if (d < best_d) {
145cabdff1aSopenharmony_ci                    best_d = d;
146cabdff1aSopenharmony_ci                    best_j = j;
147cabdff1aSopenharmony_ci                }
148cabdff1aSopenharmony_ci            }
149cabdff1aSopenharmony_ci            match += best_j;
150cabdff1aSopenharmony_ci        }
151cabdff1aSopenharmony_ci        hits[match] += count[i];
152cabdff1aSopenharmony_ci    }
153cabdff1aSopenharmony_ci}
154cabdff1aSopenharmony_ci
155cabdff1aSopenharmony_cistatic void select_palette(AVCodecContext *avctx, int out_palette[4],
156cabdff1aSopenharmony_ci                           int out_alpha[4], unsigned hits[33])
157cabdff1aSopenharmony_ci{
158cabdff1aSopenharmony_ci    DVDSubtitleContext *dvdc = avctx->priv_data;
159cabdff1aSopenharmony_ci    int i, j, bright, mult;
160cabdff1aSopenharmony_ci    uint32_t color;
161cabdff1aSopenharmony_ci    int selected[4] = { 0 };
162cabdff1aSopenharmony_ci    uint32_t pseudopal[33] = { 0 };
163cabdff1aSopenharmony_ci    uint32_t refcolor[3] = { 0x00000000, 0xFFFFFFFF, 0xFF000000 };
164cabdff1aSopenharmony_ci
165cabdff1aSopenharmony_ci    /* Bonus for transparent: if the rectangle fits tightly the text, the
166cabdff1aSopenharmony_ci       background color can be quite rare, but it would be ugly without it */
167cabdff1aSopenharmony_ci    hits[0] *= 16;
168cabdff1aSopenharmony_ci    /* Bonus for bright colors */
169cabdff1aSopenharmony_ci    for (i = 0; i < 16; i++) {
170cabdff1aSopenharmony_ci        if (!(hits[1 + i] + hits[17 + i]))
171cabdff1aSopenharmony_ci            continue; /* skip unused colors to gain time */
172cabdff1aSopenharmony_ci        color = dvdc->global_palette[i];
173cabdff1aSopenharmony_ci        bright = 0;
174cabdff1aSopenharmony_ci        for (j = 0; j < 3; j++, color >>= 8)
175cabdff1aSopenharmony_ci            bright += (color & 0xFF) < 0x40 || (color & 0xFF) >= 0xC0;
176cabdff1aSopenharmony_ci        mult = 2 + FFMIN(bright, 2);
177cabdff1aSopenharmony_ci        hits[ 1 + i] *= mult;
178cabdff1aSopenharmony_ci        hits[17 + i] *= mult;
179cabdff1aSopenharmony_ci    }
180cabdff1aSopenharmony_ci
181cabdff1aSopenharmony_ci    /* Select four most frequent colors */
182cabdff1aSopenharmony_ci    for (i = 0; i < 4; i++) {
183cabdff1aSopenharmony_ci        for (j = 0; j < 33; j++)
184cabdff1aSopenharmony_ci            if (hits[j] > hits[selected[i]])
185cabdff1aSopenharmony_ci                selected[i] = j;
186cabdff1aSopenharmony_ci        hits[selected[i]] = 0;
187cabdff1aSopenharmony_ci    }
188cabdff1aSopenharmony_ci
189cabdff1aSopenharmony_ci    /* Order the colors like in most DVDs:
190cabdff1aSopenharmony_ci       0: background, 1: foreground, 2: outline */
191cabdff1aSopenharmony_ci    for (i = 0; i < 16; i++) {
192cabdff1aSopenharmony_ci        pseudopal[ 1 + i] = 0x80000000 | dvdc->global_palette[i];
193cabdff1aSopenharmony_ci        pseudopal[17 + i] = 0xFF000000 | dvdc->global_palette[i];
194cabdff1aSopenharmony_ci    }
195cabdff1aSopenharmony_ci    for (i = 0; i < 3; i++) {
196cabdff1aSopenharmony_ci        int best_d = color_distance(refcolor[i], pseudopal[selected[i]]);
197cabdff1aSopenharmony_ci        for (j = i + 1; j < 4; j++) {
198cabdff1aSopenharmony_ci            int d = color_distance(refcolor[i], pseudopal[selected[j]]);
199cabdff1aSopenharmony_ci            if (d < best_d) {
200cabdff1aSopenharmony_ci                FFSWAP(int, selected[i], selected[j]);
201cabdff1aSopenharmony_ci                best_d = d;
202cabdff1aSopenharmony_ci            }
203cabdff1aSopenharmony_ci        }
204cabdff1aSopenharmony_ci    }
205cabdff1aSopenharmony_ci
206cabdff1aSopenharmony_ci    /* Output */
207cabdff1aSopenharmony_ci    for (i = 0; i < 4; i++) {
208cabdff1aSopenharmony_ci        out_palette[i] = selected[i] ? (selected[i] - 1) & 0xF : 0;
209cabdff1aSopenharmony_ci        out_alpha  [i] = !selected[i] ? 0 : selected[i] < 17 ? 0x80 : 0xFF;
210cabdff1aSopenharmony_ci    }
211cabdff1aSopenharmony_ci}
212cabdff1aSopenharmony_ci
213cabdff1aSopenharmony_cistatic void build_color_map(AVCodecContext *avctx, int cmap[],
214cabdff1aSopenharmony_ci                            const uint32_t palette[],
215cabdff1aSopenharmony_ci                            const int out_palette[], unsigned int const out_alpha[])
216cabdff1aSopenharmony_ci{
217cabdff1aSopenharmony_ci    DVDSubtitleContext *dvdc = avctx->priv_data;
218cabdff1aSopenharmony_ci    int i, j, d, best_d;
219cabdff1aSopenharmony_ci    uint32_t pseudopal[4];
220cabdff1aSopenharmony_ci
221cabdff1aSopenharmony_ci    for (i = 0; i < 4; i++)
222cabdff1aSopenharmony_ci        pseudopal[i] = (out_alpha[i] << 24) |
223cabdff1aSopenharmony_ci                       dvdc->global_palette[out_palette[i]];
224cabdff1aSopenharmony_ci    for (i = 0; i < 256; i++) {
225cabdff1aSopenharmony_ci        best_d = INT_MAX;
226cabdff1aSopenharmony_ci        for (j = 0; j < 4; j++) {
227cabdff1aSopenharmony_ci            d = color_distance(pseudopal[j], palette[i]);
228cabdff1aSopenharmony_ci            if (d < best_d) {
229cabdff1aSopenharmony_ci                cmap[i] = j;
230cabdff1aSopenharmony_ci                best_d = d;
231cabdff1aSopenharmony_ci            }
232cabdff1aSopenharmony_ci        }
233cabdff1aSopenharmony_ci    }
234cabdff1aSopenharmony_ci}
235cabdff1aSopenharmony_ci
236cabdff1aSopenharmony_cistatic void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
237cabdff1aSopenharmony_ci{
238cabdff1aSopenharmony_ci    int x, y;
239cabdff1aSopenharmony_ci    uint8_t *p, *q;
240cabdff1aSopenharmony_ci
241cabdff1aSopenharmony_ci    p = src->data[0];
242cabdff1aSopenharmony_ci    q = dst->data[0] + (src->x - dst->x) +
243cabdff1aSopenharmony_ci                            (src->y - dst->y) * dst->linesize[0];
244cabdff1aSopenharmony_ci    for (y = 0; y < src->h; y++) {
245cabdff1aSopenharmony_ci        for (x = 0; x < src->w; x++)
246cabdff1aSopenharmony_ci            *(q++) = cmap[*(p++)];
247cabdff1aSopenharmony_ci        p += src->linesize[0] - src->w;
248cabdff1aSopenharmony_ci        q += dst->linesize[0] - src->w;
249cabdff1aSopenharmony_ci    }
250cabdff1aSopenharmony_ci}
251cabdff1aSopenharmony_ci
252cabdff1aSopenharmony_cistatic int encode_dvd_subtitles(AVCodecContext *avctx,
253cabdff1aSopenharmony_ci                                uint8_t *outbuf, int outbuf_size,
254cabdff1aSopenharmony_ci                                const AVSubtitle *h)
255cabdff1aSopenharmony_ci{
256cabdff1aSopenharmony_ci    DVDSubtitleContext *dvdc = avctx->priv_data;
257cabdff1aSopenharmony_ci    uint8_t *q, *qq;
258cabdff1aSopenharmony_ci    int offset1, offset2;
259cabdff1aSopenharmony_ci    int i, rects = h->num_rects, ret;
260cabdff1aSopenharmony_ci    unsigned global_palette_hits[33] = { 0 };
261cabdff1aSopenharmony_ci    int cmap[256];
262cabdff1aSopenharmony_ci    int out_palette[4];
263cabdff1aSopenharmony_ci    int out_alpha[4];
264cabdff1aSopenharmony_ci    AVSubtitleRect vrect;
265cabdff1aSopenharmony_ci    uint8_t *vrect_data = NULL;
266cabdff1aSopenharmony_ci    int x2, y2;
267cabdff1aSopenharmony_ci    int forced = 0;
268cabdff1aSopenharmony_ci
269cabdff1aSopenharmony_ci    if (rects == 0 || !h->rects)
270cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
271cabdff1aSopenharmony_ci    for (i = 0; i < rects; i++)
272cabdff1aSopenharmony_ci        if (h->rects[i]->type != SUBTITLE_BITMAP) {
273cabdff1aSopenharmony_ci            av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
274cabdff1aSopenharmony_ci            return AVERROR(EINVAL);
275cabdff1aSopenharmony_ci        }
276cabdff1aSopenharmony_ci    /* Mark this subtitle forced if any of the rectangles is forced. */
277cabdff1aSopenharmony_ci    for (i = 0; i < rects; i++)
278cabdff1aSopenharmony_ci        if ((h->rects[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
279cabdff1aSopenharmony_ci            forced = 1;
280cabdff1aSopenharmony_ci            break;
281cabdff1aSopenharmony_ci        }
282cabdff1aSopenharmony_ci
283cabdff1aSopenharmony_ci    vrect = *h->rects[0];
284cabdff1aSopenharmony_ci
285cabdff1aSopenharmony_ci    if (rects > 1) {
286cabdff1aSopenharmony_ci        /* DVD subtitles can have only one rectangle: build a virtual
287cabdff1aSopenharmony_ci           rectangle containing all actual rectangles.
288cabdff1aSopenharmony_ci           The data of the rectangles will be copied later, when the palette
289cabdff1aSopenharmony_ci           is decided, because the rectangles may have different palettes. */
290cabdff1aSopenharmony_ci        int xmin = h->rects[0]->x, xmax = xmin + h->rects[0]->w;
291cabdff1aSopenharmony_ci        int ymin = h->rects[0]->y, ymax = ymin + h->rects[0]->h;
292cabdff1aSopenharmony_ci        for (i = 1; i < rects; i++) {
293cabdff1aSopenharmony_ci            xmin = FFMIN(xmin, h->rects[i]->x);
294cabdff1aSopenharmony_ci            ymin = FFMIN(ymin, h->rects[i]->y);
295cabdff1aSopenharmony_ci            xmax = FFMAX(xmax, h->rects[i]->x + h->rects[i]->w);
296cabdff1aSopenharmony_ci            ymax = FFMAX(ymax, h->rects[i]->y + h->rects[i]->h);
297cabdff1aSopenharmony_ci        }
298cabdff1aSopenharmony_ci        vrect.x = xmin;
299cabdff1aSopenharmony_ci        vrect.y = ymin;
300cabdff1aSopenharmony_ci        vrect.w = xmax - xmin;
301cabdff1aSopenharmony_ci        vrect.h = ymax - ymin;
302cabdff1aSopenharmony_ci        if ((ret = av_image_check_size(vrect.w, vrect.h, 0, avctx)) < 0)
303cabdff1aSopenharmony_ci            return ret;
304cabdff1aSopenharmony_ci
305cabdff1aSopenharmony_ci        /* Count pixels outside the virtual rectangle as transparent */
306cabdff1aSopenharmony_ci        global_palette_hits[0] = vrect.w * vrect.h;
307cabdff1aSopenharmony_ci        for (i = 0; i < rects; i++)
308cabdff1aSopenharmony_ci            global_palette_hits[0] -= h->rects[i]->w * h->rects[i]->h;
309cabdff1aSopenharmony_ci    }
310cabdff1aSopenharmony_ci
311cabdff1aSopenharmony_ci    for (i = 0; i < rects; i++)
312cabdff1aSopenharmony_ci        count_colors(avctx, global_palette_hits, h->rects[i]);
313cabdff1aSopenharmony_ci    select_palette(avctx, out_palette, out_alpha, global_palette_hits);
314cabdff1aSopenharmony_ci
315cabdff1aSopenharmony_ci    if (rects > 1) {
316cabdff1aSopenharmony_ci        if (!(vrect_data = av_calloc(vrect.w, vrect.h)))
317cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
318cabdff1aSopenharmony_ci        vrect.data    [0] = vrect_data;
319cabdff1aSopenharmony_ci        vrect.linesize[0] = vrect.w;
320cabdff1aSopenharmony_ci        for (i = 0; i < rects; i++) {
321cabdff1aSopenharmony_ci            build_color_map(avctx, cmap, (uint32_t *)h->rects[i]->data[1],
322cabdff1aSopenharmony_ci                            out_palette, out_alpha);
323cabdff1aSopenharmony_ci            copy_rectangle(&vrect, h->rects[i], cmap);
324cabdff1aSopenharmony_ci        }
325cabdff1aSopenharmony_ci        for (i = 0; i < 4; i++)
326cabdff1aSopenharmony_ci            cmap[i] = i;
327cabdff1aSopenharmony_ci    } else {
328cabdff1aSopenharmony_ci        build_color_map(avctx, cmap, (uint32_t *)h->rects[0]->data[1],
329cabdff1aSopenharmony_ci                        out_palette, out_alpha);
330cabdff1aSopenharmony_ci    }
331cabdff1aSopenharmony_ci
332cabdff1aSopenharmony_ci    av_log(avctx, AV_LOG_DEBUG, "Selected palette:");
333cabdff1aSopenharmony_ci    for (i = 0; i < 4; i++)
334cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_DEBUG, " 0x%06"PRIx32"@@%02x (0x%x,0x%x)",
335cabdff1aSopenharmony_ci               dvdc->global_palette[out_palette[i]], out_alpha[i],
336cabdff1aSopenharmony_ci               out_palette[i], out_alpha[i] >> 4);
337cabdff1aSopenharmony_ci    av_log(avctx, AV_LOG_DEBUG, "\n");
338cabdff1aSopenharmony_ci
339cabdff1aSopenharmony_ci    // encode data block
340cabdff1aSopenharmony_ci    q = outbuf + 4;
341cabdff1aSopenharmony_ci    offset1 = q - outbuf;
342cabdff1aSopenharmony_ci    // worst case memory requirement: 1 nibble per pixel..
343cabdff1aSopenharmony_ci    if ((q - outbuf) + vrect.w * vrect.h / 2 + 17 + 21 > outbuf_size) {
344cabdff1aSopenharmony_ci        av_log(NULL, AV_LOG_ERROR, "dvd_subtitle too big\n");
345cabdff1aSopenharmony_ci        ret = AVERROR_BUFFER_TOO_SMALL;
346cabdff1aSopenharmony_ci        goto fail;
347cabdff1aSopenharmony_ci    }
348cabdff1aSopenharmony_ci    dvd_encode_rle(&q, vrect.data[0], vrect.w * 2,
349cabdff1aSopenharmony_ci                   vrect.w, (vrect.h + 1) >> 1, cmap);
350cabdff1aSopenharmony_ci    offset2 = q - outbuf;
351cabdff1aSopenharmony_ci    dvd_encode_rle(&q, vrect.data[0] + vrect.w, vrect.w * 2,
352cabdff1aSopenharmony_ci                   vrect.w, vrect.h >> 1, cmap);
353cabdff1aSopenharmony_ci
354cabdff1aSopenharmony_ci    if (dvdc->even_rows_fix && (vrect.h & 1)) {
355cabdff1aSopenharmony_ci        // Work-around for some players that want the height to be even.
356cabdff1aSopenharmony_ci        vrect.h++;
357cabdff1aSopenharmony_ci        *q++ = 0x00; // 0x00 0x00 == empty row, i.e. fully transparent
358cabdff1aSopenharmony_ci        *q++ = 0x00;
359cabdff1aSopenharmony_ci    }
360cabdff1aSopenharmony_ci
361cabdff1aSopenharmony_ci    // set data packet size
362cabdff1aSopenharmony_ci    qq = outbuf + 2;
363cabdff1aSopenharmony_ci    bytestream_put_be16(&qq, q - outbuf);
364cabdff1aSopenharmony_ci
365cabdff1aSopenharmony_ci    // send start display command
366cabdff1aSopenharmony_ci    bytestream_put_be16(&q, (h->start_display_time*90) >> 10);
367cabdff1aSopenharmony_ci    bytestream_put_be16(&q, (q - outbuf) /*- 2 */ + 8 + 12 + 2);
368cabdff1aSopenharmony_ci    *q++ = 0x03; // palette - 4 nibbles
369cabdff1aSopenharmony_ci    *q++ = (out_palette[3] << 4) | out_palette[2];
370cabdff1aSopenharmony_ci    *q++ = (out_palette[1] << 4) | out_palette[0];
371cabdff1aSopenharmony_ci    *q++ = 0x04; // alpha - 4 nibbles
372cabdff1aSopenharmony_ci    *q++ = (out_alpha[3] & 0xF0) | (out_alpha[2] >> 4);
373cabdff1aSopenharmony_ci    *q++ = (out_alpha[1] & 0xF0) | (out_alpha[0] >> 4);
374cabdff1aSopenharmony_ci
375cabdff1aSopenharmony_ci    // 12 bytes per rect
376cabdff1aSopenharmony_ci    x2 = vrect.x + vrect.w - 1;
377cabdff1aSopenharmony_ci    y2 = vrect.y + vrect.h - 1;
378cabdff1aSopenharmony_ci
379cabdff1aSopenharmony_ci    if (x2 > avctx->width || y2 > avctx->height) {
380cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR, "canvas_size(%d:%d) is too small(%d:%d) for render\n",
381cabdff1aSopenharmony_ci               avctx->width, avctx->height, x2, y2);
382cabdff1aSopenharmony_ci        ret = AVERROR(EINVAL);;
383cabdff1aSopenharmony_ci        goto fail;
384cabdff1aSopenharmony_ci    }
385cabdff1aSopenharmony_ci    *q++ = 0x05;
386cabdff1aSopenharmony_ci    // x1 x2 -> 6 nibbles
387cabdff1aSopenharmony_ci    *q++ = vrect.x >> 4;
388cabdff1aSopenharmony_ci    *q++ = (vrect.x << 4) | ((x2 >> 8) & 0xf);
389cabdff1aSopenharmony_ci    *q++ = x2;
390cabdff1aSopenharmony_ci    // y1 y2 -> 6 nibbles
391cabdff1aSopenharmony_ci    *q++ = vrect.y >> 4;
392cabdff1aSopenharmony_ci    *q++ = (vrect.y << 4) | ((y2 >> 8) & 0xf);
393cabdff1aSopenharmony_ci    *q++ = y2;
394cabdff1aSopenharmony_ci
395cabdff1aSopenharmony_ci    *q++ = 0x06;
396cabdff1aSopenharmony_ci    // offset1, offset2
397cabdff1aSopenharmony_ci    bytestream_put_be16(&q, offset1);
398cabdff1aSopenharmony_ci    bytestream_put_be16(&q, offset2);
399cabdff1aSopenharmony_ci
400cabdff1aSopenharmony_ci    *q++ = forced ? 0x00 : 0x01; // start command
401cabdff1aSopenharmony_ci    *q++ = 0xff; // terminating command
402cabdff1aSopenharmony_ci
403cabdff1aSopenharmony_ci    // send stop display command last
404cabdff1aSopenharmony_ci    bytestream_put_be16(&q, (h->end_display_time*90) >> 10);
405cabdff1aSopenharmony_ci    bytestream_put_be16(&q, (q - outbuf) - 2 /*+ 4*/);
406cabdff1aSopenharmony_ci    *q++ = 0x02; // set end
407cabdff1aSopenharmony_ci    *q++ = 0xff; // terminating command
408cabdff1aSopenharmony_ci
409cabdff1aSopenharmony_ci    qq = outbuf;
410cabdff1aSopenharmony_ci    bytestream_put_be16(&qq, q - outbuf);
411cabdff1aSopenharmony_ci
412cabdff1aSopenharmony_ci    av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%"PTRDIFF_SPECIFIER"\n", q - outbuf);
413cabdff1aSopenharmony_ci    ret = q - outbuf;
414cabdff1aSopenharmony_ci
415cabdff1aSopenharmony_cifail:
416cabdff1aSopenharmony_ci    av_free(vrect_data);
417cabdff1aSopenharmony_ci    return ret;
418cabdff1aSopenharmony_ci}
419cabdff1aSopenharmony_ci
420cabdff1aSopenharmony_cistatic int bprint_to_extradata(AVCodecContext *avctx, struct AVBPrint *buf)
421cabdff1aSopenharmony_ci{
422cabdff1aSopenharmony_ci    int ret;
423cabdff1aSopenharmony_ci    char *str;
424cabdff1aSopenharmony_ci
425cabdff1aSopenharmony_ci    ret = av_bprint_finalize(buf, &str);
426cabdff1aSopenharmony_ci    if (ret < 0)
427cabdff1aSopenharmony_ci        return ret;
428cabdff1aSopenharmony_ci    if (!av_bprint_is_complete(buf)) {
429cabdff1aSopenharmony_ci        av_free(str);
430cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
431cabdff1aSopenharmony_ci    }
432cabdff1aSopenharmony_ci
433cabdff1aSopenharmony_ci    avctx->extradata = str;
434cabdff1aSopenharmony_ci    /* Note: the string is NUL terminated (so extradata can be read as a
435cabdff1aSopenharmony_ci     * string), but the ending character is not accounted in the size (in
436cabdff1aSopenharmony_ci     * binary formats you are likely not supposed to mux that character). When
437cabdff1aSopenharmony_ci     * extradata is copied, it is also padded with AV_INPUT_BUFFER_PADDING_SIZE
438cabdff1aSopenharmony_ci     * zeros. */
439cabdff1aSopenharmony_ci    avctx->extradata_size = buf->len;
440cabdff1aSopenharmony_ci    return 0;
441cabdff1aSopenharmony_ci}
442cabdff1aSopenharmony_ci
443cabdff1aSopenharmony_cistatic int dvdsub_init(AVCodecContext *avctx)
444cabdff1aSopenharmony_ci{
445cabdff1aSopenharmony_ci    DVDSubtitleContext *dvdc = avctx->priv_data;
446cabdff1aSopenharmony_ci    static const uint32_t default_palette[16] = {
447cabdff1aSopenharmony_ci        0x000000, 0x0000FF, 0x00FF00, 0xFF0000,
448cabdff1aSopenharmony_ci        0xFFFF00, 0xFF00FF, 0x00FFFF, 0xFFFFFF,
449cabdff1aSopenharmony_ci        0x808000, 0x8080FF, 0x800080, 0x80FF80,
450cabdff1aSopenharmony_ci        0x008080, 0xFF8080, 0x555555, 0xAAAAAA,
451cabdff1aSopenharmony_ci    };
452cabdff1aSopenharmony_ci    AVBPrint extradata;
453cabdff1aSopenharmony_ci    int i, ret;
454cabdff1aSopenharmony_ci
455cabdff1aSopenharmony_ci    av_assert0(sizeof(dvdc->global_palette) == sizeof(default_palette));
456cabdff1aSopenharmony_ci    if (dvdc->palette_str) {
457cabdff1aSopenharmony_ci        ff_dvdsub_parse_palette(dvdc->global_palette, dvdc->palette_str);
458cabdff1aSopenharmony_ci    } else {
459cabdff1aSopenharmony_ci        memcpy(dvdc->global_palette, default_palette, sizeof(dvdc->global_palette));
460cabdff1aSopenharmony_ci    }
461cabdff1aSopenharmony_ci
462cabdff1aSopenharmony_ci    av_bprint_init(&extradata, 0, AV_BPRINT_SIZE_AUTOMATIC);
463cabdff1aSopenharmony_ci    if (avctx->width && avctx->height)
464cabdff1aSopenharmony_ci        av_bprintf(&extradata, "size: %dx%d\n", avctx->width, avctx->height);
465cabdff1aSopenharmony_ci    av_bprintf(&extradata, "palette:");
466cabdff1aSopenharmony_ci    for (i = 0; i < 16; i++)
467cabdff1aSopenharmony_ci        av_bprintf(&extradata, " %06"PRIx32"%c",
468cabdff1aSopenharmony_ci                   dvdc->global_palette[i] & 0xFFFFFF, i < 15 ? ',' : '\n');
469cabdff1aSopenharmony_ci
470cabdff1aSopenharmony_ci    ret = bprint_to_extradata(avctx, &extradata);
471cabdff1aSopenharmony_ci    if (ret < 0)
472cabdff1aSopenharmony_ci        return ret;
473cabdff1aSopenharmony_ci
474cabdff1aSopenharmony_ci    return 0;
475cabdff1aSopenharmony_ci}
476cabdff1aSopenharmony_ci
477cabdff1aSopenharmony_cistatic int dvdsub_encode(AVCodecContext *avctx,
478cabdff1aSopenharmony_ci                         unsigned char *buf, int buf_size,
479cabdff1aSopenharmony_ci                         const AVSubtitle *sub)
480cabdff1aSopenharmony_ci{
481cabdff1aSopenharmony_ci    //DVDSubtitleContext *s = avctx->priv_data;
482cabdff1aSopenharmony_ci    int ret;
483cabdff1aSopenharmony_ci
484cabdff1aSopenharmony_ci    ret = encode_dvd_subtitles(avctx, buf, buf_size, sub);
485cabdff1aSopenharmony_ci    return ret;
486cabdff1aSopenharmony_ci}
487cabdff1aSopenharmony_ci
488cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(DVDSubtitleContext, x)
489cabdff1aSopenharmony_ci#define SE AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_ENCODING_PARAM
490cabdff1aSopenharmony_cistatic const AVOption options[] = {
491cabdff1aSopenharmony_ci    {"palette", "set the global palette", OFFSET(palette_str), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, SE },
492cabdff1aSopenharmony_ci    {"even_rows_fix", "Make number of rows even (workaround for some players)", OFFSET(even_rows_fix), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, SE},
493cabdff1aSopenharmony_ci    { NULL },
494cabdff1aSopenharmony_ci};
495cabdff1aSopenharmony_ci
496cabdff1aSopenharmony_cistatic const AVClass dvdsubenc_class = {
497cabdff1aSopenharmony_ci    .class_name = "VOBSUB subtitle encoder",
498cabdff1aSopenharmony_ci    .item_name  = av_default_item_name,
499cabdff1aSopenharmony_ci    .option     = options,
500cabdff1aSopenharmony_ci    .version    = LIBAVUTIL_VERSION_INT,
501cabdff1aSopenharmony_ci};
502cabdff1aSopenharmony_ci
503cabdff1aSopenharmony_ciconst FFCodec ff_dvdsub_encoder = {
504cabdff1aSopenharmony_ci    .p.name         = "dvdsub",
505cabdff1aSopenharmony_ci    .p.long_name    = NULL_IF_CONFIG_SMALL("DVD subtitles"),
506cabdff1aSopenharmony_ci    .p.type         = AVMEDIA_TYPE_SUBTITLE,
507cabdff1aSopenharmony_ci    .p.id           = AV_CODEC_ID_DVD_SUBTITLE,
508cabdff1aSopenharmony_ci    .init           = dvdsub_init,
509cabdff1aSopenharmony_ci    FF_CODEC_ENCODE_SUB_CB(dvdsub_encode),
510cabdff1aSopenharmony_ci    .p.priv_class   = &dvdsubenc_class,
511cabdff1aSopenharmony_ci    .priv_data_size = sizeof(DVDSubtitleContext),
512cabdff1aSopenharmony_ci    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
513cabdff1aSopenharmony_ci};
514