~ubuntu-branches/ubuntu/vivid/bino/vivid-proposed

« back to all changes in this revision

Viewing changes to src/subtitle_renderer.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Schaal
  • Date: 2011-09-09 14:23:54 UTC
  • Revision ID: james.westby@ubuntu.com-20110909142354-m1a9a4523i7ddb02
Tags: upstream-1.2.0
ImportĀ upstreamĀ versionĀ 1.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * This file is part of bino, a 3D video player.
 
3
 *
 
4
 * Copyright (C) 2011
 
5
 * Martin Lambers <marlam@marlam.de>
 
6
 * Joe <cuchac@email.cz>
 
7
 * FrĆ©dĆ©ric Devernay <Frederic.Devernay@inrialpes.fr>
 
8
 *
 
9
 * This program is free software; you can redistribute it and/or modify
 
10
 * it under the terms of the GNU General Public License as published by
 
11
 * the Free Software Foundation; either version 3 of the License, or
 
12
 * (at your option) any later version.
 
13
 *
 
14
 * This program is distributed in the hope that it will be useful,
 
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
17
 * GNU General Public License for more details.
 
18
 *
 
19
 * You should have received a copy of the GNU General Public License
 
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
21
 */
 
22
 
 
23
#include "config.h"
 
24
 
 
25
#include <vector>
 
26
#include <limits>
 
27
#include <cstring>
 
28
#include <cstdio>
 
29
#include <stdint.h>
 
30
 
 
31
#include <GL/glew.h>
 
32
 
 
33
#if (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__
 
34
# include <windows.h>
 
35
#endif
 
36
#if defined __APPLE__
 
37
# include <unistd.h>
 
38
#endif
 
39
 
 
40
extern "C"
 
41
{
 
42
#include <ass/ass.h>
 
43
}
 
44
 
 
45
#include "gettext.h"
 
46
#define _(string) gettext(string)
 
47
 
 
48
#include "dbg.h"
 
49
#include "exc.h"
 
50
#include "str.h"
 
51
#include "blob.h"
 
52
#include "msg.h"
 
53
#include "thread.h"
 
54
 
 
55
#include "subtitle_renderer.h"
 
56
 
 
57
 
 
58
subtitle_renderer_initializer::subtitle_renderer_initializer(subtitle_renderer &renderer) :
 
59
    _subtitle_renderer(renderer)
 
60
{
 
61
}
 
62
 
 
63
void subtitle_renderer_initializer::run()
 
64
{
 
65
    _subtitle_renderer.init();
 
66
}
 
67
 
 
68
 
 
69
/* Rendering subtitles with LibASS is not thread-safe.
 
70
 *
 
71
 * We have multiple concurrent subtitle rendering threads if we are running from Equalizer
 
72
 * and the Equalizer configuration contains multiple channels per node (since each channel
 
73
 * has a video output, which in turn has a subtitle renderer).
 
74
 *
 
75
 * Everything works fine as long as every channel is connected to the same X11 display.
 
76
 * But if channels are connected to different X11 displays, the application crashes.
 
77
 * The culprit here seems to be the freetype library, as this library causes the same
 
78
 * trouble in other applications that are not related to LibASS.
 
79
 *
 
80
 * Anyway, to fix this we use one big global lock around LibASS calls.
 
81
 */
 
82
static mutex global_libass_mutex;
 
83
 
 
84
subtitle_renderer::subtitle_renderer() :
 
85
    _initializer(*this),
 
86
    _initialized(false),
 
87
    _fontconfig_conffile(NULL),
 
88
    _ass_library(NULL),
 
89
    _ass_renderer(NULL),
 
90
    _ass_track(NULL)
 
91
{
 
92
    _initializer.start();
 
93
}
 
94
 
 
95
subtitle_renderer::~subtitle_renderer()
 
