|
MythTV
0.26-pre
|
00001 // -*- Mode: c++ -*- 00002 // Copyright (c) 2003-2005, Daniel Kristjansson 00003 00004 #include <cassert> 00005 #include <algorithm> 00006 using namespace std; 00007 00008 #include "cc708window.h" 00009 #include "mythlogging.h" 00010 00011 /************************************************************************ 00012 00013 FCC Addons to EIA-708. 00014 00015 * Decoders must support the standard, large, and small caption sizes 00016 and must allow the caption provider to choose a size and allow the 00017 viewer to choose an alternative size. 00018 00019 * Decoders must support the eight fonts listed in EIA-708. Caption 00020 providers may specify 1 of these 8 font styles to be used to write 00021 caption text. Decoders must include the ability for consumers to 00022 choose among the eight fonts. The decoder must display the font 00023 chosen by the caption provider unless the viewer chooses a different 00024 font. 00025 00026 * Decoders must implement the same 8 character background colors 00027 as those that Section 9 requires be implemented for character 00028 foreground (white, black, red, green, blue, yellow, magenta and cyan). 00029 00030 * Decoders must implement options for altering the appearance of 00031 caption character edges. 00032 00033 * Decoders must display the color chosen by the caption provider, 00034 and must allow viewers to override the foreground and/or background 00035 color chosen by the caption provider and select alternate colors. 00036 00037 * Decoders must be capable of decoding and processing data for the 00038 six standard services, but information from only one service need 00039 be displayed at a given time. 00040 00041 * Decoders must include an option that permits a viewer to choose a 00042 setting that will display captions as intended by the caption 00043 provider (a default). Decoders must also include an option that 00044 allows a viewer's chosen settings to remain until the viewer 00045 chooses to alter these settings, including during periods when 00046 the television is turned off. 00047 00048 * Cable providers and other multichannel video programming 00049 distributors must transmit captions in a format that will be 00050 understandable to this decoder circuitry in digital cable 00051 television sets when transmitting programming to digital 00052 television devices. 00053 00054 ******************************************************************************/ 00055 00056 const uint k708JustifyLeft = 0; 00057 const uint k708JustifyRight = 1; 00058 const uint k708JustifyCenter = 2; 00059 const uint k708JustifyFull = 3; 00060 00061 const uint k708EffectSnap = 0; 00062 const uint k708EffectFade = 1; 00063 const uint k708EffectWipe = 2; 00064 00065 const uint k708BorderNone = 0; 00066 const uint k708BorderRaised = 1; 00067 const uint k708BorderDepressed = 2; 00068 const uint k708BorderUniform = 3; 00069 const uint k708BorderShadowLeft = 4; 00070 const uint k708BorderShadowRight = 5; 00071 00072 const uint k708DirLeftToRight = 0; 00073 const uint k708DirRightToLeft = 1; 00074 const uint k708DirTopToBottom = 2; 00075 const uint k708DirBottomToTop = 3; 00076 00077 const uint k708AttrSizeSmall = 0; 00078 const uint k708AttrSizeStandard = 1; 00079 const uint k708AttrSizeLarge = 2; 00080 00081 const uint k708AttrOffsetSubscript = 0; 00082 const uint k708AttrOffsetNormal = 1; 00083 const uint k708AttrOffsetSuperscript = 2; 00084 00085 const uint k708AttrFontDefault = 1; 00086 const uint k708AttrFontMonospacedSerif = 1; 00087 const uint k708AttrFontProportionalSerif = 2; 00088 const uint k708AttrFontMonospacedSansSerif = 3; 00089 const uint k708AttrFontProportionalSansSerif = 4; 00090 const uint k708AttrFontCasual = 5; 00091 const uint k708AttrFontCursive = 6; 00092 const uint k708AttrFontSmallCaps = 7; 00093 00094 extern const uint k708AttrEdgeNone = 0; 00095 extern const uint k708AttrEdgeRaised = 1; 00096 extern const uint k708AttrEdgeDepressed = 2; 00097 extern const uint k708AttrEdgeUniform = 3; 00098 extern const uint k708AttrEdgeLeftDropShadow = 4; 00099 extern const uint k708AttrEdgeRightDropShadow = 5; 00100 00101 const uint k708AttrColorBlack = 0; 00102 const uint k708AttrColorWhite = 63; 00103 00104 const uint k708AttrOpacitySolid = 0; 00105 const uint k708AttrOpacityFlash = 1; 00106 const uint k708AttrOpacityTranslucent = 2; 00107 const uint k708AttrOpacityTransparent = 3; 00108 00109 CC708Window::CC708Window() 00110 : priority(0), visible(0), 00111 anchor_point(0), relative_pos(0), 00112 anchor_vertical(0), anchor_horizontal(0), 00113 row_count(0), column_count(0), 00114 row_lock(0), column_lock(0), 00115 pen_style(0), window_style(0), 00116 00117 fill_color(0), fill_opacity(0), 00118 border_color(0), border_type(0), 00119 scroll_dir(0), print_dir(0), 00120 effect_dir(0), display_effect(0), 00121 effect_speed(0), 00122 justify(0), word_wrap(0), 00123 00124 true_row_count(0), true_column_count(0), 00125 text(NULL), exists(false), 00126 changed(true), lock(QMutex::Recursive) 00127 { 00128 } 00129 00130 void CC708Window::DefineWindow(int _priority, int _visible, 00131 int _anchor_point, int _relative_pos, 00132 int _anchor_vertical, int _anchor_horizontal, 00133 int _row_count, int _column_count, 00134 int _row_lock, int _column_lock, 00135 int _pen_style, int _window_style) 00136 { 00137 QMutexLocker locker(&lock); 00138 00139 _row_count++; 00140 _column_count++; 00141 00142 priority = _priority; 00143 visible = _visible; 00144 anchor_point = _anchor_point; 00145 relative_pos = _relative_pos; 00146 anchor_vertical = _anchor_vertical; 00147 anchor_horizontal = _anchor_horizontal; 00148 row_count = _row_count; 00149 column_count = _column_count; 00150 row_lock = _row_lock; 00151 column_lock = _column_lock; 00152 00153 if ((!_pen_style && !exists) || _pen_style) 00154 pen.SetPenStyle(_pen_style ? _pen_style : 1); 00155 00156 if ((!_window_style && !exists) || _window_style) 00157 SetWindowStyle(_window_style ? _window_style : 1); 00158 00159 uint old_row = true_row_count; 00160 uint old_col = true_column_count; 00161 // these could be bigger if row/column lock is false, resp. 00162 true_row_count = row_count; // (row_lock) ? row_count : max(row_count + 1, (uint)2); 00163 true_column_count = column_count; 00164 00165 if (text && exists && (old_col == true_column_count) && 00166 (old_row < true_row_count)) 00167 { 00168 // We need to add more rows to an existing window 00169 uint num = true_row_count * true_column_count; 00170 CC708Character *new_text = new CC708Character[num]; 00171 pen.column = 0; 00172 pen.row = 0; 00173 for (uint i = 0; i < old_row * old_col; i++) 00174 new_text[i] = text[i]; 00175 for (uint i = old_row * old_col; i < num; i++) 00176 new_text[i].attr = pen.attr; 00177 delete [] text; 00178 text = new_text; 00179 } 00180 else if (text && (!exists || (old_row != true_row_count) || 00181 (old_col != true_column_count))) 00182 { 00183 delete [] text; 00184 text = NULL; 00185 } 00186 00187 if (!text) 00188 { 00189 uint num = true_row_count * true_column_count; 00190 text = new CC708Character[num]; 00191 pen.column = 0; 00192 pen.row = 0; 00193 for (uint i = 0; i < num; i++) 00194 text[i].attr = pen.attr; 00195 } 00196 00197 exists = true; 00198 changed = true; 00199 } 00200 00201 00202 CC708Window::~CC708Window() 00203 { 00204 QMutexLocker locker(&lock); 00205 00206 exists = false; 00207 true_row_count = 0; 00208 true_column_count = 0; 00209 00210 if (text) 00211 { 00212 delete [] text; 00213 text = NULL; 00214 } 00215 } 00216 00217 void CC708Window::Clear(void) 00218 { 00219 QMutexLocker locker(&lock); 00220 00221 if (!exists || !text) 00222 return; 00223 00224 for (uint i = 0; i < true_row_count * true_column_count; i++) 00225 { 00226 text[i].character = QChar(' '); 00227 text[i].attr = pen.attr; 00228 } 00229 changed = true; 00230 } 00231 00232 CC708Character &CC708Window::GetCCChar(void) const 00233 { 00234 QMutexLocker locker(&lock); 00235 assert(exists); 00236 assert(text); 00237 assert(pen.row < true_row_count); 00238 assert(pen.column < true_column_count); 00239 return text[pen.row * true_column_count + pen.column]; 00240 } 00241 00242 vector<CC708String*> CC708Window::GetStrings(void) const 00243 { 00244 QMutexLocker locker(&lock); 00245 00246 vector<CC708String*> list; 00247 00248 CC708String *cur = NULL; 00249 00250 if (!text) 00251 return list; 00252 00253 for (uint j = 0; j < true_row_count; j++) 00254 { 00255 for (uint i = 0; i < true_column_count; i++) 00256 { 00257 CC708Character &chr = text[j * true_column_count + i]; 00258 if (!cur) 00259 { 00260 cur = new CC708String; 00261 cur->x = i; 00262 cur->y = j; 00263 cur->str = QString("%1").arg(chr.character); 00264 cur->attr = chr.attr; 00265 } 00266 else if (cur->attr == chr.attr) 00267 { 00268 cur->str = QString("%1%2").arg(cur->str).arg(chr.character); 00269 } 00270 else// if (cur->attr != chr.attr) 00271 { 00272 list.push_back(cur); 00273 cur = NULL; 00274 i--; 00275 } 00276 } 00277 if (cur) 00278 { 00279 list.push_back(cur); 00280 cur = NULL; 00281 } 00282 } 00283 return list; 00284 } 00285 00286 void CC708Window::SetWindowStyle(uint style) 00287 { 00288 const uint style2justify[] = 00289 { 00290 k708JustifyLeft, k708JustifyLeft, k708JustifyLeft, k708JustifyCenter, 00291 k708JustifyLeft, k708JustifyLeft, k708JustifyCenter, k708JustifyLeft, 00292 }; 00293 00294 if ((style < 1) || (style > 7)) 00295 return; 00296 00297 fill_color = k708AttrColorBlack; 00298 fill_opacity = ((2 == style) || (5 == style)) ? 00299 k708AttrOpacityTransparent : k708AttrOpacitySolid; 00300 border_color = k708AttrColorBlack; 00301 border_type = k708BorderNone; 00302 scroll_dir = (style < 7) ? k708DirBottomToTop : k708DirRightToLeft; 00303 print_dir = (style < 7) ? k708DirLeftToRight : k708DirTopToBottom; 00304 effect_dir = scroll_dir; 00305 display_effect = k708EffectSnap; 00306 effect_speed = 0; 00307 justify = style2justify[style]; 00308 word_wrap = (style > 3) && (style < 7) ? 1 : 0; 00309 00311 // It appears that ths is missused by broadcasters (FOX -- Dollhouse) 00312 fill_opacity = k708AttrOpacityTransparent; 00314 changed = true; 00315 } 00316 00317 void CC708Window::AddChar(QChar ch) 00318 { 00319 if (!exists) 00320 return; 00321 00322 QString dbg_char = ch; 00323 if (ch.toAscii() < 32) 00324 dbg_char = QString("0x%1").arg( (int)ch.toAscii(), 0,16); 00325 00326 if (!IsPenValid()) 00327 { 00328 LOG(VB_VBI, LOG_INFO, 00329 QString("AddChar(%1) at (c %2, r %3) INVALID win(%4,%5)") 00330 .arg(dbg_char).arg(pen.column).arg(pen.row) 00331 .arg(true_column_count).arg(true_row_count)); 00332 return; 00333 } 00334 00335 if (ch.toAscii() == 0x0D) 00336 { 00337 Scroll(pen.row + 1, 0); 00338 changed = true; 00339 return; 00340 } 00341 00342 if (ch.toAscii() == 0x08) 00343 { 00344 DecrPenLocation(); 00345 GetCCChar().attr = pen.attr; 00346 GetCCChar().character = QChar(' '); 00347 changed = true; 00348 return; 00349 } 00350 00351 GetCCChar().attr = pen.attr; 00352 GetCCChar().character = ch; 00353 int c = pen.column; 00354 int r = pen.row; 00355 IncrPenLocation(); 00356 changed = true; 00357 00358 LOG(VB_VBI, LOG_INFO, QString("AddChar(%1) at (c %2, r %3) -> (%4,%5)") 00359 .arg(dbg_char).arg(c).arg(r).arg(pen.column).arg(pen.row)); 00360 } 00361 00362 void CC708Window::Scroll(int row, int col) 00363 { 00364 QMutexLocker locker(&lock); 00365 00366 if (!true_row_count || !true_column_count) 00367 return; 00368 00369 if (text && (k708DirBottomToTop == scroll_dir) && 00370 (row >= (int)true_row_count)) 00371 { 00372 for (uint j = 0; j < true_row_count - 1; j++) 00373 for (uint i = 0; i < true_column_count; i++) 00374 text[(true_column_count * j) + i] = 00375 text[(true_column_count * (j+1)) + i]; 00376 //uint colsz = true_column_count * sizeof(CC708Character); 00377 //memmove(text, text + colsz, colsz * (true_row_count - 1)); 00378 00379 CC708Character tmp(*this); 00380 for (uint i = 0; i < true_column_count; i++) 00381 text[(true_column_count * (true_row_count - 1)) + i] = tmp; 00382 00383 pen.row = true_row_count - 1; 00384 changed = true; 00385 } 00386 else 00387 { 00388 pen.row = row; 00389 } 00390 // TODO implement other 3 scroll directions... 00391 00392 pen.column = col; 00393 } 00394 00395 void CC708Window::IncrPenLocation(void) 00396 { 00397 // TODO: Scroll direction and up/down printing, 00398 // and word wrap not handled yet... 00399 int new_column = pen.column, new_row = pen.row; 00400 00401 new_column += (print_dir == k708DirLeftToRight) ? +1 : 0; 00402 new_column += (print_dir == k708DirRightToLeft) ? -1 : 0; 00403 new_row += (print_dir == k708DirTopToBottom) ? +1 : 0; 00404 new_row += (print_dir == k708DirBottomToTop) ? -1 : 0; 00405 00406 #if 0 00407 LOG(VB_VBI, LOG_INFO, QString("IncrPen dir%1: (c %2, r %3) -> (%4,%5)") 00408 .arg(print_dir).arg(pen.column).arg(pen.row) 00409 .arg(new_column).arg(new_row)); 00410 #endif 00411 00412 if (k708DirLeftToRight == print_dir || k708DirRightToLeft == print_dir) 00413 { 00414 // basic wrapping for l->r, r->l languages 00415 if (!row_lock && column_lock && (new_column >= (int)true_column_count)) 00416 { 00417 new_column = 0; 00418 new_row += 1; 00419 } 00420 else if (!row_lock && column_lock && (new_column < 0)) 00421 { 00422 new_column = (int)true_column_count - 1; 00423 new_row -= 1; 00424 } 00425 Scroll(new_row, new_column); 00426 } 00427 else 00428 { 00429 pen.column = max(new_column, 0); 00430 pen.row = max(new_row, 0); 00431 } 00432 // TODO implement other 2 scroll directions... 00433 00434 LimitPenLocation(); 00435 } 00436 00437 void CC708Window::DecrPenLocation(void) 00438 { 00439 // TODO: Scroll direction and up/down printing, 00440 // and word wrap not handled yet... 00441 int new_column = pen.column, new_row = pen.row; 00442 00443 new_column -= (print_dir == k708DirLeftToRight) ? +1 : 0; 00444 new_column -= (print_dir == k708DirRightToLeft) ? -1 : 0; 00445 new_row -= (print_dir == k708DirTopToBottom) ? +1 : 0; 00446 new_row -= (print_dir == k708DirBottomToTop) ? -1 : 0; 00447 00448 #if 0 00449 LOG(VB_VBI, LOG_INFO, QString("DecrPen dir%1: (c %2, r %3) -> (%4,%5)") 00450 .arg(print_dir).arg(pen.column).arg(pen.row) 00451 .arg(new_column).arg(new_row)); 00452 #endif 00453 00454 if (k708DirLeftToRight == print_dir || k708DirRightToLeft == print_dir) 00455 { 00456 // basic wrapping for l->r, r->l languages 00457 if (!row_lock && column_lock && (new_column >= (int)true_column_count)) 00458 { 00459 new_column = 0; 00460 new_row += 1; 00461 } 00462 else if (!row_lock && column_lock && (new_column < 0)) 00463 { 00464 new_column = (int)true_column_count - 1; 00465 new_row -= 1; 00466 } 00467 Scroll(new_row, new_column); 00468 } 00469 else 00470 { 00471 pen.column = max(new_column, 0); 00472 pen.row = max(new_row, 0); 00473 } 00474 // TODO implement other 2 scroll directions... 00475 00476 LimitPenLocation(); 00477 } 00478 00479 void CC708Window::SetPenLocation(uint row, uint column) 00480 { 00481 Scroll(row, column); 00482 LimitPenLocation(); 00483 } 00484 00485 void CC708Window::LimitPenLocation(void) 00486 { 00487 // basic limiting 00488 uint max_col = max((int)true_column_count - 1, 0); 00489 uint max_row = max((int)true_row_count - 1, 0); 00490 pen.column = min(pen.column, max_col); 00491 pen.row = min(pen.row, max_row); 00492 } 00493 00494 /***************************************************************************/ 00495 00496 void CC708Pen::SetPenStyle(uint style) 00497 { 00498 static const uint style2font[] = { 0, 0, 1, 2, 3, 4, 3, 4 }; 00499 00500 if ((style < 1) || (style > 7)) 00501 return; 00502 00503 attr.pen_size = k708AttrSizeStandard; 00504 attr.offset = k708AttrOffsetNormal; 00505 attr.font_tag = style2font[style]; 00506 attr.italics = 0; 00507 attr.underline = 0; 00508 attr.boldface = 0; 00509 attr.edge_type = 0; 00510 attr.fg_color = k708AttrColorWhite; 00511 attr.fg_opacity = k708AttrOpacitySolid; 00512 attr.bg_color = k708AttrColorBlack; 00513 attr.bg_opacity = (style<6) ? 00514 k708AttrOpacitySolid : k708AttrOpacityTransparent; 00515 attr.edge_color = k708AttrColorBlack; 00516 attr.override_fg_color = false; 00517 } 00518 00519 CC708Character::CC708Character(const CC708Window &win) 00520 { 00521 attr = win.pen.attr; 00522 character = ' '; 00523 } 00524 00525 bool CC708CharacterAttribute::operator==( 00526 const CC708CharacterAttribute &other) const 00527 { 00528 return ((pen_size == other.pen_size) && 00529 (offset == other.offset) && 00530 (text_tag == other.text_tag) && 00531 (font_tag == other.font_tag) && 00532 (edge_type == other.edge_type) && 00533 (underline == other.underline) && 00534 (italics == other.italics) && 00535 (fg_color == other.fg_color) && 00536 (fg_opacity == other.fg_opacity) && 00537 (bg_color == other.bg_color) && 00538 (bg_opacity == other.bg_opacity) && 00539 (edge_color == other.edge_color)); 00540 } 00541 00542 QColor CC708CharacterAttribute::ConvertToQColor(uint c) 00543 { 00544 // Color is expressed in 6 bits, 2 each for red, green, and blue. 00545 // U.S. ATSC programs seem to use just the higher-order bit, 00546 // i.e. values 0 and 2, so the last two elements of X[] are both 00547 // set to the maximum 255, otherwise font colors are dim. 00548 static int X[] = {0, 96, 255, 255}; 00549 return QColor(X[(c>>4)&3], X[(c>>2)&3], X[c&3]); 00550 }
1.7.6.1