diff --git a/src/608.c b/src/608.c index e19dfd2e..0075cbd4 100644 --- a/src/608.c +++ b/src/608.c @@ -1,4 +1,5 @@ #include "ccextractor.h" +#include "608_spupng.h" static const int rowdata[] = {11,-1,1,2,3,4,12,13,14,15,5,6,7,8,9,10}; // Relationship between the first PAC byte and the row number @@ -282,7 +283,7 @@ void handle_text_attr(const unsigned char c1, const unsigned char c2, struct s_c } -void write_subtitle_file_footer(struct s_context_cc608 *context) +void write_subtitle_file_footer(struct ccx_s_write *out) { switch (ccx_options.write_format) { @@ -293,7 +294,7 @@ void write_subtitle_file_footer(struct s_context_cc608 *context) dbg_print(CCX_DMT_608, "\r%s\n", str); } enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str); - write(context->out->fh, enc_buffer, enc_buffer_used); + write(out->fh, enc_buffer, enc_buffer_used); break; case CCX_OF_SMPTETT: sprintf ((char *) str,"\n"); @@ -302,10 +303,10 @@ void write_subtitle_file_footer(struct s_context_cc608 *context) dbg_print(CCX_DMT_608, "\r%s\n", str); } enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str); - write (context->out->fh, enc_buffer,enc_buffer_used); + write (out->fh, enc_buffer,enc_buffer_used); break; case CCX_OF_SPUPNG: - write_spumux_footer(context); + write_spumux_footer(out); break; default: // Nothing to do, no footer on this format break; @@ -313,7 +314,7 @@ void write_subtitle_file_footer(struct s_context_cc608 *context) } -void write_subtitle_file_header(struct s_context_cc608 *context) +void write_subtitle_file_header(struct ccx_s_write *out) { switch (ccx_options.write_format) { @@ -323,19 +324,19 @@ void write_subtitle_file_header(struct s_context_cc608 *context) //fprintf_encoded (wb->fh, sami_header); REQUEST_BUFFER_CAPACITY(strlen (sami_header)*3); enc_buffer_used=encode_line (enc_buffer,(unsigned char *) sami_header); - write (context->out->fh, enc_buffer,enc_buffer_used); + write (out->fh, enc_buffer,enc_buffer_used); break; case CCX_OF_SMPTETT: // This header brought to you by McPoodle's CCASDI //fprintf_encoded (wb->fh, sami_header); REQUEST_BUFFER_CAPACITY(strlen (smptett_header)*3); enc_buffer_used=encode_line (enc_buffer,(unsigned char *) smptett_header); - write(context->out->fh, enc_buffer, enc_buffer_used); + write(out->fh, enc_buffer, enc_buffer_used); break; case CCX_OF_RCWT: // Write header - write(context->out->fh, rcwt_header, sizeof(rcwt_header)); + write(out->fh, rcwt_header, sizeof(rcwt_header)); break; case CCX_OF_SPUPNG: - write_spumux_header(context); + write_spumux_header(out); break; case CCX_OF_TRANSCRIPT: // No header. Fall thru default: diff --git a/src/608.h b/src/608.h index c8dfaaee..fa69b9e8 100644 --- a/src/608.h +++ b/src/608.h @@ -42,7 +42,6 @@ struct s_context_cc608 int new_channel; // The new channel after a channel change int my_field; // Used for sanity checks long bytes_processed_608; // To be written ONLY by process_608 - void* spupng_data; struct ccx_s_write *out; }; @@ -91,10 +90,6 @@ unsigned char cctoupper (unsigned char c); int general_608_init (void); LLONG get_visible_end (void); -void write_spumux_header(struct s_context_cc608 *context); -void write_spumux_footer(struct s_context_cc608 *context); -int write_cc_buffer_as_spupng(struct eia608_screen* data, struct s_context_cc608 *context); - #define CC608_SCREEN_WIDTH 32 #define REQUEST_BUFFER_CAPACITY(length) if (length>enc_buffer_capacity) \ diff --git a/src/608_spupng.c b/src/608_spupng.c index 40573698..ba1677f6 100644 --- a/src/608_spupng.c +++ b/src/608_spupng.c @@ -1,435 +1,7 @@ #include #include - -#ifdef _WIN32 -#include -#define mkdir(path, mode) _mkdir(path) -#endif - #include "608_spupng.h" -// #include "wstfont2.xbm" // Teletext font, not used -#include "ccfont2.xbm" // CC font from libzvbi - -// CC page dimensions -#define ROWS 15 -#define COLUMNS 32 - -/* Closed Caption character cell dimensions */ -#define CCW 16 -#define CCH 26 /* line doubled */ -#define CCPL (ccfont2_width / CCW * ccfont2_height / CCH) - -void -write_spumux_header(struct s_context_cc608 *context) -{ - if (0 == context->spupng_data) - context->spupng_data = spunpg_init(context); - - spupng_write_header((struct spupng_t*)context->spupng_data); -} - -void -write_spumux_footer(struct s_context_cc608 *context) -{ - if (0 != context->spupng_data) - { - struct spupng_t *sp = (struct spupng_t *) context->spupng_data; - - spupng_write_footer(sp); - spunpg_free(sp); - - context->spupng_data = 0; - context->out->fh = -1; - } -} - -int -write_cc_buffer_as_spupng(struct eia608_screen *data, struct s_context_cc608 *context) -{ - if (0 != context->spupng_data) - { - struct spupng_t *sp = (struct spupng_t *) context->spupng_data; - return spupng_write_ccbuffer(sp, data, context); - } - return 0; -} - -static int initialized = 0; - -struct spupng_t *spunpg_init(struct s_context_cc608 *context) -{ - struct spupng_t *sp = (struct spupng_t *) malloc(sizeof(struct spupng_t)); - if (NULL == sp) - fatal(EXIT_NOT_ENOUGH_MEMORY, "Memory allocation failed"); - - if (!initialized) - { - initialized = 1; - spupng_init_font(); - } - - if ((sp->fpxml = fdopen(context->out->fh, "w")) == NULL) - { - fatal(EXIT_FILE_CREATION_FAILED, "Cannot open %s: %s\n", - context->out->filename, strerror(errno)); - } - sp->dirname = (char *) malloc( - sizeof(char) * (strlen(context->out->filename) + 3)); - if (NULL == sp->dirname) - fatal(EXIT_NOT_ENOUGH_MEMORY, "Memory allocation failed"); - - strcpy(sp->dirname, context->out->filename); - char* p = strrchr(sp->dirname, '.'); - if (NULL == p) - p = sp->dirname + strlen(sp->dirname); - *p = '\0'; - strcat(sp->dirname, ".d"); - if (0 != mkdir(sp->dirname, 0777)) - { - if (errno != EEXIST) - { - fatal(EXIT_FILE_CREATION_FAILED, "Cannot create %s: %s\n", - sp->dirname, strerror(errno)); - } - // If dirname isn't a directory or if we don't have write permission, - // the first attempt to create a .png file will fail and we'll XXxit. - } - - // enough to append /subNNNN.png - sp->pngfile = (char *) malloc(sizeof(char) * (strlen(sp->dirname) + 13)); - if (NULL == sp->pngfile) - fatal(EXIT_NOT_ENOUGH_MEMORY, "Memory allocation failed"); - sp->fileIndex = 0; - - // For NTSC closed captions and 720x480 DVD subtitle resolution: - // Each character is 16x26. - // 15 rows by 32 columns, plus 2 columns for left & right padding - // So each .png image will be 16*34 wide and 26*15 high, or 544x390 - // To center image in 720x480 DVD screen, offset image by 88 and 45 - // Need to keep yOffset even to prevent flicker on interlaced displays - // Would need to do something different for PAL format and teletext. - sp->xOffset = 88; - sp->yOffset = 46; - - return sp; -} - -void -spunpg_free(struct spupng_t *sp) -{ - free(sp->dirname); - free(sp->pngfile); - free(sp); -} - -void -spupng_write_header(struct spupng_t *sp) -{ - fprintf(sp->fpxml, "\n\n"); - if (num_input_files > 0) - fprintf(sp->fpxml, "\n", inputfile[0]); -} - -void -spupng_write_footer(struct spupng_t *sp) -{ - fprintf(sp->fpxml, "\n\n"); - fflush(sp->fpxml); - fclose(sp->fpxml); -} - -int -spupng_write_ccbuffer(struct spupng_t *sp, struct eia608_screen* data, - struct s_context_cc608 *context) -{ - LLONG ms_start = context->current_visible_start_ms + subs_delay; - if (ms_start < 0) - { - dbg_print(CCX_DMT_VERBOSE, "Negative start\n"); - return 0; - } - - int row; - int empty_buf = 1; - for (row = 0; row < 15; row++) - { - if (data->row_used[row]) - { - empty_buf = 0; - break; - } - } - if (empty_buf) - { - dbg_print(CCX_DMT_VERBOSE, "Blank page\n"); - return 0; - } - - LLONG ms_end = get_visible_end() + subs_delay; - - sprintf(sp->pngfile, "%s/sub%04d.png", sp->dirname, sp->fileIndex++); - if ((sp->fppng = fopen(sp->pngfile, "wb")) == NULL) - { - fatal(EXIT_FILE_CREATION_FAILED, "Cannot open %s: %s\n", - sp->pngfile, strerror(errno)); - } - if (!spupng_export_png(sp, data)) - { - fatal(EXIT_FILE_CREATION_FAILED, "Cannot write %s: %s\n", - sp->pngfile, strerror(errno)); - } - fclose(sp->fppng); - - fprintf(sp->fpxml, "fpxml, " end=\"%.3f\"", ((double)ms_end) / 1000); - dbg_print(CCX_DMT_608, " end=\"%.3f\"", ((double)ms_end) / 1000); - fprintf(sp->fpxml, " image=\"%s\"", sp->pngfile); - dbg_print(CCX_DMT_608, " image=\"%s\"", sp->pngfile); - fprintf(sp->fpxml, " xoffset=\"%d\"", sp->xOffset); - dbg_print(CCX_DMT_608, " xoffset=\"%d\"", sp->xOffset); - fprintf(sp->fpxml, " yoffset=\"%d\"", sp->yOffset); - dbg_print(CCX_DMT_608, " yoffset=\"%d\"", sp->yOffset); - fprintf(sp->fpxml, ">\n\n"); - dbg_print(CCX_DMT_608, "-->\n"); - - fflush(sp->fpxml); - - return 1; -} - -// -// Begin copy from http://zapping.cvs.sourceforge.net/viewvc/zapping/vbi/src/exp-gfx.c?view=markup&pathrev=zvbi-0-2-33 -// - -void -spupng_init_font() -{ - uint8_t *t, *p; - int i, j; - - /* de-interleave font image (puts all chars in row 0) */ -#if 0 - if (!(t = malloc(wstfont2_width * wstfont2_height / 8))) - exit(EXIT_FAILURE); - - for (p = t, i = 0; i < TCH; i++) - for (j = 0; j < wstfont2_height; p += wstfont2_width / 8, j += TCH) - memcpy(p, wstfont2_bits + (j + i) * wstfont2_width / 8, - wstfont2_width / 8); - - memcpy(wstfont2_bits, t, wstfont2_width * wstfont2_height / 8); - free (t); -#endif - if (!(t = (uint8_t*)malloc(ccfont2_width * ccfont2_height / 8))) - fatal(EXIT_NOT_ENOUGH_MEMORY, "Memory allocation failed"); - - for (p = t, i = 0; i < CCH; i++) - for (j = 0; j < ccfont2_height; p += ccfont2_width / 8, j += CCH) - memcpy(p, ccfont2_bits + (j + i) * ccfont2_width / 8, - ccfont2_width / 8); - - memcpy(ccfont2_bits, t, ccfont2_width * ccfont2_height / 8); - free(t); -} - -/** - * @internal - * @param c Unicode. - * @param italic @c TRUE to switch to slanted character set. - * - * Translate Unicode character to glyph number in ccfont2 image. - * - * @return - * Glyph number. - */ -static unsigned int -unicode_ccfont2(unsigned int c, int italic) -{ - static const unsigned short specials[] = { - 0x00E1, 0x00E9, - 0x00ED, 0x00F3, 0x00FA, 0x00E7, 0x00F7, 0x00D1, 0x00F1, 0x25A0, - 0x00AE, 0x00B0, 0x00BD, 0x00BF, 0x2122, 0x00A2, 0x00A3, 0x266A, - 0x00E0, 0x0020, 0x00E8, 0x00E2, 0x00EA, 0x00EE, 0x00F4, 0x00FB }; - unsigned int i; - - if (c < 0x0020) - c = 15; /* invalid */ - else if (c < 0x0080) - c = c; - else { - for (i = 0; i < sizeof(specials) / sizeof(specials[0]); i++) - if (specials[i] == c) { - c = i + 6; - goto slant; - } - - c = 15; /* invalid */ - } - -slant: - if (italic) - c += 4 * 32; - - return c; -} - -/** - * @internal - * @param p Plane of @a canvas_type char, short, int. - * @param i Index. - * - * @return - * Pixel @a i in plane @a p. - */ -#define peek(p, i) \ -((canvas_type == sizeof(uint8_t)) ? ((uint8_t *)(p))[i] : \ - ((canvas_type == sizeof(uint16_t)) ? ((uint16_t *)(p))[i] : \ - ((uint32_t *)(p))[i])) - -/** - * @internal - * @param p Plane of @a canvas_type char, short, int. - * @param i Index. - * @param v Value. - * - * Set pixel @a i in plane @a p to value @a v. - */ -#define poke(p, i, v) \ -((canvas_type == sizeof(uint8_t)) ? (((uint8_t *)(p))[i] = (v)) : \ - ((canvas_type == sizeof(uint16_t)) ? (((uint16_t *)(p))[i] = (v)) : \ - (((uint32_t *)(p))[i] = (v)))) - -/** - * @internal - * @param canvas_type sizeof(char, short, int). - * @param canvas Pointer to image plane where the character is to be drawn. - * @param rowstride @a canvas byte distance from line to line. - * @param pen Pointer to color palette of @a canvas_type (index 0 background - * pixels, index 1 foreground pixels). - * @param font Pointer to font image with width @a cpl x @a cw pixels, height - * @a ch pixels, depth one bit, bit '1' is foreground. - * @param cpl Chars per line (number of characters in @a font image). - * @param cw Character cell width in pixels. - * @param ch Character cell height in pixels. - * @param glyph Glyph number in font image, 0 ... @a cpl - 1. - * @param underline Bit mask of character rows. For each bit - * 1 << (n = 0 ... @a ch - 1) set all of character row n to - * foreground color. - * @param size Size of character, either NORMAL, DOUBLE_WIDTH (draws left - * and right half), DOUBLE_HEIGHT (draws upper half only), - * DOUBLE_SIZE (left and right upper half), DOUBLE_HEIGHT2 - * (lower half), DOUBLE_SIZE2 (left and right lower half). - * - * Draw one character (function template - define a static version with - * constant @a canvas_type, @a font, @a cpl, @a cw, @a ch). - */ -static void -draw_char(int canvas_type, uint8_t *canvas, int rowstride, - uint8_t *pen, uint8_t *font, int cpl, int cw, int ch, - int glyph, unsigned int underline) -{ - uint8_t *src; - int shift, x, y; - - assert(cw >= 8 && cw <= 16); - assert(ch >= 1 && ch <= 31); - - x = glyph * cw; - shift = x & 7; - src = font + (x >> 3); - - for (y = 0; y < ch; underline >>= 1, y++) { - int bits = ~0; - - if (!(underline & 1)) { -#ifdef __i386__ - bits = (*((uint16_t *) src) >> shift); -#else - /* unaligned/little endian */ - bits = ((src[1] * 256 + src[0]) >> shift); -#endif - } - - for (x = 0; x < cw; bits >>= 1, x++) - poke(canvas, x, peek(pen, bits & 1)); - - canvas += rowstride; - - src += cpl * cw / 8; - } -} - -/** - * @internal - * @param canvas_type sizeof(char, short, int). - * @param canvas Pointer to image plane where the character is to be drawn. - * @param rowstride @a canvas byte distance from line to line. - * @param color Color value of @a canvas_type. - * @param cw Character width in pixels. - * @param ch Character height in pixels. - * - * Draw blank character. - */ -static void -draw_blank(int canvas_type, uint8_t *canvas, unsigned int rowstride, - unsigned int color, int cw, int ch) -{ - int x, y; - - for (y = 0; y < ch; y++) { - for (x = 0; x < cw; x++) - poke(canvas, x, color); - - canvas += rowstride; - } -} - -/* - * PNG and XPM drawing functions (palette-based) - */ -static void -draw_char_indexed(uint8_t * canvas, int rowstride, uint8_t * pen, - int unicode, int italic, int underline) -{ - draw_char(sizeof(*canvas), canvas, rowstride, - pen, (uint8_t *) ccfont2_bits, CCPL, CCW, CCH, - unicode_ccfont2(unicode, italic), - underline * (3 << 24) /* cell row 24, 25 */); -} - - void draw_row(struct eia608_screen* data, int row, uint8_t * canvas, int rowstride) { @@ -503,7 +75,7 @@ static png_byte alpha[10] = 0 }; -int +int spupng_write_png(struct spupng_t *sp, struct eia608_screen* data, png_structp png_ptr, png_infop info_ptr, png_bytep image, @@ -620,7 +192,102 @@ unknown_error: return 0; } -// -// End copy from http://zapping.cvs.sourceforge.net/viewvc/zapping/vbi/src/exp-gfx.c?view=markup&pathrev=zvbi-0-2-33 -// +int +spupng_write_ccbuffer(struct spupng_t *sp, struct eia608_screen* data, + struct s_context_cc608 *context) +{ + LLONG ms_start = context->current_visible_start_ms + subs_delay; + if (ms_start < 0) + { + dbg_print(CCX_DMT_VERBOSE, "Negative start\n"); + return 0; + } + + int row; + int empty_buf = 1; + for (row = 0; row < 15; row++) + { + if (data->row_used[row]) + { + empty_buf = 0; + break; + } + } + if (empty_buf) + { + dbg_print(CCX_DMT_VERBOSE, "Blank page\n"); + return 0; + } + + LLONG ms_end = get_visible_end() + subs_delay; + + sprintf(sp->pngfile, "%s/sub%04d.png", sp->dirname, sp->fileIndex++); + if ((sp->fppng = fopen(sp->pngfile, "wb")) == NULL) + { + fatal(EXIT_FILE_CREATION_FAILED, "Cannot open %s: %s\n", + sp->pngfile, strerror(errno)); + } + if (!spupng_export_png(sp, data)) + { + fatal(EXIT_FILE_CREATION_FAILED, "Cannot write %s: %s\n", + sp->pngfile, strerror(errno)); + } + fclose(sp->fppng); + + fprintf(sp->fpxml, "fpxml, " end=\"%.3f\"", ((double)ms_end) / 1000); + dbg_print(CCX_DMT_608, " end=\"%.3f\"", ((double)ms_end) / 1000); + fprintf(sp->fpxml, " image=\"%s\"", sp->pngfile); + dbg_print(CCX_DMT_608, " image=\"%s\"", sp->pngfile); + fprintf(sp->fpxml, " xoffset=\"%d\"", sp->xOffset); + dbg_print(CCX_DMT_608, " xoffset=\"%d\"", sp->xOffset); + fprintf(sp->fpxml, " yoffset=\"%d\"", sp->yOffset); + dbg_print(CCX_DMT_608, " yoffset=\"%d\"", sp->yOffset); + fprintf(sp->fpxml, ">\n\n"); + dbg_print(CCX_DMT_608, "-->\n"); + + fflush(sp->fpxml); + + return 1; +} +int write_cc_buffer_as_spupng(struct eia608_screen *data,struct s_context_cc608 *context) +{ + if (0 != context->out->spupng_data) + { + struct spupng_t *sp = (struct spupng_t *) context->out->spupng_data; + return spupng_write_ccbuffer(sp, data, context); + } + return 0; +} diff --git a/src/608_spupng.h b/src/608_spupng.h index 5b79d129..54b4064f 100644 --- a/src/608_spupng.h +++ b/src/608_spupng.h @@ -1,37 +1,9 @@ #ifndef __608_SPUPNG_H__ -#include "png.h" #include "ccextractor.h" +#include "spupng_encoder.h" -struct spupng_t -{ - FILE* fpxml; - FILE* fppng; - char* dirname; - char* pngfile; - int fileIndex; - int xOffset; - int yOffset; -}; -struct spupng_t *spunpg_init(struct s_context_cc608 *context); -void spunpg_free(struct spupng_t *spupng); - -void spupng_write_header(struct spupng_t *spupng); -void spupng_write_footer(struct spupng_t *spupng); - -int spupng_write_ccbuffer(struct spupng_t *spupng, struct eia608_screen* data, - struct s_context_cc608 *context); - -void spupng_init_font(); - -int spupng_write_png(struct spupng_t *spupng, - struct eia608_screen* data, - png_structp png_ptr, png_infop info_ptr, - png_bytep image, - png_bytep* row_pointer, - unsigned int ww, unsigned int wh); - -int spupng_export_png(struct spupng_t *spupng, struct eia608_screen* data); +int write_cc_buffer_as_spupng(struct eia608_screen *data,struct s_context_cc608 *context); #endif /* __608_SPUPNG_H__ */ diff --git a/src/ccextractor.c b/src/ccextractor.c index 1d15f6bd..b98b7980 100644 --- a/src/ccextractor.c +++ b/src/ccextractor.c @@ -470,7 +470,7 @@ int main(int argc, char *argv[]) writeraw (UTF8_BOM, sizeof (UTF8_BOM), &wbout1); if (ccx_options.encoding==CCX_ENC_UNICODE) // Write BOM writeraw (LITTLE_ENDIAN_BOM, sizeof (LITTLE_ENDIAN_BOM), &wbout1); - write_subtitle_file_header(&context_cc608_field_1); + write_subtitle_file_header(context_cc608_field_1.out); } } if (ccx_options.extract == 12) @@ -510,7 +510,7 @@ int main(int argc, char *argv[]) writeraw (UTF8_BOM, sizeof (UTF8_BOM), &wbout2); if (ccx_options.encoding==CCX_ENC_UNICODE) // Write BOM writeraw (LITTLE_ENDIAN_BOM, sizeof (LITTLE_ENDIAN_BOM), &wbout2); - write_subtitle_file_header(&context_cc608_field_2); + write_subtitle_file_header(context_cc608_field_2.out); } } } @@ -825,7 +825,7 @@ int main(int argc, char *argv[]) } if (ccx_options.end_credits_text!=NULL) try_to_add_end_credits(&context_cc608_field_1); - write_subtitle_file_footer(&context_cc608_field_1); + write_subtitle_file_footer(context_cc608_field_1.out); } if (wbout2.fh!=-1) { @@ -840,7 +840,7 @@ int main(int argc, char *argv[]) } if (ccx_options.end_credits_text!=NULL) try_to_add_end_credits(&context_cc608_field_2); - write_subtitle_file_footer(&context_cc608_field_2); + write_subtitle_file_footer(context_cc608_field_2.out); } telxcc_close(); flushbuffer (&wbout1,true); diff --git a/src/ccextractor.h b/src/ccextractor.h index 366dcef4..3198f21d 100644 --- a/src/ccextractor.h +++ b/src/ccextractor.h @@ -139,6 +139,7 @@ struct ccx_s_write { int fh; char *filename; + void* spupng_data; }; @@ -369,8 +370,8 @@ int levenshtein_dist (const uint64_t *s1, const uint64_t *s2, unsigned s1len, un void init_context_cc608(struct s_context_cc608 *data, int field); unsigned encode_line (unsigned char *buffer, unsigned char *text); void buffered_seek (int offset); -void write_subtitle_file_header(struct s_context_cc608 *context); -void write_subtitle_file_footer(struct s_context_cc608 *context); +void write_subtitle_file_header(struct ccx_s_write *out); +void write_subtitle_file_footer(struct ccx_s_write *out); extern void build_parity_table(void); void tlt_process_pes_packet(uint8_t *buffer, uint16_t size) ; diff --git a/src/platform.h b/src/platform.h index b5fc3d4d..f937be8a 100644 --- a/src/platform.h +++ b/src/platform.h @@ -16,6 +16,8 @@ typedef int socklen_t; typedef uint32_t in_addr_t; #define IN_CLASSD(i) (((INT32)(i) & 0xf0000000) == 0xe0000000) #define IN_MULTICAST(i) IN_CLASSD(i) +#include +#define mkdir(path, mode) _mkdir(path) #else // _WIN32 @@ -27,6 +29,8 @@ typedef uint32_t in_addr_t; #include #include #include +#include +#include #endif // _WIN32 diff --git a/src/spupng_encoder.c b/src/spupng_encoder.c new file mode 100644 index 00000000..bf30cf85 --- /dev/null +++ b/src/spupng_encoder.c @@ -0,0 +1,294 @@ +#include "ccfont2.xbm" // CC font from libzvbi +#include "platform.h" +#include "spupng_encoder.h" +#include + +#define CCPL (ccfont2_width / CCW * ccfont2_height / CCH) + +static int initialized = 0; + +void +spupng_init_font() +{ + uint8_t *t, *p; + int i, j; + + /* de-interleave font image (puts all chars in row 0) */ + if (!(t = (uint8_t*)malloc(ccfont2_width * ccfont2_height / 8))) + fatal(EXIT_NOT_ENOUGH_MEMORY, "Memory allocation failed"); + + for (p = t, i = 0; i < CCH; i++) + for (j = 0; j < ccfont2_height; p += ccfont2_width / 8, j += CCH) + memcpy(p, ccfont2_bits + (j + i) * ccfont2_width / 8, + ccfont2_width / 8); + + memcpy(ccfont2_bits, t, ccfont2_width * ccfont2_height / 8); + free(t); +} + +struct spupng_t *spunpg_init(struct ccx_s_write *out) +{ + struct spupng_t *sp = (struct spupng_t *) malloc(sizeof(struct spupng_t)); + if (NULL == sp) + fatal(EXIT_NOT_ENOUGH_MEMORY, "Memory allocation failed"); + + if (!initialized) + { + initialized = 1; + spupng_init_font(); + } + + if ((sp->fpxml = fdopen(out->fh, "w")) == NULL) + { + fatal(EXIT_FILE_CREATION_FAILED, "Cannot open %s: %s\n", + out->filename, strerror(errno)); + } + sp->dirname = (char *) malloc( + sizeof(char) * (strlen(out->filename) + 3)); + if (NULL == sp->dirname) + fatal(EXIT_NOT_ENOUGH_MEMORY, "Memory allocation failed"); + + strcpy(sp->dirname, out->filename); + char* p = strrchr(sp->dirname, '.'); + if (NULL == p) + p = sp->dirname + strlen(sp->dirname); + *p = '\0'; + strcat(sp->dirname, ".d"); + if (0 != mkdir(sp->dirname, 0777)) + { + if (errno != EEXIST) + { + fatal(EXIT_FILE_CREATION_FAILED, "Cannot create %s: %s\n", + sp->dirname, strerror(errno)); + } + // If dirname isn't a directory or if we don't have write permission, + // the first attempt to create a .png file will fail and we'll XXxit. + } + + // enough to append /subNNNN.png + sp->pngfile = (char *) malloc(sizeof(char) * (strlen(sp->dirname) + 13)); + if (NULL == sp->pngfile) + fatal(EXIT_NOT_ENOUGH_MEMORY, "Memory allocation failed"); + sp->fileIndex = 0; + + // For NTSC closed captions and 720x480 DVD subtitle resolution: + // Each character is 16x26. + // 15 rows by 32 columns, plus 2 columns for left & right padding + // So each .png image will be 16*34 wide and 26*15 high, or 544x390 + // To center image in 720x480 DVD screen, offset image by 88 and 45 + // Need to keep yOffset even to prevent flicker on interlaced displays + // Would need to do something different for PAL format and teletext. + sp->xOffset = 88; + sp->yOffset = 46; + + return sp; +} +void +spunpg_free(struct spupng_t *sp) +{ + free(sp->dirname); + free(sp->pngfile); + free(sp); +} + +void +spupng_write_header(struct spupng_t *sp) +{ + fprintf(sp->fpxml, "\n\n"); + if (num_input_files > 0) + fprintf(sp->fpxml, "\n", inputfile[0]); +} + +void +spupng_write_footer(struct spupng_t *sp) +{ + fprintf(sp->fpxml, "\n\n"); + fflush(sp->fpxml); + fclose(sp->fpxml); +} + +void write_spumux_header(struct ccx_s_write *out) +{ + if (0 == out->spupng_data) + out->spupng_data = spunpg_init(out); + + spupng_write_header((struct spupng_t*)out->spupng_data); +} + +void write_spumux_footer(struct ccx_s_write *out) +{ + if (0 != out->spupng_data) + { + struct spupng_t *sp = (struct spupng_t *) out->spupng_data; + + spupng_write_footer(sp); + spunpg_free(sp); + + out->spupng_data = 0; + out->fh = -1; + } +} + +/** + * @internal + * @param p Plane of @a canvas_type char, short, int. + * @param i Index. + * + * @return + * Pixel @a i in plane @a p. + */ +#define peek(p, i) \ +((canvas_type == sizeof(uint8_t)) ? ((uint8_t *)(p))[i] : \ + ((canvas_type == sizeof(uint16_t)) ? ((uint16_t *)(p))[i] : \ + ((uint32_t *)(p))[i])) + +/** + * @internal + * @param p Plane of @a canvas_type char, short, int. + * @param i Index. + * @param v Value. + * + * Set pixel @a i in plane @a p to value @a v. + */ +#define poke(p, i, v) \ +((canvas_type == sizeof(uint8_t)) ? (((uint8_t *)(p))[i] = (v)) : \ + ((canvas_type == sizeof(uint16_t)) ? (((uint16_t *)(p))[i] = (v)) : \ + (((uint32_t *)(p))[i] = (v)))) + +/** + * @internal + * @param c Unicode. + * @param italic @c TRUE to switch to slanted character set. + * + * Translate Unicode character to glyph number in ccfont2 image. + * + * @return + * Glyph number. + */ +static unsigned int +unicode_ccfont2(unsigned int c, int italic) +{ + static const unsigned short specials[] = { + 0x00E1, 0x00E9, + 0x00ED, 0x00F3, 0x00FA, 0x00E7, 0x00F7, 0x00D1, 0x00F1, 0x25A0, + 0x00AE, 0x00B0, 0x00BD, 0x00BF, 0x2122, 0x00A2, 0x00A3, 0x266A, + 0x00E0, 0x0020, 0x00E8, 0x00E2, 0x00EA, 0x00EE, 0x00F4, 0x00FB }; + unsigned int i; + + if (c < 0x0020) + c = 15; /* invalid */ + else if (c < 0x0080) + /*c = c */; + else { + for (i = 0; i < sizeof(specials) / sizeof(specials[0]); i++) + if (specials[i] == c) { + c = i + 6; + goto slant; + } + + c = 15; /* invalid */ + } + +slant: + if (italic) + c += 4 * 32; + + return c; +} + +/** + * @internal + * @param canvas_type sizeof(char, short, int). + * @param canvas Pointer to image plane where the character is to be drawn. + * @param rowstride @a canvas byte distance from line to line. + * @param color Color value of @a canvas_type. + * @param cw Character width in pixels. + * @param ch Character height in pixels. + * + * Draw blank character. + */ +static void +draw_blank(int canvas_type, uint8_t *canvas, unsigned int rowstride, + unsigned int color, int cw, int ch) +{ + int x, y; + + for (y = 0; y < ch; y++) { + for (x = 0; x < cw; x++) + poke(canvas, x, color); + + canvas += rowstride; + } +} + +/** + * @internal + * @param canvas_type sizeof(char, short, int). + * @param canvas Pointer to image plane where the character is to be drawn. + * @param rowstride @a canvas byte distance from line to line. + * @param pen Pointer to color palette of @a canvas_type (index 0 background + * pixels, index 1 foreground pixels). + * @param font Pointer to font image with width @a cpl x @a cw pixels, height + * @a ch pixels, depth one bit, bit '1' is foreground. + * @param cpl Chars per line (number of characters in @a font image). + * @param cw Character cell width in pixels. + * @param ch Character cell height in pixels. + * @param glyph Glyph number in font image, 0 ... @a cpl - 1. + * @param underline Bit mask of character rows. For each bit + * 1 << (n = 0 ... @a ch - 1) set all of character row n to + * foreground color. + * @param size Size of character, either NORMAL, DOUBLE_WIDTH (draws left + * and right half), DOUBLE_HEIGHT (draws upper half only), + * DOUBLE_SIZE (left and right upper half), DOUBLE_HEIGHT2 + * (lower half), DOUBLE_SIZE2 (left and right lower half). + * + * Draw one character (function template - define a static version with + * constant @a canvas_type, @a font, @a cpl, @a cw, @a ch). + */ +static void +draw_char(int canvas_type, uint8_t *canvas, int rowstride, + uint8_t *pen, uint8_t *font, int cpl, int cw, int ch, + int glyph, unsigned int underline) +{ + uint8_t *src; + int shift, x, y; + + assert(cw >= 8 && cw <= 16); + assert(ch >= 1 && ch <= 31); + + x = glyph * cw; + shift = x & 7; + src = font + (x >> 3); + + for (y = 0; y < ch; underline >>= 1, y++) { + int bits = ~0; + + if (!(underline & 1)) { +#ifdef __i386__ + bits = (*((uint16_t *) src) >> shift); +#else + /* unaligned/little endian */ + bits = ((src[1] * 256 + src[0]) >> shift); +#endif + } + + for (x = 0; x < cw; bits >>= 1, x++) + poke(canvas, x, peek(pen, bits & 1)); + + canvas += rowstride; + + src += cpl * cw / 8; + } +} + +/* + * PNG and XPM drawing functions (palette-based) + */ +void draw_char_indexed(uint8_t * canvas, int rowstride, uint8_t * pen, + int unicode, int italic, int underline) +{ + draw_char(sizeof(*canvas), canvas, rowstride, + pen, (uint8_t *) ccfont2_bits, CCPL, CCW, CCH, + unicode_ccfont2(unicode, italic), + underline * (3 << 24) /* cell row 24, 25 */); +} diff --git a/src/spupng_encoder.h b/src/spupng_encoder.h new file mode 100644 index 00000000..29ba7fa4 --- /dev/null +++ b/src/spupng_encoder.h @@ -0,0 +1,30 @@ +#ifndef _SPUPNG_ENCODER_H +#define _SPUPNG_ENCODER_H + +#include +#include "png.h" +#include "ccextractor.h" + +// CC page dimensions +#define ROWS 15 +#define COLUMNS 32 +/* Closed Caption character cell dimensions */ +#define CCW 16 +#define CCH 26 /* line doubled */ + +struct spupng_t +{ + FILE* fpxml; + FILE* fppng; + char* dirname; + char* pngfile; + int fileIndex; + int xOffset; + int yOffset; +}; + +void write_spumux_header(struct ccx_s_write *out); +void write_spumux_footer(struct ccx_s_write *out); +void draw_char_indexed(uint8_t * canvas, int rowstride, uint8_t * pen, + int unicode, int italic, int underline); +#endif