96
{
 
97
    if (_initializer.is_running())
 
98
    {
 
99
        try
 
100
        {
 
101
            _initializer.cancel();
 
102
        }
 
103
        catch (...)
 
104
        {
 
105
        }
 
106
    }
 
107
    if (_ass_track)
 
108
    {
 
109
        ass_free_track(_ass_track);
 
110
    }
 
111
    if (_ass_renderer)
 
112
    {
 
113
        ass_renderer_done(_ass_renderer);
 
114
    }
 
115
    if (_ass_library)
 
116
    {
 
117
        ass_library_done(_ass_library);
 
118
    }
 
119
    if (_fontconfig_conffile)
 
120
    {
 
121
        (void)std::remove(_fontconfig_conffile);
 
122
    }
 
123
}
 
124
 
 
125
static void libass_msg_callback(int level, const char *fmt, va_list args, void *)
 
126
{
 
127
    msg::level_t msg_levels[8] = { msg::ERR, msg::ERR, msg::WRN, msg::WRN, msg::WRN, msg::INF, msg::DBG, msg::DBG };
 
128
    msg::level_t l = (level < 0 || level > 7 ? msg::ERR : msg_levels[level]);
 
129
    std::string s = str::vasprintf(fmt, args);
 
130
    msg::msg(l, std::string("LibASS: ") + s);
 
131
}
 
132
 
 
133
const char *subtitle_renderer::get_fontconfig_conffile()
 
134
{
 
135
#if ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __APPLE__
 
136
    /* Fontconfig annoyingly requires a configuration file, but there is no
 
137
     * default location for it on Windows or Mac OS X. So we just create a temporary one.
 
138
     * Note that if something goes wrong and we return NULL here, the application will
 
139
     * likely crash when trying to render a subtitle. */
 
140
    FILE *f;
 
141
# if ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__)
 
142
    /* Note that due to lack of mkstemp() on Windows, this code has race conditions. */
 
143
    char tmppathname[MAX_PATH];
 
144
    static char tmpfilename[MAX_PATH];
 
145
    DWORD ret;
 
146
    ret = GetTempPath(MAX_PATH, tmppathname);
 
147
    if (ret > MAX_PATH || ret == 0)
 
148
        return NULL;
 
149
    if (GetTempFileName(tmppathname, "fonts_conf", 0, tmpfilename) == 0)
 
150
        return NULL;
 
151
    if (!(f = fopen(tmpfilename, "w")))
 
152
        return NULL;
 
153
# else /* __APPLE__ */
 
154
    static char tmpfilename[] = "/tmp/fontsXXXXXX.conf";
 
155
    int d = mkstemps(tmpfilename, 5);
 
156
    if (d == -1)
 
157
        return NULL;
 
158
    if (!(f = fdopen(d, "w")))
 
159
        return NULL;
 
160
# endif
 
161
    fprintf(f,
 
162
            "<?xml version=\"1.0\"?>\n"
 
163
            "<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">\n"
 
164
            "<fontconfig>\n"
 
165
# if ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__)
 
166
            "<dir>WINDOWSFONTDIR</dir>\n"
 
167
            "<dir>~/.fonts</dir>\n"
 
168
            "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n"
 
169
            "<cachedir>~/.fontconfig</cachedir>\n"
 
170
# else /* __APPLE__ */
 
171
            "<dir>/usr/share/fonts</dir>\n"
 
172
            "<dir>/usr/X11/lib/X11/fonts</dir>\n"
 
173
            "<dir>/usr/X11/share/fonts</dir>\n"
 
174
            "<dir>/opt/X11/share/fonts</dir>\n"
 
175
            "<dir>/Library/Fonts</dir>\n"
 
176
            "<dir>/Network/Library/Fonts</dir>\n"
 
177
            "<dir>/System/Library/Fonts</dir>\n"
 
178
            "<dir>~/Library/Application Support/Bino/fonts</dir>\n"
 
179
            "<cachedir>/var/cache/fontconfig</cachedir>\n"
 
180
            "<cachedir>/usr/X11/var/cache/fontconfig</cachedir>\n"
 
181
            "<cachedir>/opt/X11/var/cache/fontconfig</cachedir>\n"
 
182
            "<cachedir>~/Library/Application Support/Bino/cache/fonts</cachedir>\n"
 
183
            "<cachedir>~/.fontconfig</cachedir>\n"
 
184
# endif
 
185
            "<config>\n"
 
186
            "<blank>\n"
 
187
            "<int>0x0020</int> <int>0x00A0</int> <int>0x00AD</int> <int>0x034F</int> <int>0x0600</int>\n"
 
188
            "<int>0x0601</int> <int>0x0602</int> <int>0x0603</int> <int>0x06DD</int> <int>0x070F</int>\n"
 
189
            "<int>0x115F</int> <int>0x1160</int> <int>0x1680</int> <int>0x17B4</int> <int>0x17B5</int>\n"
 
190
            "<int>0x180E</int> <int>0x2000</int> <int>0x2001</int> <int>0x2002</int> <int>0x2003</int>\n"
 
191
            "<int>0x2004</int> <int>0x2005</int> <int>0x2006</int> <int>0x2007</int> <int>0x2008</int>\n"
 
192
            "<int>0x2009</int> <int>0x200A</int> <int>0x200B</int> <int>0x200C</int> <int>0x200D</int>\n"
 
193
            "<int>0x200E</int> <int>0x200F</int> <int>0x2028</int> <int>0x2029</int> <int>0x202A</int>\n"
 
194
            "<int>0x202B</int> <int>0x202C</int> <int>0x202D</int> <int>0x202E</int> <int>0x202F</int>\n"
 
195
            "<int>0x205F</int> <int>0x2060</int> <int>0x2061</int> <int>0x2062</int> <int>0x2063</int>\n"
 
196
            "<int>0x206A</int> <int>0x206B</int> <int>0x206C</int> <int>0x206D</int> <int>0x206E</int>\n"
 
197
            "<int>0x206F</int> <int>0x2800</int> <int>0x3000</int> <int>0x3164</int> <int>0xFEFF</int>\n"
 
198
            "<int>0xFFA0</int> <int>0xFFF9</int> <int>0xFFFA</int> <int>0xFFFB</int>\n"
 
199
            "</blank>\n"
 
200
            "<rescan><int>30</int></rescan>\n"
 
201
            "</config>\n"
 
202
            "</fontconfig>\n");
 
203
    fclose(f);
 
204
    return tmpfilename;
 
205
#else
 
206
    /* Systems other than Windows and Mac OS are expected to have a default
 
207
     * fontconfig configuration file so that we can simply return NULL. */
 
208
    return NULL;
 
209
#endif
 
210
}
 
211
 
 
212
void subtitle_renderer::init()
 
213
{
 
214
    if (!_initialized)
 
215
    {
 
216
        try
 
217
        {
 
218
            global_libass_mutex.lock();
 
219
 
 
220
            ASS_Library *ass_library = ass_library_init();
 
221
            if (!ass_library)
 
222
            {
 
223
                throw exc(_("Cannot initialize LibASS."));
 
224
            }
 
225
            ass_set_message_cb(ass_library, libass_msg_callback, NULL);
 
226
            ass_set_extract_fonts(ass_library, 1);
 
227
            _ass_library = ass_library;
 
228
 
 
229
            ASS_Renderer *ass_renderer = ass_renderer_init(_ass_library);
 
230
            if (!ass_renderer)
 
231
            {
 
232
                throw exc(_("Cannot initialize LibASS renderer."));
 
233
            }
 
234
            ass_set_hinting(ass_renderer, ASS_HINTING_NATIVE);
 
235
            _fontconfig_conffile = get_fontconfig_conffile();
 
236
            ass_set_fonts(ass_renderer, NULL, "sans-serif", 1, _fontconfig_conffile, 1);
 
237
            _ass_renderer = ass_renderer;
 
238
 
 
239
            _initialized = true;
 
240
            global_libass_mutex.unlock();
 
241
        }
 
242
        catch (...)
 
243
        {
 
244
            // Either we threw this exception ourselves, or the thread was cancelled.
 
245
            // Note that if the thread was cancelled, we likely leak ressources.
 
246
            // But at least we avoid to deconstruct potentially inconsistent ASS_Library
 
247
            // and ASS_Renderer objects.
 
248
            global_libass_mutex.unlock();
 
249
            throw;
 
250
        }
 
251
    }
 
252
}
 
