MythTV  0.26-pre
cc708window.cpp
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends