@@ -484,14 +484,156 @@ void write_control_code(const int fd, const unsigned char channel, const enum co
484484 * @param row 0-14 (inclusive)
485485 * @param column 0-31 (inclusive)
486486 *
487- * //TODO: Preamble code need to take into account font as well
488- *
487+ * Returns an indent-based preamble code (positions cursor at column with white color)
489488 */
490489enum control_code get_preamble_code (const unsigned char row , const unsigned char column )
491490{
492491 return PREAMBLE_CC_START + 1 + (row * 8 ) + (column / 4 );
493492}
494493
494+ /**
495+ * Get byte2 value for a styled PAC (color/font at column 0)
496+ * Returns 0x40-0x4F or 0x60-0x6F depending on the style
497+ *
498+ * @param color The color to use
499+ * @param font The font style to use
500+ * @param use_high_range If true, use 0x60-0x6F range instead of 0x40-0x4F
501+ *
502+ * PAC style encoding (byte2):
503+ * 0x40/0x60: white, regular 0x41/0x61: white, underline
504+ * 0x42/0x62: green, regular 0x43/0x63: green, underline
505+ * 0x44/0x64: blue, regular 0x45/0x65: blue, underline
506+ * 0x46/0x66: cyan, regular 0x47/0x67: cyan, underline
507+ * 0x48/0x68: red, regular 0x49/0x69: red, underline
508+ * 0x4a/0x6a: yellow, regular 0x4b/0x6b: yellow, underline
509+ * 0x4c/0x6c: magenta, regular 0x4d/0x6d: magenta, underline
510+ * 0x4e/0x6e: white, italics 0x4f/0x6f: white, italic underline
511+ */
512+ static unsigned char get_styled_pac_byte2 (enum ccx_decoder_608_color_code color , enum font_bits font , bool use_high_range )
513+ {
514+ unsigned char base = use_high_range ? 0x60 : 0x40 ;
515+ unsigned char style_offset ;
516+
517+ // Handle italics specially - they're always white
518+ if (font == FONT_ITALICS )
519+ return base + 0x0e ;
520+ if (font == FONT_UNDERLINED_ITALICS )
521+ return base + 0x0f ;
522+
523+ // Map color to base offset (0, 2, 4, 6, 8, 10, 12)
524+ switch (color )
525+ {
526+ case COL_WHITE :
527+ style_offset = 0x00 ;
528+ break ;
529+ case COL_GREEN :
530+ style_offset = 0x02 ;
531+ break ;
532+ case COL_BLUE :
533+ style_offset = 0x04 ;
534+ break ;
535+ case COL_CYAN :
536+ style_offset = 0x06 ;
537+ break ;
538+ case COL_RED :
539+ style_offset = 0x08 ;
540+ break ;
541+ case COL_YELLOW :
542+ style_offset = 0x0a ;
543+ break ;
544+ case COL_MAGENTA :
545+ style_offset = 0x0c ;
546+ break ;
547+ default :
548+ // For unsupported colors (black, transparent, userdefined), fall back to white
549+ style_offset = 0x00 ;
550+ break ;
551+ }
552+
553+ // Add 1 for underlined
554+ if (font == FONT_UNDERLINED )
555+ style_offset += 1 ;
556+
557+ return base + style_offset ;
558+ }
559+
560+ /**
561+ * Check if the row uses high range (0x60-0x6F) or low range (0x40-0x4F) for styled PACs
562+ * Rows that have byte2 in 0x70-0x7F range for indents use 0x60-0x6F for styles
563+ */
564+ static bool row_uses_high_range (unsigned char row )
565+ {
566+ // Based on the preamble code table:
567+ // Rows 2, 4, 6, 8, 10, 13, 15 use the "high" range (byte2 0x70-0x7F for indents)
568+ // which corresponds to 0x60-0x6F for styled PACs
569+ return (row == 1 || row == 3 || row == 5 || row == 7 || row == 9 || row == 12 || row == 14 );
570+ }
571+
572+ /**
573+ * Write a styled PAC code (color/font at column 0) directly
574+ * This is more efficient than using indent PAC + mid-row code when at column 0
575+ *
576+ * @param fd File descriptor
577+ * @param channel Caption channel (1-4)
578+ * @param row Row number (0-14)
579+ * @param color Color to set
580+ * @param font Font style to set
581+ * @param disassemble If true, output assembly format
582+ * @param bytes_written Pointer to byte counter
583+ */
584+ static void write_styled_preamble (const int fd , const unsigned char channel , const unsigned char row ,
585+ enum ccx_decoder_608_color_code color , enum font_bits font ,
586+ const bool disassemble , unsigned int * bytes_written )
587+ {
588+ // Get the preamble code for column 0 to obtain byte1
589+ enum control_code base_preamble = get_preamble_code (row , 0 );
590+ unsigned char byte1 = odd_parity (get_first_byte (channel , base_preamble ));
591+
592+ // Get styled byte2
593+ bool use_high_range = row_uses_high_range (row );
594+ unsigned char byte2 = odd_parity (get_styled_pac_byte2 (color , font , use_high_range ));
595+
596+ check_padding (fd , disassemble , bytes_written );
597+
598+ if (disassemble )
599+ {
600+ // Output assembly format like {0100Gr} for row 1, green
601+ const char * color_names [] = {"Wh" , "Gr" , "Bl" , "Cy" , "R" , "Y" , "Ma" , "Wh" , "Bk" , "Wh" };
602+ const char * font_suffix = "" ;
603+ if (font == FONT_UNDERLINED )
604+ font_suffix = "U" ;
605+ else if (font == FONT_ITALICS )
606+ font_suffix = "I" ;
607+ else if (font == FONT_UNDERLINED_ITALICS )
608+ font_suffix = "IU" ;
609+
610+ fdprintf (fd , "{%02d00%s%s}" , row + 1 , color_names [color ], font_suffix );
611+ }
612+ else
613+ {
614+ if (* bytes_written % 2 == 0 )
615+ write_wrapped (fd , " " , 1 );
616+ fdprintf (fd , "%02x%02x" , byte1 , byte2 );
617+ }
618+ * bytes_written += 2 ;
619+ }
620+
621+ /**
622+ * Check if a styled PAC can be used (when color/font differs from white/regular and column is 0)
623+ */
624+ static bool can_use_styled_pac (enum ccx_decoder_608_color_code color , enum font_bits font , unsigned char column )
625+ {
626+ // Styled PACs can only be used at column 0
627+ if (column != 0 )
628+ return false;
629+
630+ // If style is already white/regular, no need for styled PAC
631+ if (color == COL_WHITE && font == FONT_REGULAR )
632+ return false;
633+
634+ return true;
635+ }
636+
495637enum control_code get_tab_offset_code (const unsigned char column )
496638{
497639 int offset = column % 4 ;
@@ -519,6 +661,23 @@ enum control_code get_font_code(enum font_bits font, enum ccx_decoder_608_color_
519661 }
520662}
521663
664+ // Get frame rate value from scc_framerate setting
665+ // 0=29.97 (default), 1=24, 2=25, 3=30
666+ static float get_scc_fps (int scc_framerate )
667+ {
668+ switch (scc_framerate )
669+ {
670+ case 1 :
671+ return 24.0f ;
672+ case 2 :
673+ return 25.0f ;
674+ case 3 :
675+ return 30.0f ;
676+ default :
677+ return 29.97f ;
678+ }
679+ }
680+
522681void add_timestamp (const struct encoder_ctx * context , LLONG time , const bool disassemble )
523682{
524683 write_wrapped (context -> out -> fh , context -> encoded_crlf , context -> encoded_crlf_length );
@@ -528,8 +687,9 @@ void add_timestamp(const struct encoder_ctx *context, LLONG time, const bool dis
528687 unsigned hour , minute , second , milli ;
529688 millis_to_time (time , & hour , & minute , & second , & milli );
530689
531- // SMPTE format
532- float frame = milli * 29.97 / 1000 ;
690+ // SMPTE format - use configurable frame rate (issue #1191)
691+ float fps = get_scc_fps (context -> scc_framerate );
692+ float frame = milli * fps / 1000 ;
533693 fdprintf (context -> out -> fh , "%02u:%02u:%02u:%02.f\t" , hour , minute , second , frame );
534694}
535695
@@ -578,6 +738,23 @@ int write_cc_buffer_as_scenarist(const struct eia608_screen *data, struct encode
578738 {
579739 if (switch_font || switch_color )
580740 {
741+ // Optimization (issue #1191): Use styled PAC when at column 0 with non-default style
742+ // This avoids needing a separate mid-row code
743+ if (column == 0 && can_use_styled_pac (data -> colors [row ][column ], data -> fonts [row ][column ], 0 ))
744+ {
745+ write_styled_preamble (context -> out -> fh , data -> channel , row ,
746+ data -> colors [row ][column ], data -> fonts [row ][column ],
747+ disassemble , & bytes_written );
748+ current_row = row ;
749+ current_column = 0 ;
750+ current_font = data -> fonts [row ][column ];
751+ current_color = data -> colors [row ][column ];
752+ // Write the character and continue
753+ write_character (context -> out -> fh , data -> characters [row ][column ], disassemble , & bytes_written );
754+ ++ current_column ;
755+ continue ;
756+ }
757+
581758 if (data -> characters [row ][column ] == ' ' )
582759 {
583760 // The MID-ROW code is going to move the cursor to the
0 commit comments