253
 
 
254
bool subtitle_renderer::is_initialized()
 
255
{
 
256
    if (_initializer.is_running())
 
257
    {
 
258
        return false;
 
259
    }
 
260
    else
 
261
    {
 
262
        // rethrow a possible exception if something went wrong
 
263
        _initializer.finish();
 
264
        return _initialized;
 
265
    }
 
266
}
 
267
 
 
268
bool subtitle_renderer::render_to_display_size(const subtitle_box &box) const
 
269
{
 
270
    return (box.format != subtitle_box::image);
 
271
}
 
272
 
 
273
void subtitle_renderer::prerender(const subtitle_box &box, int64_t timestamp,
 
274
        const parameters &params,
 
275
        int width, int height, float pixel_aspect_ratio,
 
276
        int &bb_x, int &bb_y, int &bb_w, int &bb_h)
 
277
{
 
278
    assert(_initialized);
 
279
    _fmt = box.format;
 
280
    switch (_fmt)
 
281
    {
 
282
    case subtitle_box::text:
 
283
    case subtitle_box::ass:
 
284
        prerender_ass(box, timestamp, params, width, height, pixel_aspect_ratio);
 
285
        break;
 
286
    case subtitle_box::image:
 
287
        prerender_img(box);
 
288
        break;
 
289
    }
 
290
    bb_x = _bb_x;
 
291
    bb_y = _bb_y;
 
292
    bb_w = _bb_w;
 
293
    bb_h = _bb_h;
 
294
}
 
295
 
 
296
void subtitle_renderer::render(uint32_t *bgra32_buffer)
 
297
{
 
298
    switch (_fmt)
 
299
    {
 
300
    case subtitle_box::text:
 
301
    case subtitle_box::ass:
 
302
        render_ass(bgra32_buffer);
 
303
        break;
 
304
    case subtitle_box::image:
 
305
        render_img(bgra32_buffer);
 
306
        break;
 
307
    }
 
308
}
 
309
 
 
310
void subtitle_renderer::blend_ass_image(const ASS_Image *img, uint32_t *buf)
 
311
{
 
312
    const unsigned int R = (img->color >> 24u) & 0xffu;
 
313
    const unsigned int G = (img->color >> 16u) & 0xffu;
 
314
    const unsigned int B = (img->color >>  8u) & 0xffu;
 
315
    const unsigned int A = 255u - (img->color & 0xffu);
 
316
 
 
317
    unsigned char *src = img->bitmap;
 
318
    for (int src_y = 0; src_y < img->h; src_y++)
 
319
    {
 
320
        int dst_y = src_y + img->dst_y - _bb_y;
 
321
        if (dst_y >= _bb_h)
 
322
        {
 
323
            break;
 
324
        }
 
325
        for (int src_x = 0; src_x < img->w; src_x++)
 
326
        {
 
327
            unsigned int a = src[src_x] * A / 255u;
 
328
            int dst_x = src_x + img->dst_x - _bb_x;
 
329
            if (dst_x >= _bb_w)
 
330
            {
 
331
                break;
 
332
            }
 
333
            uint32_t oldval = buf[dst_y * _bb_w + dst_x];
 
334
            // XXX: The BGRA layout used here may be wrong on big endian system
 
335
            uint32_t newval = std::min(a + (oldval >> 24u), 255u) << 24u
 
336
                | ((a * R + (255u - a) * ((oldval >> 16u) & 0xffu)) / 255u) << 16u
 
337
                | ((a * G + (255u - a) * ((oldval >>  8u) & 0xffu)) / 255u) << 8u
 
338
                | ((a * B + (255u - a) * ((oldval       ) & 0xffu)) / 255u);
 
339
            buf[dst_y * _bb_w + dst_x] = newval;
 
340
        }
 
341
        src += img->stride;
 
342
    }
 
343
}
 
