1 #include "hardsubs.hpp"
9 #include "timeparse.hpp"
16 uint32_t resolution_w
= 0;
17 uint32_t resolution_h
= 0;
19 std::map
<unsigned char, std::string
> variables
;
21 void check_in_bounds(struct subtitle
& sub
)
24 int xalign_type
= sub
.xalign_type
;
25 int yalign_type
= sub
.yalign_type
;
26 int32_t xalign
= sub
.xalign
;
27 int32_t yalign
= sub
.yalign
;
28 uint32_t width
= sub
.subtitle_img
->get_width();
29 uint32_t height
= sub
.subtitle_img
->get_height();
30 if(xalign_type
== ALIGN_CUSTOM
&& xalign
< 0)
32 if(xalign_type
== ALIGN_CUSTOM
&& xalign
+ width
> resolution_w
)
34 if(width
> resolution_w
)
36 if(yalign_type
== ALIGN_CUSTOM
&& yalign
< 0)
38 if(yalign_type
== ALIGN_CUSTOM
&& yalign
+ height
> resolution_h
)
40 if(height
> resolution_h
)
43 std::cerr
<< "WARNING: Subtitles exceed display bounds" << std::endl
;
48 if(!variables
.count('\\'))
49 variables
['\\'] = '\\';
50 if(!variables
.count('A'))
51 variables
['A'] = "(unknown)";
52 if(!variables
.count('R'))
53 variables
['R'] = "(unknown)";
54 if(!variables
.count('L'))
55 variables
['L'] = "(unknown)";
56 if(!variables
.count('G'))
57 variables
['G'] = "(unknown)";
60 void copy_surface(SDL_Surface
* s
, unsigned char* buffer
, uint32_t total_width
, uint32_t y
,
61 uint32_t align_type
, uint32_t extralines
)
69 xalign
= (total_width
- s
->w
) / 2;
72 xalign
= total_width
- s
->w
;
76 for(uint32_t y2
= 0; y2
< s
->h
+ extralines
; y2
++)
77 for(uint32_t x
= 0; x
< total_width
; x
++) {
78 if(x
< xalign
|| x
> xalign
+ (uint32_t)s
->w
|| y2
>= (uint32_t)s
->h
)
79 buffer
[(y
+ y2
) * total_width
+ x
] = 0;
81 buffer
[(y
+ y2
) * total_width
+ x
] =
82 ((unsigned char*)s
->pixels
)[y2
* s
->pitch
+ (x
- xalign
)];
87 void render_halo(uint32_t w
, uint32_t h
, unsigned char* data
, uint32_t _thickness
)
89 int32_t thickness
= _thickness
;
90 for(uint32_t y
= 0; y
< h
; y
++)
91 for(uint32_t x
= 0; x
< w
; x
++) {
92 bool left
= (x
> 0) ? (data
[y
* w
+ x
- 1] == 1) : false;
93 bool up
= (y
> 0) ? (data
[y
* w
+ x
- w
] == 1) : false;
94 if(data
[y
* w
+ x
] != 1)
97 for(int32_t j
= up
? thickness
: -thickness
; j
<= thickness
; j
++) {
98 if(y
+ j
< 0 || y
+ j
>= h
)
100 for(int32_t i
= left
? thickness
: -thickness
; i
<= thickness
; i
++) {
101 if(x
+ i
< 0 || x
+ i
>= w
)
103 if(data
[(y
+ j
) * w
+ (x
+ i
)] == 0)
104 data
[(y
+ j
) * w
+ (x
+ i
)] = 2;
111 image_frame_rgbx
* hardsub_render_settings::operator()()
113 std::list
<SDL_Surface
*> lines
;
115 throw std::runtime_error("No font set");
117 //Initialize SDL_ttf.
118 if(!TTF_WasInit() && TTF_Init() < 0) {
119 std::stringstream str
;
120 str
<< "Can't initialize SDL_ttf: " << TTF_GetError();
121 throw std::runtime_error(str
.str());
124 //Open the font and render the text.
125 TTF_Font
* font
= TTF_OpenFont(font_name
.c_str(), font_size
);
128 clr
.r
= clr
.g
= clr
.b
= 255;
129 std::string tmp
= "";
131 for(size_t i
= 0; i
< text
.length(); i
++) {
132 char tmp2
[2] = {0, 0};
140 else if(variables
.count(text
[i
])) {
141 tmp
+= variables
[text
[i
]];
143 } else if(text
[i
] == 'n') {
144 SDL_Surface
* s
= TTF_RenderUTF8_Solid(font
, tmp
.c_str(), clr
);
146 throw std::runtime_error("Can't render text");
151 std::stringstream str
;
152 str
<< "Bad escape character '" << text
[i
] << "'";
153 throw std::runtime_error(str
.str());
157 SDL_Surface
* s
= TTF_RenderUTF8_Solid(font
, tmp
.c_str(), clr
);
159 throw std::runtime_error("Can't render text");
168 //Calculate image size and allocate buffers.
169 uint32_t total_width
= 0;
170 uint32_t total_height
= 0;
171 unsigned char* buffer1
= NULL
;
172 image_frame_rgbx
* img
= NULL
;
173 for(std::list
<SDL_Surface
*>::iterator i
= lines
.begin(); i
!= lines
.end(); ++i
) {
174 if((*i
)->w
+ 2 * halo_thickness
> total_width
)
175 total_width
= (*i
)->w
+ 2 * halo_thickness
;
176 total_height
+= ((*i
)->h
+ 2 * halo_thickness
+ spacing
);
179 buffer1
= new unsigned char[total_width
* total_height
];
180 img
= new image_frame_rgbx(total_width
, total_height
);
188 //Copy SDL surfaces to index buffer.
190 for(std::list
<SDL_Surface
*>::iterator i
= lines
.begin(); i
!= lines
.end(); ++i
) {
191 copy_surface(*i
, buffer1
, total_width
, line
, align_type
, spacing
+ 2 * halo_thickness
);
192 line
+= ((*i
)->h
+ spacing
+ 2 * halo_thickness
);
195 render_halo(total_width
, total_height
, buffer1
, halo_thickness
);
197 //Make full color buffer from indexed buffer.
198 uint32_t v0
= 0, v1
= 0, v2
= 0;
199 unsigned char* data
= img
->get_pixels();
200 for(uint32_t y
= 0; y
< total_height
; y
++)
201 for(uint32_t x
= 0; x
< total_width
; x
++)
202 switch(buffer1
[y
* total_width
+ x
]) {
205 data
[y
* 4 * total_width
+ 4 * x
+ 0] = background_r
;
206 data
[y
* 4 * total_width
+ 4 * x
+ 1] = background_g
;
207 data
[y
* 4 * total_width
+ 4 * x
+ 2] = background_b
;
208 data
[y
* 4 * total_width
+ 4 * x
+ 3] = background_a
;
212 data
[y
* 4 * total_width
+ 4 * x
+ 0] = foreground_r
;
213 data
[y
* 4 * total_width
+ 4 * x
+ 1] = foreground_g
;
214 data
[y
* 4 * total_width
+ 4 * x
+ 2] = foreground_b
;
215 data
[y
* 4 * total_width
+ 4 * x
+ 3] = foreground_a
;
219 data
[y
* 4 * total_width
+ 4 * x
+ 0] = halo_r
;
220 data
[y
* 4 * total_width
+ 4 * x
+ 1] = halo_g
;
221 data
[y
* 4 * total_width
+ 4 * x
+ 2] = halo_b
;
222 data
[y
* 4 * total_width
+ 4 * x
+ 3] = halo_a
;
229 void hardsub_settings::reset()
231 rsettings
.font_size
= DEFAULT_FONT_SIZE
;
232 rsettings
.halo_thickness
= DEFAULT_HALO_THICKNESS
;
233 rsettings
.foreground_r
= DEFAULT_FOREGROUND_R
;
234 rsettings
.foreground_g
= DEFAULT_FOREGROUND_G
;
235 rsettings
.foreground_b
= DEFAULT_FOREGROUND_B
;
236 rsettings
.foreground_a
= DEFAULT_FOREGROUND_A
;
237 rsettings
.halo_r
= DEFAULT_HALO_R
;
238 rsettings
.halo_g
= DEFAULT_HALO_G
;
239 rsettings
.halo_b
= DEFAULT_HALO_B
;
240 rsettings
.halo_a
= DEFAULT_HALO_A
;
241 rsettings
.background_r
= DEFAULT_BACKGROUND_R
;
242 rsettings
.background_g
= DEFAULT_BACKGROUND_G
;
243 rsettings
.background_b
= DEFAULT_BACKGROUND_B
;
244 rsettings
.background_a
= DEFAULT_BACKGROUND_A
;
245 rsettings
.align_type
= DEFAULT_ALIGN_TYPE
;
246 rsettings
.spacing
= DEFAULT_SPACING
;
247 duration
= DEFAULT_DURATION
;
248 xalign_type
= DEFAULT_XALIGN_TYPE
;
249 yalign_type
= DEFAULT_YALIGN_TYPE
;
253 hardsub_settings::hardsub_settings()
258 subtitle
* hardsub_settings::operator()()
261 image_frame_rgbx
* img
= rsettings();
263 subtitle
* sub
= new subtitle();
264 sub
->timecode
= timecode
;
265 sub
->duration
= duration
;
266 sub
->xalign_type
= xalign_type
;
267 sub
->xalign
= xalign
;
268 sub
->yalign_type
= yalign_type
;
269 sub
->yalign
= yalign
;
270 sub
->used_settings
= rsettings
;
271 sub
->subtitle_img
= img
;
272 check_in_bounds(*sub
);
280 void render_subtitle(image_frame_rgbx
& bottom
, struct subtitle
& sub
)
282 int32_t xalign
, yalign
;
285 switch(sub
.xalign_type
) {
290 xalign
= ((int32_t)bottom
.get_width() - (int32_t)sub
.subtitle_img
->get_width()) / 2;
293 xalign
= (int32_t)bottom
.get_width() - (int32_t)sub
.subtitle_img
->get_width();
300 switch(sub
.yalign_type
) {
305 yalign
= ((int32_t)bottom
.get_height() - (int32_t)sub
.subtitle_img
->get_height()) / 2;
308 yalign
= (int32_t)bottom
.get_height() - (int32_t)sub
.subtitle_img
->get_height();
314 if(xalign
< -(int32_t)sub
.subtitle_img
->get_width() || yalign
< -(int32_t)sub
.subtitle_img
->get_height())
315 return; //Outside image.
316 if(xalign
>= (int32_t)bottom
.get_width() || yalign
>= (int32_t)bottom
.get_height())
317 return; //Outside image.
318 if(sub
.subtitle_img
->get_width() == 0 || sub
.subtitle_img
->get_height() == 0)
319 return; //Nothing to draw.
321 uint32_t overlay_xoffset
= (xalign
< 0) ? -xalign
: 0;
322 uint32_t overlay_yoffset
= (yalign
< 0) ? -yalign
: 0;
323 uint32_t overlay_width
= sub
.subtitle_img
->get_width() - overlay_xoffset
;
324 uint32_t overlay_height
= sub
.subtitle_img
->get_height() - overlay_yoffset
;
329 if(xalign
+ overlay_width
> bottom
.get_width())
330 overlay_width
= bottom
.get_width() - xalign
;
331 if(yalign
+ overlay_height
> bottom
.get_height())
332 overlay_height
= bottom
.get_height() - yalign
;
334 for(uint32_t y
= 0; y
< overlay_height
; y
++) {
335 unsigned char* bottomr
= bottom
.get_pixels() + ((y
+ yalign
) * 4 * bottom
.get_width()) + 4 * xalign
;
336 unsigned char* overlayr
= sub
.subtitle_img
->get_pixels() + ((y
+ overlay_yoffset
) * 4 *
337 sub
.subtitle_img
->get_width()) + 4 * overlay_xoffset
;
338 for(uint32_t x
= 0; x
< overlay_width
; x
++) {
339 uint32_t ibase
= x
* 4;
340 int alpha
= overlayr
[ibase
+ 3];
341 bottomr
[ibase
+ 0] = (unsigned char)((overlayr
[ibase
+ 0] * alpha
+ bottomr
[ibase
+ 0] *
342 (255 - alpha
)) / 255);
343 bottomr
[ibase
+ 1] = (unsigned char)((overlayr
[ibase
+ 1] * alpha
+ bottomr
[ibase
+ 1] *
344 (255 - alpha
)) / 255);
345 bottomr
[ibase
+ 2] = (unsigned char)((overlayr
[ibase
+ 2] * alpha
+ bottomr
[ibase
+ 2] *
346 (255 - alpha
)) / 255);
347 bottomr
[ibase
+ 3] = 0;
352 std::list
<hardsub_settings
*> settings_stack
;
356 int64_t signed_settingvalue(const std::string
& _setting
, int64_t limit_low
, int64_t limit_high
)
358 std::string setting
= settingvalue(_setting
);
360 uint64_t rawparsed
= 0;
361 bool negative
= false;
363 if(setting
.length() > 0 && setting
[0] == '-') {
367 if(index
== setting
.length())
368 throw std::runtime_error("Bad number");
370 for(; index
< setting
.length(); index
++) {
371 if(setting
[index
] < '0' || setting
[index
] > '9')
372 throw std::runtime_error("Bad number");
373 if(rawparsed
>= 0xFFFFFFFFFFFFFFFFULL
/ 10)
374 throw std::runtime_error("Number absolute value too large");
375 rawparsed
= 10 * rawparsed
+ (setting
[index
] - '0');
378 //Take negation if needed and check range.
380 if(rawparsed
> 0x7FFFFFFFFFFFFFFFULL
)
381 throw std::runtime_error("Value overflows");
385 if(rawparsed
> 0x8000000000000000ULL
)
386 throw std::runtime_error("Value underflows");
387 else if(rawparsed
== 0x8000000000000000ULL
)
388 parsed
= 2 * -(int64_t)(1ULL << 62);
390 parsed
= -(int64_t)rawparsed
;
392 if(parsed
< limit_low
|| parsed
> limit_high
)
393 throw std::runtime_error("Value outside valid range");
397 uint64_t time_settingvalue(const std::string
& setting
)
399 std::string v
= settingvalue(setting
);
400 return parse_timespec(v
);
403 void color_settingvalue(const std::string
& setting
, uint8_t& r
, uint8_t& g
, uint8_t& b
, uint8_t& a
)
405 std::string v
= settingvalue(setting
);
407 size_t component
= 0;
409 while(index
< v
.length()) {
411 throw std::runtime_error("Bad color specification");
413 //Find the start of next component and component length.
414 size_t tmp
= v
.find_first_of(",", index
);
415 size_t cstart
= index
;
417 if(tmp
> v
.length()) {
419 clength
= v
.length() - cstart
;
422 clength
= tmp
- cstart
;
426 if(clength
== 0 || clength
> 3)
427 throw std::runtime_error("Bad color specification");
428 for(size_t i
= 0; i
< clength
; i
++)
429 if(v
[cstart
+ i
] < '0' || v
[cstart
+ i
] > '9')
430 throw std::runtime_error("Bad color specification");
432 value
= value
* 10 + (v
[cstart
+ i
] - '0');
434 throw std::runtime_error("Bad color specification");
435 c
[component
] = value
;
440 throw std::runtime_error("Bad color specification");
441 else if(component
== 1) {
443 } else if(component
== 2) {
446 } else if(component
== 3) {
450 } else if(component
== 4) {
458 subtitle
* parse_subtitle_option_one(struct hardsub_settings
& settings
, const std::string
& option
)
460 if(isstringprefix(option
, "font="))
461 settings
.rsettings
.font_name
= settingvalue(option
);
462 else if(isstringprefix(option
, "size="))
463 settings
.rsettings
.font_size
= signed_settingvalue(option
, 1, 10000);
464 else if(isstringprefix(option
, "spacing="))
465 settings
.rsettings
.spacing
= signed_settingvalue(option
, 0, 10000);
466 else if(isstringprefix(option
, "duration="))
467 settings
.duration
= time_settingvalue(option
);
468 else if(option
== "xpos=left")
469 settings
.xalign_type
= ALIGN_LEFT
;
470 else if(option
== "xpos=center")
471 settings
.xalign_type
= ALIGN_CENTER
;
472 else if(option
== "xpos=right")
473 settings
.xalign_type
= ALIGN_RIGHT
;
474 else if(isstringprefix(option
, "xpos=")) {
475 settings
.xalign
= signed_settingvalue(option
, -2000000000, 2000000000);
476 settings
.xalign_type
= ALIGN_CUSTOM
;
477 } else if(option
== "ypos=top")
478 settings
.yalign_type
= ALIGN_TOP
;
479 else if(option
== "ypos=center")
480 settings
.yalign_type
= ALIGN_CENTER
;
481 else if(option
== "ypos=bottom")
482 settings
.yalign_type
= ALIGN_BOTTOM
;
483 else if(isstringprefix(option
, "ypos=")) {
484 settings
.yalign
= signed_settingvalue(option
, -2000000000, 2000000000);
485 settings
.yalign_type
= ALIGN_CUSTOM
;
486 } else if(isstringprefix(option
, "halo="))
487 settings
.rsettings
.halo_thickness
= signed_settingvalue(option
, 0, 1000);
488 else if(option
== "textalign=left")
489 settings
.rsettings
.align_type
= ALIGN_LEFT
;
490 else if(option
== "textalign=center")
491 settings
.rsettings
.align_type
= ALIGN_CENTER
;
492 else if(option
== "textalign=right")
493 settings
.rsettings
.align_type
= ALIGN_RIGHT
;
494 else if(option
== "reset")
496 else if(option
== "push")
497 settings_stack
.push_back(new hardsub_settings(settings
));
498 else if(option
== "pop") {
499 if(settings_stack
.empty())
500 throw std::runtime_error("Attempt to pop empty stack");
501 hardsub_settings
* s
= settings_stack
.back();
502 settings_stack
.pop_back();
505 } else if(isstringprefix(option
, "text=")) {
506 std::string tmp
= settingvalue(option
);
507 size_t split
= tmp
.find_first_of(",");
508 if(split
> tmp
.length())
509 throw std::runtime_error("Bad text syntax");
510 settings
.timecode
= parse_timespec(tmp
.substr(0, split
));
511 settings
.rsettings
.text
= tmp
.substr(split
+ 1);
513 } else if(isstringprefix(option
, "foreground-color=")) {
514 uint8_t r
= 255, g
= 255, b
= 255, a
= 255;
515 color_settingvalue(option
, r
, g
, b
, a
);
516 settings
.rsettings
.foreground_r
= r
;
517 settings
.rsettings
.foreground_g
= g
;
518 settings
.rsettings
.foreground_b
= b
;
519 settings
.rsettings
.foreground_a
= a
;
520 } else if(isstringprefix(option
, "halo-color=")) {
521 uint8_t r
= 0, g
= 0, b
= 0, a
= 255;
522 color_settingvalue(option
, r
, g
, b
, a
);
523 settings
.rsettings
.halo_r
= r
;
524 settings
.rsettings
.halo_g
= g
;
525 settings
.rsettings
.halo_b
= b
;
526 settings
.rsettings
.halo_a
= a
;
527 } else if(isstringprefix(option
, "background-color=")) {
528 uint8_t r
= 0, g
= 0, b
= 0, a
= 0;
529 color_settingvalue(option
, r
, g
, b
, a
);
530 settings
.rsettings
.background_r
= r
;
531 settings
.rsettings
.background_g
= g
;
532 settings
.rsettings
.background_b
= b
;
533 settings
.rsettings
.background_a
= a
;
535 throw std::runtime_error("Unknown subtitle option");
541 std::list
<subtitle
*> parse_subtitle_option(struct hardsub_settings
& settings
, const std::string
& option
)
543 std::list
<subtitle
*> list
;
545 if(isstringprefix(option
, "script=")) {
546 std::string filename
= settingvalue(option
);
547 std::ifstream
stream(filename
.c_str());
549 throw std::runtime_error("Can't open script file");
552 std::getline(stream
, opt
);
555 subtitle
* x
= parse_subtitle_option_one(settings
, opt
);
559 if(!stream
.eof() && (stream
.bad() || stream
.fail()))
560 throw std::runtime_error("Can't read script file");
562 subtitle
* x
= parse_subtitle_option_one(settings
, option
);
570 void print_hardsubs_help(const std::string
& prefix
)
572 std::cout
<< prefix
<< "font=<file>" << std::endl
;
573 std::cout
<< "\tUse the specified font." << std::endl
;
574 std::cout
<< prefix
<< "size=<size>" << std::endl
;
575 std::cout
<< "\tUse the specified font size (default 16)." << std::endl
;
576 std::cout
<< prefix
<< "xpos=<pixels>" << std::endl
;
577 std::cout
<< "\tUse the specified subtitle x-offset." << std::endl
;
578 std::cout
<< prefix
<< "xpos=left" << std::endl
;
579 std::cout
<< "\tPlace subtitles to left." << std::endl
;
580 std::cout
<< prefix
<< "xpos=center" << std::endl
;
581 std::cout
<< "\tPlace subtitles to center (default)." << std::endl
;
582 std::cout
<< prefix
<< "xpos=right" << std::endl
;
583 std::cout
<< "\tPlace subtitles to right." << std::endl
;
584 std::cout
<< prefix
<< "ypos=<pixels>" << std::endl
;
585 std::cout
<< "\tUse the specified subtitle y-offset." << std::endl
;
586 std::cout
<< prefix
<< "ypos=top" << std::endl
;
587 std::cout
<< "\tPlace subtitles to top." << std::endl
;
588 std::cout
<< prefix
<< "ypos=center" << std::endl
;
589 std::cout
<< "\tPlace subtitles to center." << std::endl
;
590 std::cout
<< prefix
<< "ypos=bottom" << std::endl
;
591 std::cout
<< "\tPlace subtitles to bottom (default)." << std::endl
;
592 std::cout
<< prefix
<< "duration=<duration>" << std::endl
;
593 std::cout
<< "\tSubtitles last <duration>." << std::endl
;
594 std::cout
<< prefix
<< "halo=<thickness>" << std::endl
;
595 std::cout
<< "\tSubtitle halo thickness <thickness>." << std::endl
;
596 std::cout
<< prefix
<< "foreground-color=<a>" << std::endl
;
597 std::cout
<< prefix
<< "foreground-color=<rgb>,<a>" << std::endl
;
598 std::cout
<< prefix
<< "foreground-color=<r>,<g>,<b>" << std::endl
;
599 std::cout
<< prefix
<< "foreground-color=<r>,<g>,<b>,<a>" << std::endl
;
600 std::cout
<< "\tSet foreground color. Default is fully opaque white." << std::endl
;
601 std::cout
<< prefix
<< "halo-color=<a>" << std::endl
;
602 std::cout
<< prefix
<< "halo-color=<rgb>,<a>" << std::endl
;
603 std::cout
<< prefix
<< "halo-color=<r>,<g>,<b>" << std::endl
;
604 std::cout
<< prefix
<< "halo-color=<r>,<g>,<b>,<a>" << std::endl
;
605 std::cout
<< "\tSet halo color. Default is fully opaque black." << std::endl
;
606 std::cout
<< prefix
<< "background-color=<a>" << std::endl
;
607 std::cout
<< prefix
<< "background-color=<rgb>,<a>" << std::endl
;
608 std::cout
<< prefix
<< "background-color=<r>,<g>,<b>" << std::endl
;
609 std::cout
<< prefix
<< "background-color=<r>,<g>,<b>,<a>" << std::endl
;
610 std::cout
<< "\tSet background color. Default is fully transparent black." << std::endl
;
611 std::cout
<< prefix
<< "textalign=left" << std::endl
;
612 std::cout
<< prefix
<< "textalign=center" << std::endl
;
613 std::cout
<< prefix
<< "textalign=right" << std::endl
;
614 std::cout
<< "\tSet text alignment between lines. Default is center." << std::endl
;
615 std::cout
<< prefix
<< "spacing=<spacing>" << std::endl
;
616 std::cout
<< "\tSet text spacing between lines. Default is 1." << std::endl
;
617 std::cout
<< prefix
<< "text=<timecode>,<text>" << std::endl
;
618 std::cout
<< "\tDisplay <text> at <timecode>. '\\\\' stands for backslash," << std::endl
;
619 std::cout
<< "\t'\\n' stands for newline." << std::endl
;
620 std::cout
<< prefix
<< "reset" << std::endl
;
621 std::cout
<< "\tReset to defaults." << std::endl
;
622 std::cout
<< prefix
<< "push" << std::endl
;
623 std::cout
<< "\tPush current settings to stack." << std::endl
;
624 std::cout
<< prefix
<< "pop" << std::endl
;
625 std::cout
<< "\tPop settings from stack." << std::endl
;
626 std::cout
<< prefix
<< "script=<file>" << std::endl
;
627 std::cout
<< "\tRead subtitle commands from <file> and do them." << std::endl
;
630 std::list
<subtitle
*> process_hardsubs_options(struct hardsub_settings
& settings
, const std::string
& prefix
, int argc
, char** argv
)
632 std::list
<subtitle
*> global
;
633 for(int i
= 1; i
< argc
; i
++) {
634 std::string arg
= argv
[i
];
637 if(!isstringprefix(arg
, prefix
))
640 std::list
<subtitle
*> local_list
;
641 local_list
= parse_subtitle_option(settings
, arg
.substr(prefix
.length()));
642 for(std::list
<subtitle
*>::iterator i
= local_list
.begin(); i
!= local_list
.end(); i
++)
643 global
.push_back(*i
);
644 } catch(std::exception
& e
) {
645 std::stringstream str
;
646 str
<< "Error processing option '" << arg
<< "': " << e
.what();
647 throw std::runtime_error(str
.str());
655 std::string
decimalstring_frombinary(const std::vector
<unsigned char>& num
, bool ssh_style
)
657 std::vector
<unsigned char> reverse
;
658 unsigned brounds
= ssh_style
? 7 : 8;
659 size_t length
= num
.size();
661 for(length
= 0; length
< num
.size(); length
++)
662 if(num
[length
] < 128)
665 if(length
> num
.size())
666 throw std::runtime_error("Invalid ssh-style mpint");
668 reverse
.push_back('0');
669 for(size_t i
= 0; i
< length
; i
++) {
670 for(size_t j
= 0; j
< brounds
; j
++) {
671 bool carry
= ((num
[i
] & (1 << (brounds
- 1 - j
))) != 0);
672 for(size_t k
= 0; k
< reverse
.size(); k
++) {
673 unsigned x
= 2 * (reverse
[k
] - '0') + (carry
? 1 : 0);
675 reverse
[k
] = '0' + x
% 10;
678 reverse
.push_back('1');
681 std::string
final(reverse
.size(), '0');
682 std::copy(reverse
.rbegin(), reverse
.rend(), final
.begin());
687 void subtitle_process_gameinfo(std::list
<subtitle
*>& subs
, struct packet
& p
)
689 if(p
.rp_minor
== 'A' || p
.rp_minor
== 'G') {
690 std::stringstream str
;
691 for(size_t i
= 0; i
< p
.rp_payload
.size(); i
++)
692 str
<< p
.rp_payload
[i
];
693 std::string newarg
= str
.str();
694 subtitle_update_parameter(subs
, p
.rp_minor
, newarg
);
695 } else if(p
.rp_minor
== 'R') {
696 subtitle_update_parameter(subs
, p
.rp_minor
, decimalstring_frombinary(p
.rp_payload
, false));
697 } else if(p
.rp_minor
== 'L') {
698 std::stringstream str
;
700 if(p
.rp_payload
.size() < 8)
702 v
|= (uint64_t)p
.rp_payload
[0] << 56;
703 v
|= (uint64_t)p
.rp_payload
[1] << 48;
704 v
|= (uint64_t)p
.rp_payload
[2] << 40;
705 v
|= (uint64_t)p
.rp_payload
[3] << 32;
706 v
|= (uint64_t)p
.rp_payload
[4] << 24;
707 v
|= (uint64_t)p
.rp_payload
[5] << 16;
708 v
|= (uint64_t)p
.rp_payload
[6] << 8;
709 v
|= (uint64_t)p
.rp_payload
[7];
710 v
= (v
+ 999999) / 1000000;
711 uint64_t hours
= v
/ 3600000;
713 uint64_t minutes
= v
/ 60000;
715 uint64_t seconds
= v
/ 1000;
719 str
<< std::setfill('0') << std::setw(2) << minutes
<< ":" << std::setfill('0')
720 << std::setw(2) << seconds
<< "." << std::setfill('0') << std::setw(3) << v
;
721 std::string newarg
= str
.str();
722 subtitle_update_parameter(subs
, p
.rp_minor
, newarg
);
724 std::cerr
<< "WARNING: Unknown gameinfo type " << (unsigned)p
.rp_minor
<< "." << std::endl
;
728 subtitle::~subtitle()
732 void subtitle_update_parameter(std::list
<subtitle
*>& subs
, unsigned char parameter
, const std::string
& value
)
734 variables
[parameter
] = value
;
735 for(std::list
<subtitle
*>::iterator i
= subs
.begin(); i
!= subs
.end(); ++i
)
737 image_frame_rgbx
* subtitle_img
= (*i
)->subtitle_img
;
738 (*i
)->subtitle_img
= (*i
)->used_settings();
740 check_in_bounds(**i
);
741 } catch(std::exception
& e
) {
742 std::cerr
<< "WARNING: Can't update subtitles: " << e
.what() << std::endl
;
746 void subtitle_set_resolution(uint32_t w
, uint32_t h
)
755 int real_main(int argc
, char** argv
)
757 std::list
<subtitle
*> list
, list2
;
759 for(int i
= 1; i
< argc
; i
++) {
760 list2
= parse_subtitle_option(s
, argv
[i
]);
761 for(std::list
<subtitle
*>::iterator j
= list2
.begin(); j
!= list2
.end(); ++j
)
765 SDL_Init(SDL_INIT_EVERYTHING
);
766 for(std::list
<subtitle
*>::iterator i
= list
.begin(); i
!= list
.end(); ++i
) {
767 image_frame_rgbx
& _i
= *((*i
)->subtitle_img
);
768 uint32_t iwidth
= _i
.get_width();
769 const unsigned char* idata
= _i
.get_pixels();
770 SDL_Surface
* s
= SDL_SetVideoMode(_i
.get_width(), _i
.get_height(), 32, SDL_SWSURFACE
| SDL_DOUBLEBUF
);
773 for(uint32_t y
= 0; y
< _i
.get_height(); y
++)
774 for(uint32_t x
= 0; x
< iwidth
; x
++) {
775 ((unsigned char*)s
->pixels
)[y
* s
->pitch
+ 4 * x
+ 0] =
776 idata
[y
* 4 * iwidth
+ 4 * x
+ 0];
777 ((unsigned char*)s
->pixels
)[y
* s
->pitch
+ 4 * x
+ 1] =
778 idata
[y
* 4 * iwidth
+ 4 * x
+ 1];
779 ((unsigned char*)s
->pixels
)[y
* s
->pitch
+ 4 * x
+ 2] =
780 idata
[y
* 4 * iwidth
+ 4 * x
+ 2];
781 ((unsigned char*)s
->pixels
)[y
* s
->pitch
+ 4 * x
+ 3] =
782 idata
[y
* 4 * iwidth
+ 4 * x
+ 3];
784 SDL_UnlockSurface(s
);
789 if(!SDL_WaitEvent(&e
))
790 std::cerr
<< "Can't wait for event" << std::endl
;
791 else if(e
.type
== SDL_QUIT
)