Add built-in VGA font
[jpcrr.git] / streamtools / hardsubs.cpp
blobd35add9e7b7af55094af35d8a4554fa34ab82779
1 #include "hardsubs.hpp"
2 #include "misc.hpp"
3 #include <stdexcept>
4 #include <iomanip>
5 #include <fstream>
6 #include <iostream>
7 #include <map>
8 #include <sstream>
9 #include "timeparse.hpp"
10 #include "SDL_ttf.h"
11 #include "SDL.h"
13 namespace
15 std::map<unsigned char, std::string> variables;
17 void init_variables()
19 if(!variables.count('\\'))
20 variables['\\'] = '\\';
21 if(!variables.count('A'))
22 variables['A'] = "(unknown)";
23 if(!variables.count('R'))
24 variables['R'] = "(unknown)";
25 if(!variables.count('L'))
26 variables['L'] = "(unknown)";
27 if(!variables.count('G'))
28 variables['G'] = "(unknown)";
31 void copy_surface(SDL_Surface* s, unsigned char* buffer, uint32_t total_width, uint32_t y,
32 uint32_t align_type, uint32_t extralines)
34 uint32_t xalign = 0;
35 switch(align_type) {
36 case ALIGN_LEFT:
37 xalign = 0;
38 break;
39 case ALIGN_CENTER:
40 xalign = (total_width - s->w) / 2;
41 break;
42 case ALIGN_RIGHT:
43 xalign = total_width - s->w;
44 break;
46 SDL_LockSurface(s);
47 for(uint32_t y2 = 0; y2 < s->h + extralines; y2++)
48 for(uint32_t x = 0; x < total_width; x++) {
49 if(x < xalign || x > xalign + (uint32_t)s->w || y2 >= (uint32_t)s->h)
50 buffer[(y + y2) * total_width + x] = 0;
51 else
52 buffer[(y + y2) * total_width + x] =
53 ((unsigned char*)s->pixels)[y2 * s->pitch + (x - xalign)];
55 SDL_UnlockSurface(s);
58 void render_halo(uint32_t w, uint32_t h, unsigned char* data, uint32_t _thickness)
60 int32_t thickness = _thickness;
61 for(uint32_t y = 0; y < h; y++)
62 for(uint32_t x = 0; x < w; x++) {
63 bool left = (x > 0) ? (data[y * w + x - 1] == 1) : false;
64 bool up = (y > 0) ? (data[y * w + x - w] == 1) : false;
65 if(data[y * w + x] != 1)
66 continue;
68 for(int32_t j = up ? thickness: -thickness; j <= thickness; j++) {
69 if(y + j < 0 || y + j >= h)
70 continue;
71 for(int32_t i = left ? thickness : -thickness; i <= thickness; i++) {
72 if(x + i < 0 || x + i >= w)
73 continue;
74 if(data[(y + j) * w + (x + i)] == 0)
75 data[(y + j) * w + (x + i)] = 2;
82 image_frame_rgbx* hardsub_render_settings::operator()()
84 std::list<SDL_Surface*> lines;
85 if(font_name == "")
86 throw std::runtime_error("No font set");
88 //Initialize SDL_ttf.
89 if(!TTF_WasInit() && TTF_Init() < 0) {
90 std::stringstream str;
91 str << "Can't initialize SDL_ttf: " << TTF_GetError();
92 throw std::runtime_error(str.str());
95 //Open the font and render the text.
96 TTF_Font* font = TTF_OpenFont(font_name.c_str(), font_size);
97 try {
98 SDL_Color clr;
99 clr.r = clr.g = clr.b = 255;
100 std::string tmp = "";
101 bool escape = false;
102 for(size_t i = 0; i < text.length(); i++) {
103 char tmp2[2] = {0, 0};
104 if(!escape)
105 if(text[i] == '\\')
106 escape = true;
107 else {
108 tmp2[0] = text[i];
109 tmp += tmp2;
111 else if(variables.count(text[i])) {
112 tmp += variables[text[i]];
113 escape = false;
114 } else if(text[i] == 'n') {
115 SDL_Surface* s = TTF_RenderUTF8_Solid(font, tmp.c_str(), clr);
116 if(!s)
117 throw std::runtime_error("Can't render text");
118 lines.push_back(s);
119 tmp = "";
120 escape = false;
121 } else {
122 std::stringstream str;
123 str << "Bad escape character '" << text[i] << "'";
124 throw std::runtime_error(str.str());
127 if(tmp != "") {
128 SDL_Surface* s = TTF_RenderUTF8_Solid(font, tmp.c_str(), clr);
129 if(!s)
130 throw std::runtime_error("Can't render text");
131 lines.push_back(s);
133 } catch(...) {
134 TTF_CloseFont(font);
135 throw;
137 TTF_CloseFont(font);
139 //Calculate image size and allocate buffers.
140 uint32_t total_width = 0;
141 uint32_t total_height = 0;
142 unsigned char* buffer1 = NULL;
143 image_frame_rgbx* img = NULL;
144 for(std::list<SDL_Surface*>::iterator i = lines.begin(); i != lines.end(); ++i) {
145 if((*i)->w + 2 * halo_thickness > total_width)
146 total_width = (*i)->w + 2 * halo_thickness;
147 total_height += ((*i)->h + 2 * halo_thickness + spacing);
149 try {
150 buffer1 = new unsigned char[total_width * total_height];
151 img = new image_frame_rgbx(total_width, total_height);
152 } catch(...) {
153 if(buffer1)
154 delete[] buffer1;
155 if(img)
156 delete img;
159 //Copy SDL surfaces to index buffer.
160 uint32_t line = 0;
161 for(std::list<SDL_Surface*>::iterator i = lines.begin(); i != lines.end(); ++i) {
162 copy_surface(*i, buffer1, total_width, line, align_type, spacing + 2 * halo_thickness);
163 line += ((*i)->h + spacing + 2 * halo_thickness);
164 SDL_FreeSurface(*i);
166 render_halo(total_width, total_height, buffer1, halo_thickness);
168 //Make full color buffer from indexed buffer.
169 uint v0 = 0, v1 = 0, v2 = 0;
170 unsigned char* data = img->get_pixels();
171 for(uint32_t y = 0; y < total_height; y++)
172 for(uint32_t x = 0; x < total_width; x++)
173 switch(buffer1[y * total_width + x]) {
174 case 0:
175 v0++;
176 data[y * 4 * total_width + 4 * x + 0] = background_r;
177 data[y * 4 * total_width + 4 * x + 1] = background_g;
178 data[y * 4 * total_width + 4 * x + 2] = background_b;
179 data[y * 4 * total_width + 4 * x + 3] = background_a;
180 break;
181 case 1:
182 v1++;
183 data[y * 4 * total_width + 4 * x + 0] = foreground_r;
184 data[y * 4 * total_width + 4 * x + 1] = foreground_g;
185 data[y * 4 * total_width + 4 * x + 2] = foreground_b;
186 data[y * 4 * total_width + 4 * x + 3] = foreground_a;
187 break;
188 case 2:
189 v2++;
190 data[y * 4 * total_width + 4 * x + 0] = halo_r;
191 data[y * 4 * total_width + 4 * x + 1] = halo_g;
192 data[y * 4 * total_width + 4 * x + 2] = halo_b;
193 data[y * 4 * total_width + 4 * x + 3] = halo_a;
194 break;
196 delete[] buffer1;
197 return img;
200 void hardsub_settings::reset()
202 rsettings.font_size = DEFAULT_FONT_SIZE;
203 rsettings.halo_thickness = DEFAULT_HALO_THICKNESS;
204 rsettings.foreground_r = DEFAULT_FOREGROUND_R;
205 rsettings.foreground_g = DEFAULT_FOREGROUND_G;
206 rsettings.foreground_b = DEFAULT_FOREGROUND_B;
207 rsettings.foreground_a = DEFAULT_FOREGROUND_A;
208 rsettings.halo_r = DEFAULT_HALO_R;
209 rsettings.halo_g = DEFAULT_HALO_G;
210 rsettings.halo_b = DEFAULT_HALO_B;
211 rsettings.halo_a = DEFAULT_HALO_A;
212 rsettings.background_r = DEFAULT_BACKGROUND_R;
213 rsettings.background_g = DEFAULT_BACKGROUND_G;
214 rsettings.background_b = DEFAULT_BACKGROUND_B;
215 rsettings.background_a = DEFAULT_BACKGROUND_A;
216 rsettings.align_type = DEFAULT_ALIGN_TYPE;
217 rsettings.spacing = DEFAULT_SPACING;
218 duration = DEFAULT_DURATION;
219 xalign_type = DEFAULT_XALIGN_TYPE;
220 yalign_type = DEFAULT_YALIGN_TYPE;
224 hardsub_settings::hardsub_settings()
226 reset();
229 subtitle* hardsub_settings::operator()()
231 init_variables();
232 image_frame_rgbx* img = rsettings();
233 try {
234 subtitle* sub = new subtitle();
235 sub->timecode = timecode;
236 sub->duration = duration;
237 sub->xalign_type = xalign_type;
238 sub->xalign = xalign;
239 sub->yalign_type = yalign_type;
240 sub->yalign = yalign;
241 sub->used_settings = rsettings;
242 sub->subtitle_img = img;
243 return sub;
244 } catch(...) {
245 delete img;
246 throw;
250 void render_subtitle(image_frame_rgbx& bottom, struct subtitle& sub)
252 int32_t xalign, yalign;
254 //Compute X offset.
255 switch(sub.xalign_type) {
256 case ALIGN_LEFT:
257 xalign = 0;
258 break;
259 case ALIGN_CENTER:
260 xalign = ((int32_t)bottom.get_width() - (int32_t)sub.subtitle_img->get_width()) / 2;
261 break;
262 case ALIGN_RIGHT:
263 xalign = (int32_t)bottom.get_width() - (int32_t)sub.subtitle_img->get_width();
264 break;
265 default:
266 xalign = sub.xalign;
269 //Compute Y offset.
270 switch(sub.yalign_type) {
271 case ALIGN_TOP:
272 yalign = 0;
273 break;
274 case ALIGN_CENTER:
275 yalign = ((int32_t)bottom.get_height() - (int32_t)sub.subtitle_img->get_height()) / 2;
276 break;
277 case ALIGN_BOTTOM:
278 yalign = (int32_t)bottom.get_height() - (int32_t)sub.subtitle_img->get_height();
279 break;
280 default:
281 yalign = sub.yalign;
284 if(xalign < -(int32_t)sub.subtitle_img->get_width() || yalign < -(int32_t)sub.subtitle_img->get_height())
285 return; //Outside image.
286 if(xalign >= (int32_t)bottom.get_width() || yalign >= (int32_t)bottom.get_height())
287 return; //Outside image.
288 if(sub.subtitle_img->get_width() == 0 || sub.subtitle_img->get_height() == 0)
289 return; //Nothing to draw.
291 uint32_t overlay_xoffset = (xalign < 0) ? -xalign : 0;
292 uint32_t overlay_yoffset = (yalign < 0) ? -yalign : 0;
293 uint32_t overlay_width = sub.subtitle_img->get_width() - overlay_xoffset;
294 uint32_t overlay_height = sub.subtitle_img->get_height() - overlay_yoffset;
295 if(xalign < 0)
296 xalign = 0;
297 if(yalign < 0)
298 yalign = 0;
299 if(xalign + overlay_width > bottom.get_width())
300 overlay_width = bottom.get_width() - xalign;
301 if(yalign + overlay_height > bottom.get_height())
302 overlay_height = bottom.get_height() - yalign;
304 for(uint32_t y = 0; y < overlay_height; y++) {
305 unsigned char* bottomr = bottom.get_pixels() + ((y + yalign) * 4 * bottom.get_width()) + 4 * xalign;
306 unsigned char* overlayr = sub.subtitle_img->get_pixels() + ((y + overlay_yoffset) * 4 *
307 sub.subtitle_img->get_width()) + 4 * overlay_xoffset;
308 for(uint32_t x = 0; x < overlay_width; x++) {
309 uint32_t ibase = x * 4;
310 int alpha = overlayr[ibase + 3];
311 bottomr[ibase + 0] = (unsigned char)((overlayr[ibase + 0] * alpha + bottomr[ibase + 0] *
312 (255 - alpha)) / 255);
313 bottomr[ibase + 1] = (unsigned char)((overlayr[ibase + 1] * alpha + bottomr[ibase + 1] *
314 (255 - alpha)) / 255);
315 bottomr[ibase + 2] = (unsigned char)((overlayr[ibase + 2] * alpha + bottomr[ibase + 2] *
316 (255 - alpha)) / 255);
317 bottomr[ibase + 3] = 0;
322 std::list<hardsub_settings*> settings_stack;
324 namespace
326 int64_t signed_settingvalue(const std::string& _setting, int64_t limit_low, int64_t limit_high)
328 std::string setting = settingvalue(_setting);
329 int64_t parsed = 0;
330 uint64_t rawparsed = 0;
331 bool negative = false;
332 size_t index = 0;
333 if(setting.length() > 0 && setting[0] == '-') {
334 negative = true;
335 index++;
337 if(index == setting.length())
338 throw std::runtime_error("Bad number");
340 for(; index < setting.length(); index++) {
341 if(setting[index] < '0' || setting[index] > '9')
342 throw std::runtime_error("Bad number");
343 if(rawparsed >= 0xFFFFFFFFFFFFFFFFULL / 10)
344 throw std::runtime_error("Number absolute value too large");
345 rawparsed = 10 * rawparsed + (setting[index] - '0');
348 //Take negation if needed and check range.
349 if(!negative)
350 if(rawparsed > 0x7FFFFFFFFFFFFFFFULL)
351 throw std::runtime_error("Value overflows");
352 else
353 parsed = rawparsed;
354 else
355 if(rawparsed > 0x8000000000000000ULL)
356 throw std::runtime_error("Value underflows");
357 else if(rawparsed == 0x8000000000000000ULL)
358 parsed = 2 * -(int64_t)(1ULL << 62);
359 else
360 parsed = -(int64_t)rawparsed;
362 if(parsed < limit_low || parsed > limit_high)
363 throw std::runtime_error("Value outside valid range");
364 return parsed;
367 uint64_t time_settingvalue(const std::string& setting)
369 std::string v = settingvalue(setting);
370 return parse_timespec(v);
373 void color_settingvalue(const std::string& setting, uint8_t& r, uint8_t& g, uint8_t& b, uint8_t& a)
375 std::string v = settingvalue(setting);
376 uint8_t c[4];
377 size_t component = 0;
378 size_t index = 0;
379 while(index < v.length()) {
380 if(component > 3)
381 throw std::runtime_error("Bad color specification");
383 //Find the start of next component and component length.
384 size_t tmp = v.find_first_of(",", index);
385 size_t cstart = index;
386 size_t clength = 0;
387 if(tmp > v.length()) {
388 index = v.length();
389 clength = v.length() - cstart;
390 } else {
391 index = tmp + 1;
392 clength = tmp - cstart;
395 uint16_t value = 0;
396 if(clength == 0 || clength > 3)
397 throw std::runtime_error("Bad color specification");
398 for(size_t i = 0; i < clength; i++)
399 if(v[cstart + i] < '0' || v[cstart + i] > '9')
400 throw std::runtime_error("Bad color specification");
401 else
402 value = value * 10 + (v[cstart + i] - '0');
403 if(value > 255)
404 throw std::runtime_error("Bad color specification");
405 c[component] = value;
407 component++;
409 if(component == 0)
410 throw std::runtime_error("Bad color specification");
411 else if(component == 1) {
412 a = c[0];
413 } else if(component == 2) {
414 r = g = b = c[0];
415 a = c[1];
416 } else if(component == 3) {
417 r = c[0];
418 g = c[1];
419 b = c[2];
420 } else if(component == 4) {
421 r = c[0];
422 g = c[1];
423 b = c[2];
424 a = c[3];
428 subtitle* parse_subtitle_option_one(struct hardsub_settings& settings, const std::string& option)
430 if(isstringprefix(option, "font="))
431 settings.rsettings.font_name = settingvalue(option);
432 else if(isstringprefix(option, "size="))
433 settings.rsettings.font_size = signed_settingvalue(option, 1, 10000);
434 else if(isstringprefix(option, "spacing="))
435 settings.rsettings.spacing = signed_settingvalue(option, 0, 10000);
436 else if(isstringprefix(option, "duration="))
437 settings.duration = time_settingvalue(option);
438 else if(option == "xpos=left")
439 settings.xalign_type = ALIGN_LEFT;
440 else if(option == "xpos=center")
441 settings.xalign_type = ALIGN_CENTER;
442 else if(option == "xpos=right")
443 settings.xalign_type = ALIGN_RIGHT;
444 else if(isstringprefix(option, "xpos=")) {
445 settings.xalign = signed_settingvalue(option, -2000000000, 2000000000);
446 settings.xalign_type = ALIGN_CUSTOM;
447 } else if(option == "ypos=top")
448 settings.yalign_type = ALIGN_TOP;
449 else if(option == "ypos=center")
450 settings.yalign_type = ALIGN_CENTER;
451 else if(option == "ypos=bottom")
452 settings.yalign_type = ALIGN_BOTTOM;
453 else if(isstringprefix(option, "ypos=")) {
454 settings.yalign = signed_settingvalue(option, -2000000000, 2000000000);
455 settings.yalign_type = ALIGN_CUSTOM;
456 } else if(isstringprefix(option, "halo="))
457 settings.rsettings.halo_thickness = signed_settingvalue(option, 0, 1000);
458 else if(option == "textalign=left")
459 settings.rsettings.align_type = ALIGN_LEFT;
460 else if(option == "textalign=center")
461 settings.rsettings.align_type = ALIGN_CENTER;
462 else if(option == "textalign=right")
463 settings.rsettings.align_type = ALIGN_RIGHT;
464 else if(option == "reset")
465 settings.reset();
466 else if(option == "push")
467 settings_stack.push_back(new hardsub_settings(settings));
468 else if(option == "pop") {
469 if(settings_stack.empty())
470 throw std::runtime_error("Attempt to pop empty stack");
471 hardsub_settings* s = settings_stack.back();
472 settings_stack.pop_back();
473 settings = *s;
474 delete s;
475 } else if(isstringprefix(option, "text=")) {
476 std::string tmp = settingvalue(option);
477 size_t split = tmp.find_first_of(",");
478 if(split > tmp.length())
479 throw std::runtime_error("Bad text syntax");
480 settings.timecode = parse_timespec(tmp.substr(0, split));
481 settings.rsettings.text = tmp.substr(split + 1);
482 return settings();
483 } else if(isstringprefix(option, "foreground-color=")) {
484 uint8_t r = 255, g = 255, b = 255, a = 255;
485 color_settingvalue(option, r, g, b, a);
486 settings.rsettings.foreground_r = r;
487 settings.rsettings.foreground_g = g;
488 settings.rsettings.foreground_b = b;
489 settings.rsettings.foreground_a = a;
490 } else if(isstringprefix(option, "halo-color=")) {
491 uint8_t r = 0, g = 0, b = 0, a = 255;
492 color_settingvalue(option, r, g, b, a);
493 settings.rsettings.halo_r = r;
494 settings.rsettings.halo_g = g;
495 settings.rsettings.halo_b = b;
496 settings.rsettings.halo_a = a;
497 } else if(isstringprefix(option, "background-color=")) {
498 uint8_t r = 0, g = 0, b = 0, a = 0;
499 color_settingvalue(option, r, g, b, a);
500 settings.rsettings.background_r = r;
501 settings.rsettings.background_g = g;
502 settings.rsettings.background_b = b;
503 settings.rsettings.background_a = a;
504 } else
505 throw std::runtime_error("Unknown subtitle option");
506 return NULL;
511 std::list<subtitle*> parse_subtitle_option(struct hardsub_settings& settings, const std::string& option)
513 std::list<subtitle*> list;
515 if(isstringprefix(option, "script=")) {
516 std::string filename = settingvalue(option);
517 std::ifstream stream(filename.c_str());
518 if(!stream)
519 throw std::runtime_error("Can't open script file");
520 while(stream) {
521 std::string opt;
522 std::getline(stream, opt);
523 if(opt == "")
524 continue;
525 subtitle* x = parse_subtitle_option_one(settings, opt);
526 if(x)
527 list.push_back(x);
529 if(!stream.eof() && (stream.bad() || stream.fail()))
530 throw std::runtime_error("Can't read script file");
531 } else {
532 subtitle* x = parse_subtitle_option_one(settings, option);
533 if(x)
534 list.push_back(x);
537 return list;
540 void print_hardsubs_help(const std::string& prefix)
542 std::cout << prefix << "font=<file>" << std::endl;
543 std::cout << "\tUse the specified font." << std::endl;
544 std::cout << prefix << "size=<size>" << std::endl;
545 std::cout << "\tUse the specified font size (default 16)." << std::endl;
546 std::cout << prefix << "xpos=<pixels>" << std::endl;
547 std::cout << "\tUse the specified subtitle x-offset." << std::endl;
548 std::cout << prefix << "xpos=left" << std::endl;
549 std::cout << "\tPlace subtitles to left." << std::endl;
550 std::cout << prefix << "xpos=center" << std::endl;
551 std::cout << "\tPlace subtitles to center (default)." << std::endl;
552 std::cout << prefix << "xpos=right" << std::endl;
553 std::cout << "\tPlace subtitles to right." << std::endl;
554 std::cout << prefix << "ypos=<pixels>" << std::endl;
555 std::cout << "\tUse the specified subtitle y-offset." << std::endl;
556 std::cout << prefix << "ypos=top" << std::endl;
557 std::cout << "\tPlace subtitles to top." << std::endl;
558 std::cout << prefix << "ypos=center" << std::endl;
559 std::cout << "\tPlace subtitles to center." << std::endl;
560 std::cout << prefix << "ypos=bottom" << std::endl;
561 std::cout << "\tPlace subtitles to bottom (default)." << std::endl;
562 std::cout << prefix << "duration=<duration>" << std::endl;
563 std::cout << "\tSubtitles last <duration>." << std::endl;
564 std::cout << prefix << "halo=<thickness>" << std::endl;
565 std::cout << "\tSubtitle halo thickness <thickness>." << std::endl;
566 std::cout << prefix << "foreground-color=<a>" << std::endl;
567 std::cout << prefix << "foreground-color=<rgb>,<a>" << std::endl;
568 std::cout << prefix << "foreground-color=<r>,<g>,<b>" << std::endl;
569 std::cout << prefix << "foreground-color=<r>,<g>,<b>,<a>" << std::endl;
570 std::cout << "\tSet foreground color. Default is fully opaque white." << std::endl;
571 std::cout << prefix << "halo-color=<a>" << std::endl;
572 std::cout << prefix << "halo-color=<rgb>,<a>" << std::endl;
573 std::cout << prefix << "halo-color=<r>,<g>,<b>" << std::endl;
574 std::cout << prefix << "halo-color=<r>,<g>,<b>,<a>" << std::endl;
575 std::cout << "\tSet halo color. Default is fully opaque black." << std::endl;
576 std::cout << prefix << "background-color=<a>" << std::endl;
577 std::cout << prefix << "background-color=<rgb>,<a>" << std::endl;
578 std::cout << prefix << "background-color=<r>,<g>,<b>" << std::endl;
579 std::cout << prefix << "background-color=<r>,<g>,<b>,<a>" << std::endl;
580 std::cout << "\tSet background color. Default is fully transparent black." << std::endl;
581 std::cout << prefix << "textalign=left" << std::endl;
582 std::cout << prefix << "textalign=center" << std::endl;
583 std::cout << prefix << "textalign=right" << std::endl;
584 std::cout << "\tSet text alignment between lines. Default is center." << std::endl;
585 std::cout << prefix << "spacing=<spacing>" << std::endl;
586 std::cout << "\tSet text spacing between lines. Default is 1." << std::endl;
587 std::cout << prefix << "text=<timecode>,<text>" << std::endl;
588 std::cout << "\tDisplay <text> at <timecode>. '\\\\' stands for backslash," << std::endl;
589 std::cout << "\t'\\n' stands for newline." << std::endl;
590 std::cout << prefix << "reset" << std::endl;
591 std::cout << "\tReset to defaults." << std::endl;
592 std::cout << prefix << "push" << std::endl;
593 std::cout << "\tPush current settings to stack." << std::endl;
594 std::cout << prefix << "pop" << std::endl;
595 std::cout << "\tPop settings from stack." << std::endl;
596 std::cout << prefix << "script=<file>" << std::endl;
597 std::cout << "\tRead subtitle commands from <file> and do them." << std::endl;
600 std::list<subtitle*> process_hardsubs_options(struct hardsub_settings& settings, const std::string& prefix, int argc, char** argv)
602 std::list<subtitle*> global;
603 for(int i = 1; i < argc; i++) {
604 std::string arg = argv[i];
605 if(arg == "--")
606 break;
607 if(!isstringprefix(arg, prefix))
608 continue;
609 try {
610 std::list<subtitle*> local_list;
611 local_list = parse_subtitle_option(settings, arg.substr(prefix.length()));
612 for(std::list<subtitle*>::iterator i = local_list.begin(); i != local_list.end(); i++)
613 global.push_back(*i);
614 } catch(std::exception& e) {
615 std::stringstream str;
616 str << "Error processing option '" << arg << "': " << e.what();
617 throw std::runtime_error(str.str());
620 return global;
623 void subtitle_process_gameinfo(std::list<subtitle*>& subs, struct packet& p)
625 if(p.rp_minor == 'A' || p.rp_minor == 'G') {
626 std::stringstream str;
627 for(size_t i = 0; i < p.rp_payload.size(); i++)
628 str << p.rp_payload[i];
629 std::string newarg = str.str();
630 subtitle_update_parameter(subs, p.rp_minor, newarg);
631 } else if(p.rp_minor == 'R') {
632 std::stringstream str;
633 uint64_t v = 0;
634 if(p.rp_payload.size() < 8)
635 return;
636 v |= (uint64_t)p.rp_payload[0] << 56;
637 v |= (uint64_t)p.rp_payload[1] << 48;
638 v |= (uint64_t)p.rp_payload[2] << 40;
639 v |= (uint64_t)p.rp_payload[3] << 32;
640 v |= (uint64_t)p.rp_payload[4] << 24;
641 v |= (uint64_t)p.rp_payload[5] << 16;
642 v |= (uint64_t)p.rp_payload[6] << 8;
643 v |= (uint64_t)p.rp_payload[7];
644 str << v;
645 std::string newarg = str.str();
646 subtitle_update_parameter(subs, p.rp_minor, newarg);
647 } else if(p.rp_minor == 'L') {
648 std::stringstream str;
649 uint64_t v = 0;
650 if(p.rp_payload.size() < 8)
651 return;
652 v |= (uint64_t)p.rp_payload[0] << 56;
653 v |= (uint64_t)p.rp_payload[1] << 48;
654 v |= (uint64_t)p.rp_payload[2] << 40;
655 v |= (uint64_t)p.rp_payload[3] << 32;
656 v |= (uint64_t)p.rp_payload[4] << 24;
657 v |= (uint64_t)p.rp_payload[5] << 16;
658 v |= (uint64_t)p.rp_payload[6] << 8;
659 v |= (uint64_t)p.rp_payload[7];
660 v = (v + 999999) / 1000000;
661 uint64_t hours = v / 3600000;
662 v %= 3600000;
663 uint64_t minutes = v / 60000;
664 v %= 60000;
665 uint64_t seconds = v / 1000;
666 v %= 1000;
667 if(hours > 0)
668 str << hours << ":";
669 str << std::setfill('0') << std::setw(2) << minutes << ":" << std::setfill('0')
670 << std::setw(2) << seconds << "." << std::setfill('0') << std::setw(3) << v;
671 std::string newarg = str.str();
672 subtitle_update_parameter(subs, p.rp_minor, newarg);
673 } else {
674 std::cerr << "WARNING: Unknown gameinfo type " << (unsigned)p.rp_minor << "." << std::endl;
678 subtitle::~subtitle()
682 void subtitle_update_parameter(std::list<subtitle*>& subs, unsigned char parameter, const std::string& value)
684 variables[parameter] = value;
685 for(std::list<subtitle*>::iterator i = subs.begin(); i != subs.end(); ++i)
686 try {
687 image_frame_rgbx* subtitle_img = (*i)->subtitle_img;
688 (*i)->subtitle_img = (*i)->used_settings();
689 delete subtitle_img;
690 } catch(std::exception& e) {
695 #ifdef SUBTITLE_TEST
699 int main(int argc, char** argv)
701 std::list<subtitle*> list, list2;
702 hardsub_settings s;
703 for(int i = 1; i < argc; i++) {
704 list2 = parse_subtitle_option(s, argv[i]);
705 for(std::list<subtitle*>::iterator j = list2.begin(); j != list2.end(); ++j)
706 list.push_back(*j);
709 SDL_Init(SDL_INIT_EVERYTHING);
710 for(std::list<subtitle*>::iterator i = list.begin(); i != list.end(); ++i) {
711 image_frame_rgbx& _i = *((*i)->subtitle_img);
712 uint32_t iwidth = _i.get_width();
713 const unsigned char* idata = _i.get_pixels();
714 SDL_Surface* s = SDL_SetVideoMode(_i.get_width(), _i.get_height(), 32, SDL_SWSURFACE | SDL_DOUBLEBUF);
716 SDL_LockSurface(s);
717 for(uint32_t y = 0; y < _i.get_height(); y++)
718 for(uint32_t x = 0; x < iwidth; x++) {
719 ((unsigned char*)s->pixels)[y * s->pitch + 4 * x + 0] =
720 idata[y * 4 * iwidth + 4 * x + 0];
721 ((unsigned char*)s->pixels)[y * s->pitch + 4 * x + 1] =
722 idata[y * 4 * iwidth + 4 * x + 1];
723 ((unsigned char*)s->pixels)[y * s->pitch + 4 * x + 2] =
724 idata[y * 4 * iwidth + 4 * x + 2];
725 ((unsigned char*)s->pixels)[y * s->pitch + 4 * x + 3] =
726 idata[y * 4 * iwidth + 4 * x + 3];
728 SDL_UnlockSurface(s);
730 SDL_Flip(s);
731 SDL_Event e;
732 wait_loop:
733 if(!SDL_WaitEvent(&e))
734 std::cerr << "Can't wait for event" << std::endl;
735 else if(e.type == SDL_QUIT)
736 continue;
737 goto wait_loop;
739 SDL_Quit();
741 return 0;
744 #endif