344
 
 
345
void subtitle_renderer::set_ass_parameters(const parameters &params)
 
346
{
 
347
    std::vector<std::string> overrides;
 
348
    overrides.clear();
 
349
    if (params.subtitle_font != "")
 
350
    {
 
351
        overrides.push_back(std::string("Default.Fontname=") + params.subtitle_font);
 
352
    }
 
353
    if (params.subtitle_size > 0)
 
354
    {
 
355
        overrides.push_back(std::string("Default.Fontsize=") + str::from(params.subtitle_size));
 
356
    }
 
357
    if (params.subtitle_color <= std::numeric_limits<uint32_t>::max())
 
358
    {
 
359
        unsigned int color = params.subtitle_color;
 
360
        unsigned int a = 255u - ((color >> 24u) & 0xffu);
 
361
        unsigned int r = (color >> 16u) & 0xffu;
 
362
        unsigned int g = (color >> 8u) & 0xffu;
 
363
        unsigned int b = color & 0xffu;
 
364
        std::string color_str = str::asprintf("&H%02x%02x%02x%02x", a, b, g, r);
 
365
        overrides.push_back(std::string("Default.PrimaryColour=") + color_str);
 
366
        overrides.push_back(std::string("Default.SecondaryColour=") + color_str);
 
367
    }
 
368
    const char *ass_overrides[overrides.size() + 1];
 
369
    for (size_t i = 0; i < overrides.size(); i++)
 
370
    {
 
371
        ass_overrides[i] = ::strdup(overrides[i].c_str());
 
372
    }
 
373
    ass_overrides[overrides.size()] = NULL;
 
374
    ass_set_style_overrides(_ass_library, const_cast<char **>(ass_overrides));
 
375
    ass_set_font_scale(_ass_renderer, (params.subtitle_scale >= 0.0f ? params.subtitle_scale : 1.0));
 
376
    ass_process_force_style(_ass_track);
 
377
}
 
378
 
 
379
void subtitle_renderer::prerender_ass(const subtitle_box &box, int64_t timestamp,
 
380
        const parameters &params, int width, int height, float pixel_aspect_ratio)
 
