1 /*
2 * filter_subtitler.c
3 *
4 * Copyright (C) Jan Panteltje 2001
5 *
6 * This file is part of transcode, a video stream processing tool
7 * Font reading etc from Linux mplayer
8 *
9 * transcode 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 2, or (at your option)
12 * any later version.
13 *
14 * transcode 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 GNU Make; see the file COPYING. If not, write to
21 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22 *
23 */
24
25
26 #include "subtitler.h"
27
28
29 /* for YUV to RGB in X11 */
30 #define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
31 int write_ppm_flag;
32
33 int debug_flag;
34 int frame_offset;
35 font_desc_t *vo_font;
36 font_desc_t *subtitle_current_font_descriptor;
37 uint8_t *ImageData;
38 int image_width, image_height;
39 int default_font;
40 struct passwd *userinfo;
41 char *home_dir;
42 char *user_name;
43 int line_h_start, line_h_end;
44 int center_flag;
45 char *frame_memory0, *frame_memory1;
46 char *subtitle_file;
47 char *default_font_dir;
48 vob_t *vob;
49 double dmax_vector;
50 int use_pre_processing_flag;
51 char *subtitle_font_path;
52 char *default_subtitle_font_name;
53 int default_subtitle_symbols;
54 int default_subtitle_font_size;
55 int default_subtitle_iso_extention;
56 double default_subtitle_radius;
57 double default_subtitle_thickness;
58
59 int show_output_flag;
60 int window_open_flag;
61 int window_size;
62 unsigned char *ucptrs, *ucptrd;
63 int color_depth;
64
65 double default_font_factor;
66 double subtitle_extra_character_space;
67
68 int border_luminance;
69 int default_border_luminance;
70
71 double subtitle_h_factor;
72 double subtitle_v_factor;
73 double extra_character_space;
74
75 int rgb_palette[16][3]; // rgb
76 int rgb_palette_valid_flag;
77
78 int default_subtitle_font_symbols;
79
80 int dcontrast, brightness;
81 double dsaturation;
82 double dhue, dhue_line_drift;
83 int u_shift, v_shift;
84 int slice_level;
85
86 int add_objects_flag;
87 int help_flag;
88 int de_stripe_flag;
89
90 int movie_id;
91
92 static double acr, acg, acb, acu, acv;
93 static int use_emphasis2_for_anti_aliasing_flag;
94
95 extern int add_objects(int);
96 extern int execute(char *);
97
98 /*
99 subtitle 'filter',
100 it adds objects as described in a file in .ppml format,
101 */
tc_filter(frame_list_t * pfl_,char * options)102 int tc_filter(frame_list_t *pfl_, char *options)
103 {
104 vframe_list_t *pfl = (vframe_list_t *)pfl_;
105 int a, i, x;
106 double da, db;
107 int pre = 0;
108 int vid = 0;
109 static FILE *pppm_file;
110 static FILE *fptr;
111 char temp[4096];
112 static int frame_nr;
113 static uint8_t *pfm, *opfm, *pfmend, *opfmend;
114 static uint8_t *pline_start, *pline_end, *opline_start, *opline_end;
115 static int x_shift;
116 char *running;
117 char *token;
118 static int y, b;
119 uint8_t *py, *pu, *pv;
120 static int cr, cg, cb, cy, cu, cv;
121 static int have_bottom_margin_flag;
122 //aframe_list_t *afl;
123
124
125 if (pfl->tag & TC_FILTER_GET_CONFIG) {
126 optstr_filter_desc (options, MOD_NAME, MOD_CAP, MOD_VERSION, "Panteltje", "VRYO", "1");
127 return 0;
128 }
129
130 /* filter init */
131 if(pfl->tag & TC_FILTER_INIT)
132 {
133 vob = tc_get_vob();
134 if(! vob)
135 {
136 tc_log_error(MOD_NAME, "could not tc_get_vob() failed");
137
138 return -1;
139 }
140 if(verbose) tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP);
141
142 /* identify */
143 tc_log_info(MOD_NAME,
144 "Panteltje (c) movie composer%s (alias subtitle-filter)",
145 SUBTITLER_VERSION);
146
147 /* get home directory */
148 userinfo = getpwuid(getuid() );
149 home_dir = strsave(userinfo -> pw_dir);
150 user_name = strsave(userinfo -> pw_name);
151
152 /* set some defaults */
153
154 /* for subtitles */
155
156 /* use post processing */
157 use_pre_processing_flag = 0;
158
159 /* a frame in transcode is 40 ms */
160 frame_offset = 0;
161
162 /* this selects some other symbols it seems */
163 default_font = 0; // 1 = strange symbols like stars etc..
164
165 /* this sets the font outline */
166 default_font_factor = 10.75; // outline, was .75
167
168 /* this sets the horizontal space for the subtitles */
169 subtitle_h_factor = SUBTITLE_H_FACTOR;
170
171 /* this sets how far the subtitles start from the bottom */
172 subtitle_v_factor = SUBTITLE_V_FACTOR;
173
174 /* location where font.descr is */
175 tc_snprintf(temp, sizeof(temp), "%s/.xste/fonts", home_dir);
176 default_font_dir = strsave(temp);
177 if(! default_font_dir)
178 {
179 tc_log_error(MOD_NAME, "subtitler(): could not allocate space for default_font_dir");
180
181 return -1;
182 }
183
184 /*
185 the mplayer font seems to overlap getween 'l' and 't', added some
186 extra space between characters
187 */
188 extra_character_space = EXTRA_CHAR_SPACE;
189 subtitle_extra_character_space = EXTRA_CHAR_SPACE;
190
191 /* the ppml file */
192 tc_snprintf(temp, sizeof(temp), "%s/.subtitles/demo.ppml", home_dir);
193 subtitle_file = strsave(temp);
194 if(! subtitle_file)
195 {
196 tc_log_error(MOD_NAME,
197 "subtitler(): could not allocate space for subtitle_file");
198
199 return -1;
200 }
201
202 /* for picture adjust */
203 brightness = 0; // steps
204 dcontrast = 100.0; // percent
205 dsaturation = 100.0; // percent
206 u_shift = 0; // steps
207 v_shift = 0; // steps
208
209 /* for color correction */
210 dhue = 0.0;
211 dhue_line_drift = 0.0; // The rotation in degrees at the start and end
212 // of a line.
213 // This is for correcting color errors in NTSC
214 // tapes.
215 // Use in combination with dhue in color
216 // correction.
217
218
219 /* for show output in X11 */
220 window_open_flag = 0;
221 color_depth = 0; /* get from X */
222
223 /* module settings */
224 add_objects_flag = 1;
225 de_stripe_flag = 0;
226 write_ppm_flag = 0;
227 show_output_flag = 0;
228 center_flag = 1;
229
230 /* uses when we call transcode recursive, to run a movie in a movie */
231 movie_id = 0;
232
233 /* for chroma key, better do this not each pixel .. */
234 dmax_vector = sqrt( (127.0 * 127.0) + (127.0 * 127.0) );
235
236 /*
237 for rotate and shear, the level we cut the remaining parts.
238 Note that yuv_to_ppm() also uses this, to write a modified .ppm
239 that does NOT have this luminance in it, for use by mogrify.
240 This ONLY happens if rotate or shear present.
241 */
242 default_border_luminance = LUMINANCE_MASK;
243
244 tc_snprintf(temp, sizeof(temp), "%s/.xste/fonts", home_dir);
245 subtitle_font_path = strsave(temp);
246 if(! subtitle_font_path)
247 {
248 tc_log_error(MOD_NAME,
249 "subtitler: tc_filter(): could not allocate space for subtitle_font_path, aborting");
250
251 exit(1);
252 }
253
254 default_subtitle_font_name = strsave("arial.ttf");
255 if(! default_subtitle_font_name)
256 {
257 tc_log_error(MOD_NAME,
258 "subtitler: tc_filter(): could not allocate space for default_subtitle_font_name, aborting");
259
260 exit(1);
261 }
262
263 default_subtitle_symbols = 0;
264 default_subtitle_font_size = 28;
265 default_subtitle_iso_extention = 15;
266 default_subtitle_radius = 1.0;
267 default_subtitle_thickness = 0.1;
268
269 default_subtitle_font_symbols = 0;
270
271 rgb_palette_valid_flag = 0;
272
273 /* color standard */
274
275 /* Y spec */
276 acr = 0.3;
277 acg = 0.59;
278 acb = 0.11;
279
280 /* U spec */
281 acu = .5 / (1.0 - acb);
282
283 /* V spec */
284 acv = .5 / (1.0 - acr);
285
286 use_emphasis2_for_anti_aliasing_flag = 0;
287
288 debug_flag = 0;
289
290 /* end defaults */
291 if(debug_flag)
292 {
293 tc_log_info(MOD_NAME, "options=%s", options);
294 }
295
296 if(temp[0] != 0)
297 {
298 running = strsave(options);
299 if(! running)
300 {
301 tc_log_error(MOD_NAME, "subtitler(): strsave(options) failed");
302
303 return -1;
304 }
305 while(1)
306 {
307 token = strsep (&running, " ");
308 if(token == NULL) break;
309
310 /* avoid empty string */
311 if(token[0] == 0) continue;
312
313 if(strncmp(token, "no_objects", 10) == 0)
314 {
315 add_objects_flag = 0;
316 }
317 else if(strncmp(token, "write_ppm", 9) == 0)
318 {
319 write_ppm_flag = 1;
320 }
321 else if(strncmp(token, "debug", 5) == 0)
322 {
323 debug_flag = 1;
324 }
325 else if(strncmp(token, "help", 4) == 0)
326 {
327 help_flag = 1;
328 print_options();
329
330 /* error exit */
331 return 0;
332 //exit(1);
333 }
334 else if(strncmp(token, "subtitle_file=", 14) == 0)
335 {
336 a = sscanf(token, "subtitle_file=%s", temp);
337 if(a == 1)
338 {
339 free(subtitle_file);
340 subtitle_file = strsave(temp);
341 if(! subtitle_file)
342 {
343 tc_log_error(MOD_NAME,
344 "subtitler(): could not allocate space for subtitle_file");
345
346 return -1;
347 }
348 }
349 }
350 else if(strncmp(token, "font_dir=", 9) == 0)
351 {
352 a = sscanf(token, "font_dir=%s", temp);
353 if(a == 1)
354 {
355 free(default_font_dir);
356 default_font_dir = strsave(temp);
357 if(! default_font_dir)
358 {
359 tc_log_error(MOD_NAME,
360 "subtitler(): could not allocate space for default_font_dir");
361
362 return -1;
363 }
364 }
365 }
366 sscanf(token, "color_depth=%d", &color_depth);
367 sscanf(token, "font=%d", &default_font);
368 sscanf(token, "font_factor=%lf", &default_font_factor);
369 sscanf(token, "frame_offset=%d", &frame_offset);
370 sscanf(token, "movie_id=%d", &movie_id);
371
372 if(strcmp(token, "anti_alias") == 0) use_emphasis2_for_anti_aliasing_flag = 1;
373
374 if(strcmp(token, "use_pre_processing") == 0)
375 {
376 use_pre_processing_flag = 1;
377 }
378 } /* end while parse options */
379
380 free(running);
381 } /* end if options */
382
383 if(use_pre_processing_flag)
384 {
385 tc_log_info(MOD_NAME, "Using pre processing");
386 }
387 else
388 {
389 tc_log_info(MOD_NAME, "Using post processing");
390 }
391
392 if(debug_flag)
393 {
394 tc_log_info(MOD_NAME, "PARSER RESULT: "
395 "write_ppm_flag=%d add_objects_flag=%d show_output_flag=%d "
396 "color_depth=%d frame_offset=%d movie_id=%d "
397 "use_pre_processing_flag=%d",
398 write_ppm_flag, add_objects_flag, show_output_flag,\
399 color_depth, frame_offset, movie_id,\
400 use_pre_processing_flag\
401 );
402 }
403
404 if(add_objects_flag)
405 {
406 /* read in font (also needed for frame counter) */
407 // tc_snprintf(temp, sizeof(temp), "%s/font.desc", default_font_dir);
408 tc_snprintf(temp, sizeof(temp), "arial.ttf");
409 vo_font = add_font(temp, default_subtitle_symbols, 28, 15, 1.0, 0.1);
410 if(! vo_font)
411 {
412 tc_log_error(MOD_NAME, "subtitler(): Could not load font");
413
414 /* return init error */
415 return -1;
416 }
417
418 subtitle_current_font_descriptor = vo_font;
419
420 /* load ppml file */
421 if(! load_ppml_file(subtitle_file) )
422 {
423 tc_log_error(MOD_NAME, "subtitler(): could not load file %s",\
424 subtitle_file);
425
426 /* return init error */
427 return -1;
428 }
429 } /* end if add_objects_flag */
430
431 /* return init OK */
432 return 0;
433 } /* end if filter init */
434
435 /* filter close */
436 if(pfl->tag & TC_FILTER_CLOSE)
437 {
438 /* rely on exit() */
439
440 /* return close OK */
441 return 0;
442 } /* end if filter close */
443
444 /*
445 filter frame routine
446 tag variable indicates, if we are called before
447 transcodes internal video/audo frame processing routines
448 or after and determines video/audio context
449 */
450 if(verbose & TC_STATS)
451 {
452 tc_log_info(MOD_NAME, "%s/%s %s %s",\
453 vob->mod_path, MOD_NAME, MOD_VERSION, MOD_CAP);
454
455 /*
456 tag variable indicates, if we are called before
457 transcodes internal video/audo frame processing routines
458 or after and determines video/audio context
459 */
460
461 if(pfl->tag & TC_PRE_M_PROCESS) pre = 1;
462 if(pfl->tag & TC_POST_M_PROCESS) pre = 0;
463
464 if(pfl->tag & TC_VIDEO) vid = 1;
465 if(pfl->tag & TC_AUDIO) vid = 0;
466
467 tc_log_info(MOD_NAME, "frame [%06d] %s %16s call",\
468 pfl->id, (vid)?"(video)":"(audio)",\
469 (pre)?"pre-process filter":"post-process filter");
470 } /* end if verbose and stats */
471
472 /*
473 default:
474 add the subtitles, after the coding, else edges in text get bad
475 */
476 if(use_pre_processing_flag)
477 {
478 a = (pfl->tag & TC_PRE_M_PROCESS) && (pfl->tag & TC_VIDEO);
479 }
480 else
481 {
482 a = (pfl->tag & TC_POST_M_PROCESS) && (pfl->tag & TC_VIDEO);
483 }
484
485 if(a)
486 {
487 ImageData = pfl->video_buf;
488 image_width = pfl->v_width;
489 image_height = pfl->v_height;
490 frame_nr = pfl->id;
491 if(! have_bottom_margin_flag)
492 {
493 window_bottom = image_height - window_bottom;
494 have_bottom_margin_flag = 1;
495 }
496
497 if(debug_flag)
498 {
499 tc_log_info(MOD_NAME, \
500 "frame_nr=%d \
501 ImageData=%lu image_width=%d image_height=%d",\
502 frame_nr,\
503 (unsigned long)ImageData, image_width, image_height);
504 }
505
506 /*
507 calculate where to put and how to reformat the subtitles.
508 These are globals.
509 */
510 line_h_start = subtitle_h_factor * (double)image_width;
511 line_h_end = (double)image_width - (double)line_h_start;
512 window_bottom = image_height - (subtitle_v_factor * (double)image_height);
513
514 if(de_stripe_flag)
515 {
516 /*
517 create a place to save the current frame,
518 going to use it next frame to replace the lines that are all white,
519 as caused by severe dropouts in ancient Umatic tapes.
520 NOTE!:
521 This cannot be done in INIT as then pfl->v_width and pfl->v_height
522 are not available yet (zero).
523 */
524 if(! frame_memory0)
525 {
526 /* for RGB */
527 frame_memory0 = malloc(pfl->v_width * pfl->v_height * 3);
528 if(! frame_memory0)
529 {
530 tc_log_error(MOD_NAME, "de_striper(): could not malloc frame_memory0");
531
532 /* return error */
533 return -1;
534 }
535 frame_memory1 = malloc(pfl->v_width * pfl->v_height * 3);
536 if(! frame_memory1)
537 {
538 tc_log_error(MOD_NAME, "de_striper(): could not malloc frame_memory1");
539
540 /* return error */
541 return -1;
542 }
543 } /* end if ! frame_memory */
544
545 /* save the current frame for later */
546 for(i = 0; i < pfl->v_width * pfl->v_height * 3; i++)
547 {
548 frame_memory0[i] = pfl->video_buf[i];
549 }
550
551 slice_level = 0;
552 pfm = pfl->video_buf;
553 opfm = frame_memory1;
554 pfmend = ImageData + (pfl->v_height * pfl->v_width * 3);
555 opfmend = frame_memory1 + (pfl->v_height * pfl->v_width * 3);
556 for(y = 0; y < pfl->v_height; y++)
557 {
558 /* get line boundaries for video buffer */
559 pline_start = pfm;
560 pline_end = pfm + pfl->v_width * 3;
561
562 /* get line boundaries for frame_memory1 */
563 opline_start = opfm;
564 opline_end = opfm + pfl->v_width * 3;
565 x_shift = 0;
566 /*
567 perhaps expand condition for more then one pixel in a line
568 */
569 for(x = 0; x < pfl->v_width; x++)
570 {
571 if(pfm >= pfmend - 3) break;
572
573 /* test if white stripe */
574 if( (pfm[0] - opfm[0] > slice_level) &&\
575 (pfm[1] - opfm[1] > slice_level) &&\
576 (pfm[2] - opfm[2] > slice_level) )
577 {
578
579 /* test for out of range pointers due to x_shift */
580 if( (opfm + x_shift >= (uint8_t *)frame_memory1) &&\
581 (opfm + x_shift < opfmend) )
582 {
583 /* replace with data from previous frame */
584 pfm[0] = *(opfm + x_shift);
585 pfm[1] = *(opfm + 1 + x_shift);
586 pfm[2] = *(opfm + 2 + x_shift);
587 } /* end if in range */
588 } /* end if white stripe */
589
590 pfm += 3;
591 opfm += 3;
592
593 } /* end for all x */
594
595 if(pfm >= pfmend - 3) break;
596
597 } /* end for all y */
598
599 /* save the current frame for later */
600 for(i = 0; i < pfl->v_width * pfl->v_height * 3; i++)
601 {
602 frame_memory1[i] = frame_memory0[i];
603 }
604
605 } /* end if de_stripe_flag */
606
607 if\
608 (\
609 (dcontrast != 100.0) ||\
610 (dsaturation != 100.0) ||\
611 (u_shift) ||\
612 (v_shift)\
613 )
614 {
615 /*
616 brightness, contrast, saturation, U zero shift, V zero shift.
617 */
618 ucptrs = ImageData;
619 /* set pointers */
620 py = ImageData;
621 pv = ImageData + image_width * image_height;
622 pu = ImageData + (image_width * image_height * 5) / 4;
623
624 if(vob->im_v_codec == CODEC_RGB)
625 {
626 for(y = 0; y < pfl->v_height; y++)
627 {
628 for(x = 0; x < pfl->v_width * 3; x++)
629 {
630 /* brightness */
631 if( (brightness + *py) > 255) *py = 255;
632 else if ( (brightness + *py) < 0) *py = 0;
633 else *py += brightness;
634
635 /* contrast */
636 da = *py;
637 da *= dcontrast / 100.0;
638 *py = (int)da;
639
640 } /* end for all x */
641
642 } /* end for y */
643 } /* end if color_depth 32 */
644 else if(vob->im_v_codec == CODEC_YUV)
645 {
646 for(y = 0; y < pfl->v_height; y++)
647 {
648 for(x = 0; x < pfl->v_width; x++)
649 {
650 /* brightness */
651 if( (brightness + *py) > 255) *py = 255;
652 else if ( (brightness + *py) < 0) *py = 0;
653 else *py += brightness;
654
655 /* contrast */
656 da = *py;
657 da *= dcontrast / 100.0;
658 *py = (int)da;
659
660 /* saturation */
661 a = (int)*pu - 128;
662 b = (int)*pv - 128;
663
664 a *= dsaturation / 100.0;
665 b *= dsaturation / 100.0;
666
667 *pu = (uint8_t)a + 128;
668 *pv = (uint8_t)b + 128;
669
670 /* u_shift */
671 *pu += u_shift;
672 *pu &= 0xff;
673
674 /* v_shift */
675 *pv += v_shift;
676 *pv &= 255;
677
678 /* increment Y pointer */
679 py++;
680
681 /* increment U and V vector pointers */
682 if(x % 2)
683 {
684 pu++;
685 pv++;
686 }
687 } /* end for all x */
688
689 if( (y + 1) % 2)
690 {
691 pu -= pfl->v_width / 2;
692 pv -= pfl->v_width / 2;
693 }
694
695 } /* end for y */
696 } /* end if buffer is YUV */
697 } /* end if contrast, saturation, u_shift, v_shift */
698
699 if( dhue || dhue_line_drift)
700 {
701 /*
702 UV vector rotation.
703 Dynamic UV vector rotation (NTSC line phase error correction).
704 */
705 if(vob->im_v_codec == CODEC_RGB)
706 {
707 tc_log_error(MOD_NAME, \
708 "hue operations only available in YUV 420");
709 return(-1);
710 } /* end if CODEC_RGB */
711 else if(vob->im_v_codec == CODEC_YUV)
712 {
713 /* set pointers */
714 py = ImageData;
715 pv = ImageData + image_width * image_height;
716 pu = ImageData + (image_width * image_height * 5) / 4;
717
718 for(y = 0; y < pfl->v_height; y++)
719 {
720 for(x = 0; x < pfl->v_width; x++)
721 {
722 /*
723 NTSC color correction at start and end of line
724 Assuming middle to be correct, most users would have
725 adjusted on face color somewhere in the middle.
726 */
727
728 /* the phase drift over one horizontal line */
729 da = (double)x / (double)pfl->v_width; // 0 to 1
730
731 /* go for middle, now -.5 to +.5 */
732 da -= .5;
733
734 /* multiply by specified dynamic correction factor */
735 db = dhue_line_drift * da;
736
737 /* add the static hue correction specified */
738 db += (double)dhue;
739
740 /* hue and saturation*/
741 a = (int)*pu - 128;
742 b = (int)*pv - 128;
743 adjust_color(&a, &b, db, dsaturation);
744 *pu = (uint8_t)a + 128;
745 *pv = (uint8_t)b + 128;
746
747 /* increment Y pointer */
748 py++;
749
750 /* increment U and V vector pointers */
751 if(x % 2)
752 {
753 pu++;
754 pv++;
755 }
756 } /* end for all x */
757
758 /*
759 2 x 2 color pixels on screen for each Y value,
760 repeat each line twice.
761
762 Orientation on screen Y (*) and U V (o)
763 * o
764 o o
765 drop shadow :-) color less area below char looks better.
766 sink a line.
767 */
768 if( (y + 1) % 2)
769 {
770 pu -= pfl->v_width / 2;
771 pv -= pfl->v_width / 2;
772 }
773
774 } /* end for y */
775 } /* end if buffer is YUV */
776
777 } /* end if some sort of hue */
778
779 if(add_objects_flag)
780 {
781 /*
782 collect any objects from database for this frame
783 and add to object list.
784 */
785 process_frame_number(frame_nr);
786
787 /* add objects in object list to display, and update params */
788 add_objects(frame_nr);
789
790 } /* end if add_objects_flag */
791
792 if(write_ppm_flag)
793 {
794 if(vob->im_v_codec == CODEC_RGB)
795 {
796 tc_log_error(MOD_NAME, \
797 "subtitler(): write_ppm only available in YUV 420\n");
798 return(-1);
799 } /* end if CODEC_RGB */
800 else if(vob->im_v_codec == CODEC_YUV)
801 {
802 /* set pointers */
803 py = ImageData;
804 pv = ImageData + image_width * image_height;
805 pu = ImageData + (image_width * image_height * 5) / 4;
806
807 /* open the ppm file for write */
808 tc_snprintf(temp, sizeof(temp), "%s/.subtitles/%d.ppm", home_dir, movie_id);
809 pppm_file = fopen(temp, "w");
810 if(! pppm_file)
811 {
812 tc_log_error(MOD_NAME,
813 "could not open file %s for write, aborting",\
814 temp);
815 return(-1);
816 }
817
818 /* write the ppm header */
819 fprintf(pppm_file,\
820 "P6\n%i %i\n255\n", pfl->v_width, pfl->v_height);
821
822 for(y = 0; y < pfl->v_height; y++)
823 {
824 /* get a line from buffer start, to file in RGB */
825 for(x = 0; x < pfl->v_width; x++)
826 {
827 cy = ( (0xff & *py) - 16);
828 cy *= 76310;
829
830 cu = (0xff & *pu) - 128;
831 cv = (0xff & *pv) - 128;
832
833 cr = 104635 * cv;
834 cg = -25690 * cu + -53294 * cv;
835 cb = 132278 * cu;
836
837 fprintf(pppm_file, "%c%c%c",\
838 LIMIT(cr + cy), LIMIT(cg + cy), LIMIT(cb + cy) );
839
840 /* increment Y pointer */
841 py++;
842
843 /* increment U and V vector pointers */
844 if(x % 2)
845 {
846 pu++;
847 pv++;
848 }
849 } /* end for all x */
850
851 if( (y + 1) % 2)
852 {
853 pu -= pfl->v_width / 2;
854 pv -= pfl->v_width / 2;
855 }
856
857 } /* end for y (all lines) */
858 } /* end if buffer is YUV */
859 fclose(pppm_file);
860
861 /* set the semaphore indicating the .ppm file is ready */
862 tc_snprintf(temp, sizeof(temp), "touch %s/.subtitles/%d.sem", home_dir, movie_id);
863 execute(temp);
864
865 /* now wait for the semaphore to be removed, by calling */
866 tc_snprintf(temp, sizeof(temp), "%s/.subtitles/%d.sem", home_dir, movie_id);
867 while(1)
868 {
869 fptr = fopen(temp, "r");
870 if(! fptr) break;
871
872 fclose(fptr);
873
874 /* reduce processor load */
875 usleep(10000); // 10 ms
876 } /* end while wait for handshake */
877
878 } /* end if write_ppm_flag */
879
880 if(show_output_flag)
881 {
882 /* create an xwindows display */
883 if(! window_open_flag)
884 {
885 if(debug_flag)
886 {
887 tc_log_info(MOD_NAME, "opening window");
888 }
889
890 // openwin(argc, argv, width, height);
891 openwin(0, NULL, pfl->v_width, pfl->v_height);
892
893 window_size = pfl->v_width * pfl->v_height;
894 window_open_flag = 1;
895
896 if(color_depth == 0) color_depth = get_x11_bpp();
897
898 } /* end if ! window_open_flag */
899 else /* have window */
900 {
901 if( (pfl->v_width * pfl->v_height) != window_size)
902 {
903 /* close window and open a new one */
904 // closewin(); //crashes
905 // resize_window(xsize, ysize); // problem ?
906 // no problem, now we have 2 windows, use window manager to kill one
907
908 // openwin(argc, argv, xsize, ysize);
909 openwin(0, NULL, pfl->v_width, pfl->v_height);
910
911 window_size = pfl->v_width * pfl->v_height;
912 } /* end if different window size */
913
914 /* get X11 buffer */
915 ucptrd = (unsigned char *)getbuf();
916
917 /* copy data to X11 buffer */
918 ucptrs = ImageData;
919
920 if(vob->im_v_codec == CODEC_RGB)
921 {
922 /* need vertical flip, but not horizontal flip */
923 if(color_depth == 32)
924 {
925 /* ucptrs points to start buffer, ucptrd to X buffer */
926 ucptrd += (window_size - pfl->v_width) * 4;
927 for(y = 0; y < pfl->v_height; y++)
928 {
929 /*
930 get a line from buffer start, copy to xbuffer end
931 */
932 for(x = 0; x < pfl->v_width; x++)
933 {
934 *ucptrd++ = *ucptrs++;
935 *ucptrd++ = *ucptrs++;
936 *ucptrd++ = *ucptrs++;
937
938 ucptrd++; /* nothing in byte 4 */
939 }
940
941 /* move back a line, so we V flip */
942 ucptrd -= pfl->v_width * 8;
943 } /* end for y (all lines) */
944 } /* end if color_depth 32 */
945 else if(color_depth == 24) // NOT TESTED!!!!!!!!
946 {
947 /* ucptrs points to start buffer, ucptrd to X buffer */
948 ucptrd += (window_size - pfl->v_width) * 3;
949 for(y = 0; y < pfl->v_height; y++)
950 {
951 /*
952 get a line from buffer start, copy to xbuffer end
953 */
954 for(x = 0; x < pfl->v_width; x++)
955 {
956 *ucptrd++ = *ucptrs++;
957 *ucptrd++ = *ucptrs++;
958 *ucptrd++ = *ucptrs++;
959 }
960
961 /* move back a line, so we V flip */
962 ucptrd -= pfl->v_width * 6;
963 } /* end for y (all lines) */
964 } /* end if color_depth 32 */
965 } /* end if buffer is RGB */
966 else if(vob->im_v_codec == CODEC_YUV)
967 {
968 /* set pointers */
969 py = ImageData;
970 pv = ImageData + image_width * image_height;
971 pu = ImageData + (image_width * image_height * 5) / 4;
972 /* ucptrd is pointer to xbuffer BGR */
973
974 for(y = 0; y < pfl->v_height; y++)
975 {
976 /* get a line from buffer start, copy to xbuffer BGR */
977 for(x = 0; x < pfl->v_width; x++)
978 {
979 cy = ( (0xff & *py) - 16);
980 cy *= 76310;
981
982 cu = (0xff & *pu) - 128;
983 cv = (0xff & *pv) - 128;
984
985 cr = 104635 * cv;
986 cg = -25690 * cu + -53294 * cv;
987 cb = 132278 * cu;
988
989 if(color_depth == 32) // 4 bytes per pixel
990 {
991 *ucptrd++ = LIMIT(cb + cy); // B
992 *ucptrd++ = LIMIT(cg + cy); // G
993 *ucptrd++ = LIMIT(cr + cy); // R
994
995 /* one more byte */
996 *ucptrd++ = 0; // last byte is empty.
997 } /* end if color depth 32 */
998
999 /* 24 bpp not tested */
1000 else if(color_depth == 24) // 3 bytes per pixel
1001 {
1002 *ucptrd++ = LIMIT(cb + cy); // B
1003 *ucptrd++ = LIMIT(cg + cy); // G
1004 *ucptrd++ = LIMIT(cr + cy); // R
1005 }
1006
1007 /* increment Y pointer */
1008 py++;
1009
1010 /* increment U and V vector pointers */
1011 if(x % 2)
1012 {
1013 pu++;
1014 pv++;
1015 }
1016 } /* end for all x */
1017
1018 /*
1019 2 x 2 color pixels on screen for each Y value,
1020 repeat each line twice.
1021
1022 Orientation on screen Y (*) and U V (o)
1023 * o
1024 o o
1025 drop shadow :-) color less area below char looks better.
1026 sink a line.
1027 */
1028 if( (y + 1) % 2)
1029 {
1030 pu -= pfl->v_width / 2;
1031 pv -= pfl->v_width / 2;
1032 }
1033
1034 } /* end for y (all lines) */
1035 } /* end if buffer is YUV */
1036
1037 /* show X11 buffer */
1038 putimage(pfl->v_width, pfl->v_height);
1039 } /* end if window_open_flag */
1040
1041 } /* end if show_output_flag */
1042
1043 } /* end if TC_VIDEO && TC_POST_M_PROCESS */
1044
1045 /* return OK */
1046 return 0;
1047 } /* end function tc_filter */
1048
1049
add_text(int x,int y,char * text,struct object * pa,int u,int v,double contrast,double transparency,font_desc_t * pfd,int espace)1050 int add_text(\
1051 int x, int y,\
1052 char *text,
1053 struct object *pa,\
1054 int u, int v,\
1055 double contrast, double transparency, font_desc_t *pfd, int espace)
1056 {
1057 int a;
1058 char *ptr;
1059
1060 if(debug_flag)
1061 {
1062 tc_log_info(MOD_NAME, "add_text(): x=%d y=%d text=%s \
1063 pa=%p u=%d v=%d contrast=%.2f transparency=%.2f \
1064 font_desc_t=%lu espace=%d",\
1065 x, y, (const char *)pa, text, u, v, contrast, transparency, (unsigned long)pfd, espace);
1066 }
1067
1068 ptr = text;
1069 while(*ptr)
1070 {
1071 /* convert to signed */
1072 a = *ptr;
1073 if(*ptr < 0) a += 256;
1074
1075 if(a == ' ')
1076 {
1077 /* want to print background only here, not '_' */
1078 draw_char(x, y, a, pa, u, v, contrast, transparency, pfd, 1);
1079 }
1080 else
1081 {
1082 draw_char(x, y, a, pa, u, v, contrast, transparency, pfd, 0);
1083 }
1084
1085 x += pfd->width[a] + pfd->charspace;
1086
1087
1088 x += espace; //extra_character_space;
1089 ptr++;
1090 }
1091
1092 return 1;
1093 } /* end function add_text */
1094
1095
draw_char(int x,int y,int c,struct object * pa,int u,int v,double contrast,double transparency,font_desc_t * pfd,int is_space)1096 int draw_char(\
1097 int x, int y, int c,\
1098 struct object *pa,\
1099 int u, int v,\
1100 double contrast, double transparency, font_desc_t *pfd, int is_space)
1101 {
1102 if(debug_flag)
1103 {
1104 tc_log_info(MOD_NAME, "draw_char(): arg \
1105 x=%d y=%d c=%d pa=%p u=%d v=%d contrast=%.2f transparency=%.2f \
1106 pfd=%lu is_space=%d",\
1107 x, y, c, pa, u, v, contrast, transparency, (unsigned long)pfd, is_space);
1108 }
1109
1110 draw_alpha(\
1111 x,\
1112 y,\
1113 pa,\
1114 pfd->width[c],\
1115 pfd->pic_a[pa -> font_symbols]->h,\
1116 pfd->pic_b[pa -> font_symbols]->bmp + pfd->start[c],\
1117 pfd->pic_a[pa -> font_symbols]->bmp + pfd->start[c],\
1118 pfd->pic_a[pa -> font_symbols]->w,\
1119 u, v, contrast, transparency, is_space);
1120
1121 return 1;
1122 } /* end function draw_char */
1123
1124
draw_alpha(int x0,int y0,struct object * pa,int w,int h,uint8_t * src,uint8_t * srca,int stride,int u,int v,double contrast,double transparency,int is_space)1125 void draw_alpha(\
1126 int x0, int y0,\
1127 struct object *pa,\
1128 int w, int h,\
1129 uint8_t *src, uint8_t *srca, int stride,\
1130 int u, int v, double contrast, double transparency, int is_space)
1131 {
1132 int a, b, c, x, y, sx, cd;
1133 uint8_t *py, *pu, *pv;
1134 uint8_t *sc, *sa;
1135 double dmto = 0, dmti = 0;
1136 uint8_t uy, ur, ug, ub, ua, uc;
1137 int iu, iv;
1138 int iy;
1139 unsigned char *dst;
1140 double dir, dig, dib, dor, dog, dob;
1141 double diy, diu, div, doy, dou, dov;
1142 double da, db;
1143 double opaqueness_p, opaqueness_e1, opaqueness_e2;
1144 double dmci;
1145 double dmti_p = 0, dmti_e1 = 0, dmti_e2 = 0;
1146 double dmto_p = 0, dmto_e1 = 0, dmto_e2 = 0;
1147 double dy, dblur;
1148 double dmulto, dmulti;
1149
1150
1151 if(debug_flag)
1152 {
1153 tc_log_info(MOD_NAME, \
1154 "draw_alpha(): x0=%d y0=%d pa=%p w=%d h=%d \
1155 src=%lu srca=%lu stride=%d u=%d v=%d \
1156 contrast=%.2f transparency=%.2f is_space=%d",\
1157 x0, y0, pa, w, h,\
1158 (unsigned long)src, (unsigned long)srca, stride, u, v,\
1159 contrast, transparency, is_space);
1160
1161 tc_log_info(MOD_NAME, "vob->im_v_codec=%d", vob -> im_v_codec);
1162 tc_log_info(MOD_NAME, "image_width=%d image_height=%d", image_width, image_height);
1163 tc_log_info(MOD_NAME, "ImageData=%lu", (unsigned long)ImageData);
1164 }
1165
1166 /* all */
1167 db = (1.0 - (double)pa -> transparency / 100.0);
1168 dmci = (pa -> contrast / 100.0);
1169
1170 if(rgb_palette_valid_flag)
1171 {
1172 /* pattern */
1173 /* calculate 'visibility' insert */
1174 da = (double) pa -> pattern_contrast / 15.0;
1175 opaqueness_p = da * db;
1176
1177 /* combine subtitler and DVD transparency */
1178 dmto_p = 1.0 - opaqueness_p;
1179 dmti_p = 1.0 - dmto_p;
1180
1181 dmti_p *= dmci;
1182
1183
1184 /* emphasis1 */
1185 /* calculate 'visibility' insert */
1186 da = (double) pa -> emphasis1_contrast / 15.0;
1187 opaqueness_e1 = da * db;
1188
1189 /* combine subtitler and DVD transparency */
1190 dmto_e1 = 1.0 - opaqueness_e1;
1191 dmti_e1 = 1.0 - dmto_e1;
1192
1193 dmti_e1 *= dmci;
1194
1195
1196 /* emphasis2 */
1197 /* calculate 'visibility' insert */
1198 da = (double) pa -> emphasis2_contrast / 15.0;
1199 opaqueness_e2 = da * db;
1200
1201 /* combine subtitler and DVD transparency */
1202 dmto_e2 = 1.0 - opaqueness_e2;
1203 dmti_e2 = 1.0 - dmto_e2;
1204
1205 dmti_e2 *= dmci;
1206
1207 }
1208 else
1209 {
1210 /* calculate multiplier for transparency ouside loops */
1211 dmti = db;
1212 dmto = 1.0 - dmti;
1213
1214 dmti *= dmci;
1215 }
1216
1217 sc = src;
1218 sa = srca;
1219
1220 if(vob->im_v_codec == CODEC_RGB)
1221 {
1222 a = 3 * (image_height * image_width); // size of a picture
1223
1224 for(y = 0; y < h; y++)
1225 {
1226 b = 3 * ( (y + y0) * image_width);
1227
1228 for(x = 0; x < w; x++)
1229 {
1230 c = 3 * (image_width - (x + x0) );
1231
1232 dst = ImageData + a - (b + c);
1233
1234 /* clip right scroll */
1235 if( (x + x0) > image_width - 1) continue;
1236
1237 /* clip left scroll */
1238 if( (x + x0 ) < 0) continue;
1239
1240 /* clip top scroll */
1241 if( (y + y0) > image_height - 1) continue;
1242
1243 /* clip bottom scroll */
1244 if( (y + y0) < 0) continue;
1245
1246 if(! rgb_palette_valid_flag)
1247 {
1248 if(sa[x] && !is_space)
1249 {
1250
1251 /* get original */
1252 dob = (double) dst[0];
1253 dog = (double) dst[1];
1254 dor = (double) dst[2];
1255
1256 /* get insert (character) original is BW */
1257 diy = (double) (sa[x] >> 8) + sc[x];
1258
1259 /* transparency */
1260 diy *= dmti;
1261
1262 dob *= dmto;
1263 dog *= dmto;
1264 dor *= dmto;
1265
1266 if(sa[x])
1267 {
1268 dst[0] = (int) (dob + diy);
1269 dst[1] = (int) (dog + diy);
1270 dst[2] = (int) (dor + diy);
1271 }
1272 else /* border */
1273 {
1274 dst[0] = (int) (dob);
1275 dst[1] = (int) (dog);
1276 dst[2] = (int) (dor);
1277 }
1278
1279 }
1280 } /* end if ! rgb_palette_valid_flag */
1281 else /* DVD like subs */
1282 {
1283 /* some temp vars */
1284 ub = dst[0];
1285 ug = dst[1];
1286 ur = dst[2];
1287
1288 ua = sa[x];
1289 uc = sc[x];
1290
1291 /* get original */
1292 dob = (double)dst[0];
1293 dog = (double)dst[1];
1294 dor = (double)dst[2];
1295
1296 /* blur factor y * sa[x] */
1297 dy = .3* dor + .59 * dog + .11 * dob;
1298 dblur = (double) ( ((int)dy * ua) >> 8) + uc;
1299 dblur /= 255.0;
1300
1301 if(sa[x] && !is_space)
1302 {
1303 if(sc[x] > 5)
1304 {
1305 dir = (double)rgb_palette[pa -> pattern][0];
1306 dig = (double)rgb_palette[pa -> pattern][1];
1307 dib = (double)rgb_palette[pa -> pattern][2];
1308
1309 dir *= dblur;
1310 dig *= dblur;
1311 dib *= dblur;
1312
1313 /* transparency */
1314 dir *= dmti_p;
1315 dig *= dmti_p;
1316 dib *= dmti_p;
1317
1318 dor *= dmto_p;
1319 dog *= dmto_p;
1320 dob *= dmto_p;
1321
1322 }
1323 else /* emphasis1 */
1324 {
1325 dir = (double)rgb_palette[pa -> emphasis1][0];
1326 dig = (double)rgb_palette[pa -> emphasis1][1];
1327 dib = (double)rgb_palette[pa -> emphasis1][2];
1328
1329 /* transparency */
1330 dir *= dmti_e1;
1331 dig *= dmti_e1;
1332 dib *= dmti_e1;
1333
1334 dor *= dmto_e1;
1335 dog *= dmto_e1;
1336 dob *= dmto_e1;
1337
1338 }
1339 } /* end if sc[x] */
1340 else /* emphasis2 */
1341 {
1342 /* get new part */
1343 dir = (double)rgb_palette[pa -> emphasis2][0];
1344 dig = (double)rgb_palette[pa -> emphasis2][1];
1345 dib = (double)rgb_palette[pa -> emphasis2][2];
1346
1347 /* transparency */
1348 dir *= dmti_e2;
1349 dig *= dmti_e2;
1350 dib *= dmti_e2;
1351
1352 dor *= dmto_e2;
1353 dog *= dmto_e2;
1354 dob *= dmto_e2;
1355 }
1356
1357 /* combine old and new parts in output */
1358 dst[0] = (int) (dob + dib);
1359 dst[1] = (int) (dog + dig);
1360 dst[2] = (int) (dor + dir);
1361 } /* end if DVD like subs */
1362
1363 } /* end for all x */
1364
1365 sc += stride;
1366 sa += stride;
1367
1368 } /* end for all y */
1369
1370 } /* end if RGB */
1371 else if(vob->im_v_codec == CODEC_YUV)
1372 {
1373 /*
1374 We seem to be in this format I420:
1375 y = dest;
1376 v = dest + width * height;
1377 u = dest + width * height * 5 / 4;
1378
1379 Orientation of Y (*) relative to chroma U and V (o)
1380 * o
1381 o o
1382 So, an array of 2x2 chroma pixels exists for each luminance pixel
1383 The consequence of this is that there will be a color-less area
1384 of one line on the right and on the bottom of each character.
1385 Dropshadow :-)
1386 */
1387
1388 py = ImageData;
1389 pv = ImageData + image_width * image_height;
1390 pu = ImageData + (image_width * image_height * 5) / 4;
1391
1392 a = y0 * image_width;
1393 b = image_width / 4;
1394 c = image_width / 2;
1395
1396 py += x0 + a;
1397 a /= 4;
1398
1399 pu += (x0 / 2) + a;
1400 pv += (x0 / 2) + a;
1401
1402 /* on odd lines, need to go a quarter of a 'line' back */
1403 if(y0 % 2)
1404 {
1405 pu -= b;
1406 pv -= b;
1407 }
1408
1409 for(y = 0; y < h; y++)
1410 {
1411 for(x = 0; x < w; x++)
1412 {
1413
1414 /* clip right scroll */
1415 if( (x + x0) > image_width - 1) continue;
1416
1417 /* clip left scroll */
1418 if( (x + x0 ) < 0) continue;
1419
1420 /* clip top scroll */
1421 if( (y + y0) > image_height - 1) continue;
1422
1423 /* clip bottom scroll */
1424 if( (y + y0) < 0) continue;
1425
1426 if(! rgb_palette_valid_flag)
1427 {
1428 if(sa[x] && !is_space)
1429 {
1430 /* trailing shadow no */
1431 sx = 1;
1432 if( (x + x0) % 2) sx = 0;
1433
1434 // if(x < (w - 4) ) sx = 1; // hack, looks better :-)
1435 // else sx = 0;
1436
1437 /* some temp vars */
1438 uy = py[x];
1439 ua = sa[x];
1440 uc = sc[x];
1441
1442 /* get decision factor before we change anything */
1443 // cd = ( (py[x] * sa[x]) >> 8) < 5;
1444 cd = ( (uy * ua) >> 8) < 5;
1445
1446 /* get original */
1447 doy = (double) py[x];
1448 dou = (double) (pu[x / 2 + sx] - 128);
1449 dov = (double) (pv[x / 2 + sx] - 128);
1450
1451 /* blur factor y * sa[x] */
1452 dblur = (double) ( ((int)doy * ua) >> 8) + uc;
1453 dblur /= 255.0;
1454
1455 /* calculate value insert (character) */
1456 diy = (double) ( (uy * ua) >> 8) + uc;
1457
1458 /* transparency */
1459 diy *= dmti;
1460 doy *= dmto;
1461
1462 /* add what is left of the insert (character) */
1463 py[x] = (int) (doy + diy);
1464
1465 if(cd)
1466 {
1467 diu *= dblur;
1468 div *= dblur;
1469
1470 /* change color too */
1471 diu = u * dmti;
1472 div = v * dmti;
1473
1474 dou *= dmto;
1475 dov *= dmto;
1476
1477 if(sc[x]) /* white part of char */
1478 {
1479 /* set U vector */
1480 pu[x / 2 + sx] = (int) (128.0 + dou + diu);
1481
1482 /* set V vector */
1483 pv[x / 2 + sx] = (int) (128.0 + dov + div);
1484 }
1485 else /* keep border around character colorless */
1486 {
1487 /* set U vector */
1488 pu[x / 2 + sx] = (int) (128.0 + dou); // + diu);
1489
1490 /* set V vector */
1491 pv[x / 2 + sx] = (int) (128.0 + dov); // + div);
1492 }
1493
1494 } /* end if sa[x] */
1495 } /* end if cd */
1496
1497 } /* end if ! rgb_palette_valid_flag */
1498 else
1499 {
1500 /*
1501 OK lets get this straight:
1502 I do not understand character anti-aliasing, but looked at the variables with
1503 printf().
1504 Here I see that:
1505 sa[x] > 0 for character space, it varies from 255 to 0
1506 sc[x] > 0 for character body (pattern), it varies from 0 to 255;
1507
1508 sa[x] seems to be the aliasing or blur? multiplier, and is used to attenuate
1509 the original! Not my idea of anti alias, but OK.
1510 sc[x] is the multiplier for the insert (character).
1511
1512 sa[x] is 1 within the body of a character.
1513 sa[x] increases as you go from center character outwards (fade in background)
1514 sa[x] is zero outside the character space.
1515
1516
1517 so:
1518 sa[x] = 0 is not in character space.
1519 sa[x] = 1 is character body
1520 sa[x] > 1 is outline1
1521
1522
1523 Variable names used:
1524 dxxxxo for double (format) referring to original data
1525 dxxxxi for double (format) referring to insert data
1526
1527 */
1528
1529 /*
1530 test for in character space, and something to print
1531 These character sets seem to have '_' in place of a real space, so that is why is_space,
1532 to prevent a '_' from appearing where a space is intended.
1533 */
1534
1535 /* some color trick */
1536 sx = 1;
1537 if( (x + x0) % 2) sx = 0;
1538
1539 /* trailing shadow no */
1540 // if(x < (w - 4) ) sx = 1; // hack, looks better :-)
1541 // else sx = 0;
1542
1543 if(sa[x] && !is_space)
1544 {
1545
1546 /* get multiplier as double */
1547 dmulto = (double) sa[x] / 256.0;
1548
1549 /* multiplier to range 0 to 1.0 */
1550 dmulti = (double) sc[x] / 256.0;
1551
1552 if(use_emphasis2_for_anti_aliasing_flag)
1553 {
1554 /* multiplier to range 0 to 1.0 */
1555 dmulti = (double) sc[x] / 256.0;
1556 }
1557 else
1558 {
1559 if(sc[x]) dmulti = 1.0;
1560 else dmulti = 0.0;
1561 }
1562
1563 /* test for character body */
1564 if(dmulti > .5)
1565 {
1566 /* get original */
1567 doy = (double) py[x];
1568 dou = (double) pu[x / 2 + sx] - 128;
1569 dov = (double) pv[x / 2 + sx] - 128;
1570
1571 rgb_to_yuv(\
1572 rgb_palette[pa -> pattern][0],\
1573 rgb_palette[pa -> pattern][1],\
1574 rgb_palette[pa -> pattern][2],\
1575 &iy, &iu, &iv);
1576
1577 /*
1578 better to multiply AFTER rgb_to_uuv(),
1579 as strange values may happen for u and v if low values of rgb.
1580 */
1581
1582 /* NOTE: what we call 'contrast' in DVD is actually transparency */
1583
1584 /* insert to double */
1585 diy = (double) iy;
1586 diu = (double) iu;
1587 div = (double) iv;
1588
1589 /* transparency */
1590 diy *= dmti_p;
1591 /* u and v here for color change */
1592 diu *= dmti_p;
1593 div *= dmti_p;
1594
1595 doy *= dmto_p;
1596 dou *= dmto_p;
1597 dov *= dmto_p;
1598
1599 da = (doy * dmulto) + (diy * dmulti);
1600 py[x] = (int) da;
1601
1602 /* set U vector */
1603 da = (dou * dmulto) + (diu * dmulti);
1604 pu[x / 2 + sx] = 128 + (int)da;
1605
1606 /* set V vector */
1607 da = (dov * dmulto) + (div * dmulti);
1608 pv[x / 2 + sx] = 128 + (int)da;
1609 } /* end if body */
1610 else
1611 {
1612 if(use_emphasis2_for_anti_aliasing_flag)
1613 {
1614 /* use outline2 for anti aliasing, set to grey 50 % */
1615 if( (dmulti > 0) && (dmulti < .5) )
1616 {
1617 /* get original */
1618 doy = (double) py[x];
1619 dou = (double) pu[x / 2 + sx] - 128;
1620 dov = (double) pv[x / 2 + sx] - 128;
1621
1622 /* draw outline2 */
1623 rgb_to_yuv(\
1624 rgb_palette[pa -> emphasis2][0],\
1625 rgb_palette[pa -> emphasis2][1],\
1626 rgb_palette[pa -> emphasis2][2],\
1627 &iy, &iu, &iv);
1628
1629 /* insert to double */
1630 diy = (double) iy;
1631 diu = (double) iu;
1632 div = (double) iv;
1633
1634 /* transparency */
1635 diy *= dmti_e2;
1636 diu *= dmti_e2;
1637 div *= dmti_e2;
1638
1639 doy *= dmto_e2;
1640 dou *= dmto_e2;
1641 dov *= dmto_e2;
1642
1643 /* add what is left of the insert (character) */
1644 py[x] = (int) (doy + diy);
1645
1646 /* set U vector */
1647 pu[x / 2 + sx] = 128 + (int) (dou + diu);
1648
1649 /* set V vector */
1650 pv[x / 2 + sx] = 128 + (int) (dov + div);
1651 } /* end emphasis2 for anti-aliasing */
1652 else
1653 {
1654 /* get original */
1655 doy = (double) py[x];
1656 dou = (double) pu[x / 2 + sx] - 128;
1657 dov = (double) pv[x / 2 + sx] - 128;
1658
1659 rgb_to_yuv(\
1660 rgb_palette[pa -> emphasis1][0],\
1661 rgb_palette[pa -> emphasis1][1],\
1662 rgb_palette[pa -> emphasis1][2],\
1663 &iy, &iu, &iv);
1664
1665 /* insert to double */
1666 diy = (double) iy;
1667 diu = (double) iu;
1668 div = (double) iv;
1669
1670 /* transparency */
1671 diy *= dmti_e1;
1672 diu *= dmti_e1;
1673 div *= dmti_e1;
1674
1675 doy *= dmto_e1;
1676 dou *= dmto_e1;
1677 dov *= dmto_e1;
1678
1679 da = doy + diy;
1680 py[x] = (int) da;
1681
1682 /* set U vector */
1683 da = dou + diu;
1684 pu[x / 2 + sx] = 128 + (int)da;
1685
1686 /* set V vector */
1687 da = dov + div;
1688 pv[x / 2 + sx] = 128 + (int)da;
1689 } /* end emphasis1 */
1690 } /* end if use_emphasis2_for_anti_aliasing_flag */
1691 else /* outline1 */
1692 {
1693 /* get original */
1694 doy = (double) py[x];
1695 dou = (double) pu[x / 2 + sx] - 128;
1696 dov = (double) pv[x / 2 + sx] - 128;
1697
1698 rgb_to_yuv(\
1699 rgb_palette[pa -> emphasis1][0],\
1700 rgb_palette[pa -> emphasis1][1],\
1701 rgb_palette[pa -> emphasis1][2],\
1702 &iy, &iu, &iv);
1703
1704 /* insert to double */
1705 diy = (double) iy;
1706 diu = (double) iu;
1707 div = (double) iv;
1708
1709 /* transparency */
1710 diy *= dmti_e1;
1711 diu *= dmti_e1;
1712 div *= dmti_e1;
1713
1714 doy *= dmto_e1;
1715 dou *= dmto_e1;
1716 dov *= dmto_e1;
1717
1718 da = doy + diy;
1719 py[x] = (int) da;
1720
1721 /* set U vector */
1722 da = dou + diu;
1723 pu[x / 2 + sx] = 128 + (int)da;
1724
1725 /* set V vector */
1726 da = dov + div;
1727 pv[x / 2 + sx] = 128 + (int)da;
1728 } /* end no anti-alias */
1729 } /* end dmulti < 0.5 */
1730 } /* end if sa[x] && ! is_space */
1731 else /* outline2 */
1732 {
1733 if(! use_emphasis2_for_anti_aliasing_flag ) /* use emphasis2 for character space */
1734 {
1735 /* get original */
1736 doy = (double) py[x];
1737 dou = (double) pu[x / 2 + sx] - 128;
1738 dov = (double) pv[x / 2 + sx] - 128;
1739
1740 /* draw outline2 */
1741 rgb_to_yuv(\
1742 rgb_palette[pa -> emphasis2][0],\
1743 rgb_palette[pa -> emphasis2][1],\
1744 rgb_palette[pa -> emphasis2][2],\
1745 &iy, &iu, &iv);
1746
1747 /* insert to double */
1748 diy = (double) iy;
1749 diu = (double) iu;
1750 div = (double) iv;
1751
1752 /* transparency */
1753 diy *= dmti_e2;
1754 diu *= dmti_e2;
1755 div *= dmti_e2;
1756
1757 doy *= dmto_e2;
1758 dou *= dmto_e2;
1759 dov *= dmto_e2;
1760
1761 /* add what is left of the insert (character) */
1762 py[x] = (int) (doy + diy);
1763
1764 /* set U vector */
1765 pu[x / 2 + sx] = 128 + (int) (dou + diu);
1766
1767 /* set V vector */
1768 pv[x / 2 + sx] = 128 + (int) (dov + div);
1769 } /* end if outline2 */
1770 } /* end if ! use_emphasis2_for_anti_aliasing_flag */
1771 } /* end if rgb_palette_valid_flag */
1772 } /* end for all x */
1773
1774 sc += stride;
1775 sa += stride;
1776
1777 py += image_width;
1778
1779 if( (y + y0) % 2)
1780 {
1781 pu += c;
1782 pv += c;
1783 }
1784
1785 } /* end for all y */
1786
1787 } /* end if YUV */
1788 } /* end function draw_alpha */
1789
1790
add_background(struct object * pa)1791 int add_background(struct object *pa)
1792 {
1793 int a, b, c, x, y, sx;
1794 uint8_t *py, *pu, *pv;
1795 double da;
1796 int iu, iv;
1797 double dr, dg, db;
1798 int iy;
1799 double dmci, dmti, dmto;
1800 double dir, dig, dib, diy, diu, div;
1801 double dor, dog, dob, doy, dou, dov;
1802 unsigned char *dst;
1803 int width, height;
1804 double opaqueness;
1805
1806 if(debug_flag)
1807 {
1808 tc_log_info(MOD_NAME, "add_background(): arg pa=%p", pa);
1809
1810 tc_log_info(MOD_NAME,\
1811 "pa->line_number=%d pa->bg_y_start=%d pa->bg_y_end=%d pa->bg_x_start=%d pa->bg_x_end=%d",\
1812 pa -> line_number, pa -> bg_y_start, pa -> bg_y_end, pa -> bg_x_start, pa -> bg_x_end);
1813
1814 tc_log_info(MOD_NAME,\
1815 "pa->background=%d pa->background_contrast=%d",\
1816 pa -> background, pa -> background_contrast);
1817
1818 tc_log_info(MOD_NAME,\
1819 "pa->contrast=%.2f, pa->transparency=%.2f",\
1820 pa -> contrast, pa -> transparency);
1821 }
1822
1823 /* only background if palette specified */
1824 if(! rgb_palette_valid_flag) return 1;
1825
1826 /* parameter check */
1827 if(pa -> bg_y_start < 0) return 0;
1828 if(pa -> bg_y_start > image_height - 1) return 0;
1829
1830 if(pa -> bg_x_start < 0) return 0;
1831 if(pa -> bg_x_start > image_width - 1) return 0;
1832
1833 if(pa -> bg_y_end < pa -> bg_y_start) return 0;
1834 if(pa -> bg_y_end > image_height - 1) return 0;
1835
1836 if(pa -> bg_x_end < pa -> bg_x_start) return 0;
1837 if(pa -> bg_x_end > image_width - 1) return 0;
1838
1839 /* calculate 'visibility' insert */
1840 da = (double) pa -> background_contrast / 15.0; // DVD background request, 1.0 for 100 % opaque
1841 db = (1.0 - (double)pa -> transparency / 100.0); // subtitler background request, 1.0 for 100 % opaque
1842 opaqueness = da * db;
1843
1844 /* combine subtitler and DVD transparency */
1845 dmto = 1.0 - opaqueness; // background, 1.0 for 100 % transparent
1846 dmti = 1.0 - dmto; // insert, 0.0 for 100 % transparent
1847
1848 /*
1849 do not multiply color (saturation) with contrast,
1850 saturation could be done in adjust color, but done here for speed
1851 */
1852 dmci = (pa -> contrast / 100.0); // contrast insert, 1.0 for 100 % contrast
1853
1854 dmti *= dmci;
1855
1856 if(vob->im_v_codec == CODEC_RGB)
1857 {
1858 a = 3 * (image_height * image_width); // size of a picture
1859
1860 for(y = pa -> bg_y_start; y < pa -> bg_y_end; y++)
1861 {
1862 b = 3 * (y * image_width);
1863
1864 for(x = pa -> bg_x_start; x < pa -> bg_x_end; x++)
1865 {
1866 c = 3 * (image_width - x);
1867
1868 dst = ImageData + a - (b + c);
1869
1870 /* get original */
1871 dob = (double)dst[0];
1872 dog = (double)dst[1];
1873 dor = (double)dst[2];
1874
1875 /* get new part */
1876 dir = (double)rgb_palette[pa -> background][0];
1877 dig = (double)rgb_palette[pa -> background][1];
1878 dib = (double)rgb_palette[pa -> background][2];
1879
1880 /* transparency */
1881 dir *= dmti;
1882 dig *= dmti;
1883 dib *= dmti;
1884
1885 dor *= dmto;
1886 dog *= dmto;
1887 dob *= dmto;
1888
1889 /* combine old and new parts in output */
1890 dst[0] = (int) (dib + dob);
1891 dst[1] = (int) (dig + dog);
1892 dst[2] = (int) (dir + dor);
1893
1894 } /* end for all x */
1895
1896 } /* end for all y */
1897
1898 } /* end if RGB */
1899 else if(vob->im_v_codec == CODEC_YUV)
1900 {
1901 /*
1902 We seem to be in this format I420:
1903 y = dest;
1904 v = dest + width * height;
1905 u = dest + width * height * 5 / 4;
1906
1907 Orientation of Y (*) relative to chroma U and V (o)
1908 * o
1909 o o
1910 So, an array of 2x2 chroma pixels exists for each luminance pixel
1911 The consequence of this is that there will be a color-less area
1912 of one line on the right and on the bottom of each character.
1913 Dropshadow :-)
1914 */
1915
1916 height = pa -> bg_y_end - pa -> bg_y_start;
1917 width = pa -> bg_x_end - pa -> bg_x_start;
1918
1919 py = ImageData;
1920 pv = ImageData + image_width * image_height;
1921 pu = ImageData + (image_width * image_height * 5) / 4;
1922
1923 a = pa -> bg_y_start * image_width;
1924 b = image_width / 4;
1925 c = image_width / 2;
1926
1927 py += pa -> bg_x_start + a;
1928 a /= 4;
1929
1930 pu += (pa -> bg_x_start / 2) + a;
1931 pv += (pa -> bg_x_start / 2) + a;
1932
1933 /* on odd lines, need to go a quarter of a 'line' back */
1934 if(pa -> bg_y_start % 2)
1935 {
1936 pu -= b;
1937 pv -= b;
1938 }
1939
1940 for(y = 0; y < height; y++)
1941 {
1942 for(x = 0; x < width; x++)
1943 {
1944 sx = 1;
1945 if( (x + pa -> bg_x_start) % 2) sx = 0;
1946
1947 /* get old part */
1948 doy = (double)py[x];
1949 dou = (double)pu[x / 2 + sx] - 128;
1950 dov = (double)pv[x / 2 + sx] - 128;
1951
1952 /* get new part */
1953 dr = (double)rgb_palette[pa -> background][0];
1954 dg = (double)rgb_palette[pa -> background][1];
1955 db = (double)rgb_palette[pa -> background][2];
1956
1957 rgb_to_yuv(dr, dg, db, &iy, &iu, &iv);
1958 /*
1959 better to multiply AFTER rgb_to_uuv(),
1960 as strange values may happen for u and v if low values of rgb.
1961 */
1962
1963 diy = (double)iy;
1964 diu = (double)iu;
1965 div = (double)iv;
1966
1967 /* transparency */
1968 diy *= dmti;
1969 diu *= dmti;
1970 div *= dmti;
1971
1972 doy *= dmto;
1973 dou *= dmto;
1974 dov *= dmto;
1975
1976 /* add what is left of the insert (character) */
1977 py[x] = (int) (doy + diy);
1978
1979 /* set U vector */
1980 pu[x / 2 + sx] = 128 + (int) (dou + diu);
1981
1982 /* set V vector */
1983 pv[x / 2 + sx] = 128 + (int) (dov + div);
1984
1985 } /* end for all x */
1986
1987 py += image_width;
1988
1989 if( (y + pa -> bg_y_start) % 2)
1990 {
1991 pu += c;
1992 pv += c;
1993 }
1994
1995 } /* end for all y */
1996
1997 } /* end if YUV */
1998
1999 return 1;
2000 } /* end function add_background */
2001
2002
print_options(void)2003 int print_options(void)
2004 {
2005 if(debug_flag)
2006 {
2007 tc_log_info(MOD_NAME, "print options(): arg none");
2008 }
2009 /*
2010 From transcode -0.5.1 ChangeLog:
2011 Example: -J my_filter="fonts=3 position=55 -v"
2012 */
2013
2014 tc_log_info(MOD_NAME, "(%s) help\n"
2015 "Usage -J subtitler=\"[no_objects] [subtitle_file=s]\n\
2016 [color_depth=n]\n\
2017 [font_dir=s] [font=n] [font_factor=f\n\
2018 [frame_offset=n]\n\
2019 [debug] [help] [use_pre_processing]\"\n\
2020 \n\
2021 f is float, h is hex, n is integer, s is string.\n\
2022 \n\
2023 no_objects disables subtitles and other objects (off).\n\
2024 color_depth= 32 or 24 (overrides X auto) (32).\n\
2025 font= 0 or 1, 1 gives strange symbols... (0).\n\
2026 font_dir= place where font.desc is (%s).\n\
2027 font_factor= .1 to 100 outline characters (10.75).\n\
2028 frame_offset= positive (text later) or negative (earlier) integer (0).\n\
2029 subtitle_file= pathfilename.ppml location of ppml file (%s).\n\
2030 debug prints debug messages (off).\n\
2031 help prints this list and exits.\n\
2032 use_pre_processing uses pre_processing.\n",
2033 MOD_CAP, default_font_dir, subtitle_file);
2034
2035 return 1;
2036 } /* end function print_options */
2037
2038
add_picture(struct object * pa)2039 int add_picture(struct object *pa)
2040 {
2041 /*
2042 reads yuyv in pa -> data into the YUV 420 ImageData buffer.
2043 */
2044 uint8_t *py, *pu, *pv;
2045 int a, b, c, x, y;
2046 char *ps;
2047 char ca;
2048 int u_time;
2049 int in_range;
2050 double dc, dd, dm, ds;
2051 int ck_flag = 0;
2052 int odd_line;
2053
2054 if(debug_flag)
2055 {
2056 tc_log_info(MOD_NAME, "add_picture(): arg pa=%lu\
2057 pa->xsize=%.2f pa->ysize=%.2f pa->ck_color=%.2f",\
2058 (unsigned long)pa,\
2059 pa -> xsize, pa -> ysize,\
2060 pa -> chroma_key_color);
2061 }
2062
2063 /* argument check */
2064 if(! ImageData) return 0;
2065 if(! pa) return 0;
2066 if( (int)pa -> xsize == 0) return 1;
2067 if( (int)pa -> ysize == 0) return 1;
2068
2069 /* calculate multiplier for transparency ouside loops */
2070 dm = (100.0 - pa -> transparency) / 100.0;
2071 dd = 1.0 - dm;
2072
2073 dc = dm * (pa -> contrast / 100.0);
2074 ds = (pa -> saturation / 100.0);
2075
2076 /* saturation could be done in adjust color, but done here for speed */
2077 //ds = 1.0;
2078
2079 if(vob->im_v_codec == CODEC_RGB)
2080 {
2081 /* ImageData, image_width, image_height */
2082
2083 tc_log_error(MOD_NAME, \
2084 "subtitler ONLY works with YUV 420");
2085
2086 return(-1);
2087 } /* end if RGB */
2088 else if(vob->im_v_codec == CODEC_YUV)
2089 {
2090 b = image_width / 4;
2091 c = image_width / 2;
2092
2093 py = ImageData;
2094 pu = ImageData + (image_width * image_height * 5) / 4;
2095 pv = ImageData + (image_width * image_height);
2096
2097 a = (int)pa -> ypos * image_width;
2098 py += (int)pa -> xpos + a;
2099 a /= 4;
2100 pu += ( (int)pa -> xpos / 2) + a;
2101 pv += ( (int)pa -> xpos / 2) + a;
2102
2103 ps = pa -> data;
2104
2105 if( (int)pa -> ypos % 2 )
2106 {
2107 pu -= b;
2108 pv -= b;
2109 }
2110
2111 // reading sequence is YUYV, so U is first.
2112 u_time = 1;
2113 for(y = 0; y < (int)pa -> ysize; y++)
2114 {
2115 odd_line = (y + (int)pa -> ypos) % 2;
2116
2117 for(x = 0; x < (int)pa -> xsize; x++)
2118 {
2119 /* find out if OK to display */
2120 in_range = 1;
2121 /* clip right scroll */
2122 if( (x + (int)pa -> xpos) > image_width) in_range = 0;
2123
2124 /* clip left scroll */
2125 if( (x + (int)pa -> xpos ) < 0) in_range = 0;
2126
2127 /* clip top scroll */
2128 if( (y + (int)pa -> ypos) > image_height) in_range = 0;
2129
2130 /* clip bottom scroll */
2131 if( (y + (int)pa -> ypos) < 0) in_range = 0;
2132
2133 /* slice level */
2134 a = *ps;
2135 if(a < 0) a += 256;
2136 if( a < ( (int)pa -> slice_level) ) in_range = 0;
2137
2138 if(\
2139 (pa -> zrotation != 0) ||\
2140 (pa -> xshear != 0) || (pa -> yshear != 0)\
2141 )
2142 {
2143 /*
2144 for rotate and shear, the luminance value of the border
2145 to cut away.
2146 Since this would remove picture data, for this not to
2147 happen, we add 1 step to the luminance if it happens to
2148 be the same as border_luminanc in yuv_to_ppm().
2149 With this trick it is guaranteed border_luminance never happens
2150 in the .ppm file that mogrify processes.
2151 */
2152 if(pa -> mask_level)
2153 {
2154 if(a == pa -> mask_level) in_range = 0;
2155 }
2156 else
2157 {
2158 if(a == default_border_luminance) in_range = 0;
2159 }
2160 } /* end if rotate or shear */
2161
2162 /* test for chroma key match if color specified */
2163 if(pa -> chroma_key_saturation)
2164 {
2165 if(u_time)
2166 {
2167 if(! odd_line)
2168 {
2169 a = (int)pu[x / 2] - 128;
2170 b = (int)pv[x / 2] - 128;
2171 ck_flag =\
2172 chroma_key(\
2173 a, b,\
2174 pa -> chroma_key_color,\
2175 pa -> chroma_key_window,\
2176 pa -> chroma_key_saturation);
2177 } /* end if even line */
2178 else
2179 {
2180 a = (int)pu[(x / 2) + c] - 128;
2181 b = (int)pv[(x / 2) + c] - 128;
2182 ck_flag =\
2183 chroma_key(\
2184 a, b,\
2185 pa -> chroma_key_color,\
2186 pa -> chroma_key_window,\
2187 pa -> chroma_key_saturation);
2188 } /* end if odd line */
2189 } /* end if u_time */
2190
2191 /* transport to next time here ! */
2192 if(! ck_flag) in_range = 0;
2193 } /* end if chroma key */
2194
2195 if(in_range)
2196 {
2197 py[x] *= dd;
2198 py[x] += dc * (uint8_t)*ps;
2199 } /* end if in_range */
2200
2201 ps++;
2202
2203 if(in_range)
2204 {
2205 if(u_time)
2206 {
2207 ca = *ps;
2208 ca = 128 + ( ( (uint8_t)*ps - 128 ) * ds);
2209
2210 pu[x / 2] *= dd;
2211 pu[x / 2] += dm * (uint8_t)ca;
2212 }
2213 else
2214 {
2215 ca = *ps;
2216 ca = 128 + ( ( (uint8_t)*ps - 128 ) * ds);
2217
2218 pv[x / 2] *= dd;
2219 pv[x / 2] += dm * (uint8_t)ca;
2220 }
2221
2222 /* apply hue correction if both U and V set */
2223
2224 // if(! u_time)
2225 {
2226 if(pa -> hue)
2227 {
2228 /*
2229 hue,
2230 saturation done outside adjust_color() for speed
2231 */
2232
2233 a = (int)pu[x / 2] - 128;
2234 b = (int)pv[x / 2] - 128;
2235 adjust_color(&a, &b, pa -> hue, 100.0);
2236 pu[x / 2] = (uint8_t)a + 128;
2237 pv[x / 2] = (uint8_t)b + 128;
2238
2239 } /* end if hue */
2240
2241 } /* end if ! u_time */
2242 } /* end if in range */
2243
2244 ps++;
2245 u_time = 1 - u_time;
2246
2247 } /* end for all x */
2248
2249 if( (int) pa -> xsize % 2) u_time = 1 - u_time;
2250
2251 py += image_width;
2252
2253 if(odd_line)
2254 {
2255 pu += c;
2256 pv += c;
2257 }
2258
2259 } /* end for all y */
2260
2261 } /* end if YUV 420 */
2262
2263 return 1;
2264 }/* end function add_picture */
2265
2266
set_main_movie_properties(struct object * pa)2267 int set_main_movie_properties(struct object *pa)
2268 {
2269 if(debug_flag)
2270 {
2271 tc_log_info(MOD_NAME, "set_main_movie_properties(): arg pa=%lu", (unsigned long)pa);
2272 }
2273
2274 if(! pa) return 0;
2275
2276 dcontrast = pa -> contrast;
2277 brightness = (int)pa -> brightness;
2278 dsaturation = pa -> saturation;
2279 dhue = pa -> hue;
2280 dhue_line_drift = pa -> hue_line_drift;
2281 u_shift = (int)pa -> u_shift;
2282 v_shift = (int)pa -> v_shift;
2283 de_stripe_flag = (int)pa -> de_stripe;
2284 show_output_flag = (int)pa -> show_output;
2285
2286 return 1;
2287 } /* end function set_main_movie_properties */
2288
2289
rgb_to_yuv(int r,int g,int b,int * y,int * u,int * v)2290 int rgb_to_yuv(int r, int g, int b, int *y, int *u, int *v)
2291 {
2292 double dr, dg, db, dy, du, dv;
2293
2294 if(debug_flag)
2295 {
2296 tc_log_info(MOD_NAME, "rgb_to_yuv(): arg r=%d g=%d b=%d", r, g, b);
2297 }
2298
2299 dr = (double) r;
2300 dg = (double) g;
2301 db = (double) b;
2302
2303 /* acy, acu, acv pre-calculated in init */
2304 /* convert to YUV */
2305
2306 /* test yuv coding here */
2307 dy = acr * dr + acg * dg + acb * db;
2308
2309 dy = (219.0 / 256.0) * dy + 16.5; /* nominal range: 16..235 */
2310
2311 du = acu * (db - dy);
2312 du = (224.0 / 256.0) * du; // + 128.5; /* 16..240 */
2313
2314 dv = acv * (dr - dy);
2315 dv = (224.0 / 256.0) * dv; // + 128.5; /* 16..240 */
2316
2317 *y = (int) dy;
2318 *u = (int) du;
2319 *v = (int) dv;
2320
2321 return 1;
2322 } /* end function rgb_to_yuv */
2323
2324