381
{
 
382
    // Lock
 
383
    global_libass_mutex.lock();
 
384
 
 
385
    // Set basic parameters
 
386
    ass_set_frame_size(_ass_renderer, width, height);
 
387
    ass_set_aspect_ratio(_ass_renderer, 1.0, pixel_aspect_ratio);
 
388
 
 
389
    // Put subtitle data into ASS track
 
390
    if (_ass_track)
 
391
    {
 
392
        ass_free_track(_ass_track);
 
393
    }
 
394
    _ass_track = ass_new_track(_ass_library);
 
395
    if (!_ass_track)
 
396
    {
 
397
        global_libass_mutex.unlock();
 
398
        throw exc(_("Cannot initialize LibASS track."));
 
399
    }
 
400
    std::string conv_str = box.str;
 
401
    if (params.subtitle_encoding != "")
 
402
    {
 
403
        try
 
404
        {
 
405
            conv_str = str::convert(box.str, params.subtitle_encoding, "UTF-8");
 
406
        }
 
407
        catch (std::exception &e)
 
408
        {
 
409
            msg::err(_("Subtitle character set conversion failed: %s"), e.what());
 
410
            conv_str = std::string("Dialogue: 0,0:00:00.00,9:59:59.99,") + e.what();
 
411
        }
 
412
    }
 
413
    if (box.format == subtitle_box::ass)
 
414
    {
 
415
        ass_process_codec_private(_ass_track, const_cast<char *>(box.style.c_str()), box.style.length());
 
416
        ass_process_data(_ass_track, const_cast<char *>(conv_str.c_str()), conv_str.length());
 
417
    }
 
418
    else
 
419
    {
 
420
        // Set a default ASS style for text subtitles
 
421
        std::string style =
 
422
            "[Script Info]\n"
 
423
            "ScriptType: v4.00+\n"
 
424
            "\n"
 
425
            "[V4+ Styles]\n"
 
426
            "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, "
 
427
            "OutlineColour, BackColour, Bold, Italic, Underline, BorderStyle, "
 
428
            "Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding\n"
 
429
            "Style: Default,Arial,16,&Hffffff,&Hffffff,&H0,&H0,0,0,0,1,1,0,2,10,10,10,0,0\n"
 
430
            "\n"
 
431
            "[Events]\n"
 
432
            "Format: Layer, Start, End, Text\n"
 
433
            "\n";
 
434
        ass_process_codec_private(_ass_track, const_cast<char *>(style.c_str()), style.length());
 
435
        // Convert text to ASS
 
436
        str::replace(conv_str, "\r\n", "\\N");
 
437
        str::replace(conv_str, "\n", "\\N");
 
438
        std::string str = "Dialogue: 0,0:00:00.00,9:59:59.99," + conv_str;
 
439
        ass_process_data(_ass_track, const_cast<char *>(str.c_str()), str.length());
 
440
    }
 
441
    set_ass_parameters(params);
 
442
 
 
443
    // Render subtitle
 
444
    _ass_img = ass_render_frame(_ass_renderer, _ass_track, timestamp / 1000, NULL);
 
445
 
 
446
    // Unlock
 
447
    global_libass_mutex.unlock();
 
448
 
 
449
    // Determine bounding box
 
450
    int min_x = width;
 
451
    int max_x = -1;
 
452
    int min_y = height;
 
453
    int max_y = -1;
 
454
    ASS_Image *img = _ass_img;
 
455
    while (img && img->w > 0 && img->h > 0)
 
456
    {
 
457
        if (img->dst_x < min_x)
 
458
            min_x = img->dst_x;
 
459
        if (img->dst_x + img->w - 1 > max_x)
 
460
            max_x = img->dst_x + img->w - 1;
 
461
        if (img->dst_y < min_y)
 
462
            min_y = img->dst_y;
 
463
        if (img->dst_y + img->h - 1 > max_y)
 
464
            max_y = img->dst_y + img->h - 1;
 
465
        img = img->next;
 
466
    }
 
467
    if (max_x < 0)
 
468
    {
 
469
        _bb_x = 0;
 
470
        _bb_y = 0;
 
471
        _bb_w = 0;
 
472
        _bb_h = 0;
 
473
    }
 
474
    else
 
475
    {
 
476
        _bb_x = min_x;
 
477
        _bb_w = max_x - min_x + 1;
 
478
        _bb_y = min_y;
 
479
        _bb_h = max_y - min_y + 1;
 
480
    }
 
481
}
 
482
 
 
483
void subtitle_renderer::render_ass(uint32_t *bgra32_buffer)
 
484
{
 
485
    if (_bb_w > 0 && _bb_h > 0)
 
486
    {
 
487
        std::memset(bgra32_buffer, 0, _bb_w * _bb_h * sizeof(uint32_t));
 
488
        ASS_Image *img = _ass_img;
 
489
        while (img && img->w > 0 && img->h > 0)
 
490
        {
 
491
            blend_ass_image(img, bgra32_buffer);
 
492
            img = img->next;
 
493
        }
 
494
    }
 
495
}
 
496
 
 
497
void subtitle_renderer::prerender_img(const subtitle_box &box)
 
498
{
 
499
    _img_box = &box;
 
500
    // Determine bounding box
 
501
    int min_x = std::numeric_limits<int>::max();
 
502
    int max_x = -1;
 
503
    int min_y = std::numeric_limits<int>::max();
 
504
    int max_y = -1;
 
505
    for (size_t i = 0; i < box.images.size(); i++)
 
506
    {
 
507
        const subtitle_box::image_t &img = box.images[i];
 
508
        if (img.x < min_x)
 
509
            min_x = img.x;
 
510
        if (img.x + img.w - 1 > max_x)
 
511
            max_x = img.x + img.w - 1;
 
512
        if (img.y < min_y)
 
513
            min_y = img.y;
 
514
        if (img.y + img.h - 1 > max_y)
 
515
            max_y = img.y + img.h - 1;
 
516
    }
 
517
    if (max_x < 0)
 
518
    {
 
519
        _bb_x = 0;
 
520
        _bb_y = 0;
 
521
        _bb_w = 0;
 
522
        _bb_h = 0;
 
523
    }
 
524
    else
 
525
    {
 
526
        _bb_x = min_x;
 
527
        _bb_w = max_x - min_x + 1;
 
528
        _bb_y = min_y;
 
529
        _bb_h = max_y - min_y + 1;
 
530
    }
 
531
}
 
532
 
 
533
void subtitle_renderer::render_img(uint32_t *bgra32_buffer)
 
534
{
 
535
    if (_bb_w <= 0 || _bb_h <= 0)
 
536
    {
 
537
        return;
 
538
    }
 
539
 
 
540
    std::memset(bgra32_buffer, 0, _bb_w * _bb_h * sizeof(uint32_t));
 
541
    for (size_t i = 0; i < _img_box->images.size(); i++)
 
542
    {
 
543
        const subtitle_box::image_t &img = _img_box->images[i];
 
544
        const uint8_t *src = &(img.data[0]);
 
545
        for (int src_y = 0; src_y < img.h; src_y++)
 
546
        {
 
547
            int dst_y = src_y + img.y - _bb_y;
 
548
            if (dst_y >= _bb_h)
 
549
            {
 
550
                break;
 
551
            }
 
552
            for (int src_x = 0; src_x < img.w; src_x++)
 
553
            {
 
554
                int dst_x = src_x + img.x - _bb_x;
 
555
                if (dst_x >= _bb_w)
 
556
                {
 
557
                    break;
 
558
                }
 
559
                int palette_index = src[src_x];
 
560
                uint32_t palette_entry = reinterpret_cast<const uint32_t *>(&(img.palette[0]))[palette_index];
 
561
                unsigned int A = (palette_entry >> 24u);
 
562
                unsigned int R = (palette_entry >> 16u) & 0xffu;
 
563
                unsigned int G = (palette_entry >> 8u) & 0xffu;
 
564
                unsigned int B = palette_entry & 0xffu;
 
565
                uint32_t oldval = bgra32_buffer[dst_y * _bb_w + dst_x];
 
566
                // XXX: The BGRA layout used here may be wrong on big endian system
 
567
                uint32_t newval = std::min(A + (oldval >> 24u), 255u) << 24u
 
568
                    | ((A * R + (255u - A) * ((oldval >> 16u) & 0xffu)) / 255u) << 16u
 
569
                    | ((A * G + (255u - A) * ((oldval >>  8u) & 0xffu)) / 255u) << 8u
 
570
                    | ((A * B + (255u - A) * ((oldval       ) & 0xffu)) / 255u);
 
571
                bgra32_buffer[dst_y * _bb_w + dst_x] = newval;
 
572
            }
 
573
            src += img.linesize;
 
574
        }
 
575
    }
 
576
}