00001 #include <QFontMetrics>
00002 #include <QPainter>
00003
00004 #include "mythlogging.h"
00005 #include "mythfontproperties.h"
00006 #include "mythuitext.h"
00007 #include "mythuishape.h"
00008 #include "vbilut.h"
00009 #include "mythimage.h"
00010 #include "mythuiimage.h"
00011 #include "mythpainter.h"
00012 #include "teletextscreen.h"
00013 #include "subtitlescreen.h"
00014
00015 #define LOC QString("TeletextScreen: ")
00016
00017 const QColor TeletextScreen::kColorBlack = QColor( 0, 0, 0,255);
00018 const QColor TeletextScreen::kColorRed = QColor(255, 0, 0,255);
00019 const QColor TeletextScreen::kColorGreen = QColor( 0,255, 0,255);
00020 const QColor TeletextScreen::kColorYellow = QColor(255,255, 0,255);
00021 const QColor TeletextScreen::kColorBlue = QColor( 0, 0,255,255);
00022 const QColor TeletextScreen::kColorMagenta = QColor(255, 0,255,255);
00023 const QColor TeletextScreen::kColorCyan = QColor( 0,255,255,255);
00024 const QColor TeletextScreen::kColorWhite = QColor(255,255,255,255);
00025 const int TeletextScreen::kTeletextColumns = 40;
00026 const int TeletextScreen::kTeletextRows = 26;
00027
00028 static MythFontProperties* gTTFont;
00029
00030 static char cvt_char(char ch, int lang)
00031 {
00032 int c = 0;
00033 for (int j = 0; j < 14; j++)
00034 {
00035 c = ch & 0x7F;
00036 if (c == lang_chars[0][j])
00037 ch = lang_chars[lang + 1][j];
00038 }
00039 return ch;
00040 }
00041
00042 TeletextScreen::TeletextScreen(MythPlayer *player, const char * name,
00043 int fontStretch) :
00044 MythScreenType((MythScreenType*)NULL, name),
00045 m_player(player), m_teletextReader(NULL),
00046 m_safeArea(QRect()),
00047 m_colWidth(10), m_rowHeight(10),
00048 m_bgColor(QColor(kColorBlack)),
00049 m_displaying(false), m_fontStretch(fontStretch),
00050 m_fontHeight(10)
00051 {
00052 }
00053
00054 TeletextScreen::~TeletextScreen()
00055 {
00056 ClearScreen();
00057 }
00058
00059 bool TeletextScreen::Create(void)
00060 {
00061 if (m_player)
00062 m_teletextReader = m_player->GetTeletextReader();
00063 return m_player && m_teletextReader;
00064 }
00065
00066 void TeletextScreen::ClearScreen(void)
00067 {
00068 DeleteAllChildren();
00069 QHash<int, QImage*>::iterator it = m_rowImages.begin();
00070 for (; it != m_rowImages.end(); ++it)
00071 {
00072 if (*it)
00073 delete (*it);
00074 }
00075 m_rowImages.clear();
00076 SetRedraw();
00077 }
00078
00079 QImage* TeletextScreen::GetRowImage(int row, QRect &rect)
00080 {
00081 int y = row & ~1;
00082 rect.translate(0, -(y * m_rowHeight));
00083 if (!m_rowImages.contains(y))
00084 {
00085 QImage* img = new QImage(m_safeArea.width(), m_rowHeight * 2,
00086 QImage::Format_ARGB32);
00087 if (img)
00088 {
00089 img->fill(0);
00090 m_rowImages.insert(y, img);
00091 }
00092 else
00093 return NULL;
00094 }
00095 return m_rowImages.value(y);
00096 }
00097
00098 void TeletextScreen::OptimiseDisplayedArea(void)
00099 {
00100 VideoOutput *vo = m_player->GetVideoOutput();
00101 if (!vo)
00102 return;
00103 MythPainter *osd_painter = vo->GetOSDPainter();
00104 if (!osd_painter)
00105 return;
00106
00107 QHashIterator<int, QImage*> it(m_rowImages);
00108 while (it.hasNext())
00109 {
00110 it.next();
00111 MythImage* image = osd_painter->GetFormatImage();
00112 if (!image || !it.value())
00113 continue;
00114
00115 int row = it.key();
00116 image->Assign(*(it.value()));
00117 MythUIImage *uiimage = new MythUIImage(this, QString("ttrow%1")
00118 .arg(row));
00119 if (uiimage)
00120 {
00121 uiimage->SetImage(image);
00122 uiimage->SetArea(MythRect(0, row * m_rowHeight,
00123 m_safeArea.width(), m_rowHeight * 2));
00124 }
00125 }
00126
00127 QRegion visible;
00128 QListIterator<MythUIType *> i(m_ChildrenList);
00129 while (i.hasNext())
00130 {
00131 MythUIType *img = i.next();
00132 visible = visible.united(img->GetArea());
00133 }
00134
00135 if (visible.isEmpty())
00136 return;
00137
00138 QRect bounding = visible.boundingRect();
00139 bounding = bounding.translated(m_safeArea.topLeft());
00140 bounding = m_safeArea.intersected(bounding);
00141 int left = m_safeArea.left() - bounding.left();
00142 int top = m_safeArea.top() - bounding.top();
00143 SetArea(MythRect(bounding));
00144
00145 i.toFront();;
00146 while (i.hasNext())
00147 {
00148 MythUIType *img = i.next();
00149 img->SetArea(img->GetArea().translated(left, top));
00150 }
00151 }
00152
00153 void TeletextScreen::Pulse(void)
00154 {
00155 if (!InitialiseFont() || !m_displaying)
00156 return;
00157
00158 if (m_player && m_player->GetVideoOutput())
00159 {
00160 static const float kTextPadding = 0.96f;
00161 QRect oldsafe = m_safeArea;
00162 m_safeArea = m_player->GetVideoOutput()->GetSafeRect();
00163 m_colWidth = (int)((float)m_safeArea.width() / (float)kTeletextColumns);
00164 m_rowHeight = (int)((float)m_safeArea.height() / (float)kTeletextRows);
00165
00166 if (oldsafe != m_safeArea)
00167 {
00168 m_teletextReader->SetPageChanged(true);
00169
00170 int max_width = (int)((float)m_colWidth * kTextPadding);
00171 m_fontHeight = (int)((float)m_rowHeight * kTextPadding);
00172 if (max_width > (m_colWidth - 2))
00173 max_width = m_colWidth -2;
00174 if (m_fontHeight > (m_rowHeight - 2))
00175 m_fontHeight = m_rowHeight - 2;
00176 gTTFont->GetFace()->setPixelSize(m_fontHeight);
00177
00178 m_fontStretch = 200;
00179 bool ok = false;
00180 while (!ok && m_fontStretch > 50)
00181 {
00182 gTTFont->GetFace()->setStretch(m_fontStretch);
00183 QFontMetrics font(*(gTTFont->GetFace()));
00184 if (font.averageCharWidth() <= max_width || m_fontStretch < 50)
00185 ok = true;
00186 else
00187 m_fontStretch -= 10;
00188 }
00189 }
00190 }
00191 else
00192 {
00193 return;
00194 }
00195
00196 if (!m_teletextReader->PageChanged())
00197 return;
00198
00199 ClearScreen();
00200
00201 const TeletextSubPage *ttpage = m_teletextReader->FindSubPage();
00202
00203 if (!ttpage)
00204 {
00205
00206 DrawHeader(NULL, 0);
00207 m_teletextReader->SetPageChanged(false);
00208 OptimiseDisplayedArea();
00209 return;
00210 }
00211
00212 m_teletextReader->SetSubPage(ttpage->subpagenum);
00213
00214 int a = 0;
00215 if ((ttpage->subtitle) ||
00216 (ttpage->flags & (TP_SUPPRESS_HEADER | TP_NEWSFLASH | TP_SUBTITLE)))
00217 {
00218 a = 1;
00219
00220 m_teletextReader->SetShowHeader(false);
00221 m_teletextReader->SetIsSubtitle(true);
00222 }
00223 else
00224 {
00225 m_teletextReader->SetShowHeader(true);
00226 m_teletextReader->SetIsSubtitle(false);
00227 DrawHeader(m_teletextReader->GetHeader(), ttpage->lang);
00228 m_teletextReader->SetHeaderChanged(false);
00229 }
00230
00231 for (int y = kTeletextRows - a; y >= 2; y--)
00232 DrawLine(ttpage->data[y-1], y, ttpage->lang);
00233
00234 m_teletextReader->SetPageChanged(false);
00235 OptimiseDisplayedArea();
00236 }
00237
00238 bool TeletextScreen::KeyPress(const QString &key)
00239 {
00240 if (m_teletextReader)
00241 return m_teletextReader->KeyPress(key);
00242 return false;
00243 }
00244
00245 void TeletextScreen::SetPage(int page, int subpage)
00246 {
00247 if (m_teletextReader)
00248 m_teletextReader->SetPage(page, subpage);
00249 }
00250
00251 void TeletextScreen::SetDisplaying(bool display)
00252 {
00253 m_displaying = display;
00254 if (!m_displaying)
00255 ClearScreen();
00256 }
00257
00258 void TeletextScreen::Reset(void)
00259 {
00260 if (m_teletextReader)
00261 m_teletextReader->Reset();
00262 }
00263
00264 void TeletextScreen::DrawHeader(const uint8_t *page, int lang)
00265 {
00266 if (!m_displaying)
00267 return;
00268
00269 if (page != NULL)
00270 DrawLine(page, 1, lang);
00271
00272 DrawStatus();
00273 }
00274
00275 static QColor ttcolortoqcolor(int ttcolor)
00276 {
00277 QColor color;
00278
00279 switch (ttcolor & ~kTTColorTransparent)
00280 {
00281 case kTTColorBlack: color = TeletextScreen::kColorBlack; break;
00282 case kTTColorRed: color = TeletextScreen::kColorRed; break;
00283 case kTTColorGreen: color = TeletextScreen::kColorGreen; break;
00284 case kTTColorYellow: color = TeletextScreen::kColorYellow; break;
00285 case kTTColorBlue: color = TeletextScreen::kColorBlue; break;
00286 case kTTColorMagenta: color = TeletextScreen::kColorMagenta; break;
00287 case kTTColorCyan: color = TeletextScreen::kColorCyan; break;
00288 case kTTColorWhite: color = TeletextScreen::kColorWhite; break;
00289 }
00290
00291 return color;
00292 }
00293
00294 static QString TTColorToString(int ttcolor)
00295 {
00296 switch (ttcolor & ~kTTColorTransparent)
00297 {
00298 case kTTColorBlack: return "Black";
00299 case kTTColorRed: return "Red";
00300 case kTTColorGreen: return "Green";
00301 case kTTColorYellow: return "Yellow";
00302 case kTTColorBlue: return "Blue";
00303 case kTTColorMagenta: return "Magenta";
00304 case kTTColorCyan: return "Cyan";
00305 case kTTColorWhite: return "White";
00306 default: return "Unknown";
00307 }
00308 }
00309
00310 void TeletextScreen::SetForegroundColor(int ttcolor)
00311 {
00312 LOG(VB_VBI, LOG_DEBUG, QString("SetForegroundColor(%1)")
00313 .arg(TTColorToString(ttcolor)));
00314
00315 gTTFont->SetColor(ttcolortoqcolor(ttcolor));
00316 }
00317
00318 void TeletextScreen::SetBackgroundColor(int ttcolor)
00319 {
00320 LOG(VB_VBI, LOG_DEBUG, QString("SetBackgroundColor(%1)")
00321 .arg(TTColorToString(ttcolor)));
00322
00323 m_bgColor = ttcolortoqcolor(ttcolor);
00324 m_bgColor.setAlpha((ttcolor & kTTColorTransparent) ? 0x00 : 0xff);
00325 }
00326
00327 void TeletextScreen::DrawLine(const uint8_t *page, uint row, int lang)
00328 {
00329 bool mosaic;
00330 bool conceal;
00331 MUNUSED bool seperation;
00332 MUNUSED bool flash;
00333 bool doubleheight;
00334 MUNUSED bool blink;
00335 bool hold;
00336 bool endbox;
00337 bool startbox;
00338
00339 unsigned char last_ch = ' ';
00340 unsigned char ch;
00341
00342 uint fgcolor = kTTColorWhite;
00343 uint bgcolor = kTTColorBlack;
00344 uint newfgcolor = kTTColorWhite;
00345 uint newbgcolor = kTTColorBlack;
00346
00347 if (m_teletextReader->IsSubtitle() || m_teletextReader->IsTransparent())
00348 {
00349 bgcolor = kTTColorTransparent;
00350 newbgcolor = kTTColorTransparent;
00351
00352 bool isBlank = true;
00353 for (uint i = (row == 1 ? 8 : 0); i < (uint) kTeletextColumns; i++)
00354 {
00355 ch = page[i] & 0x7F;
00356 if (ch != ' ')
00357 {
00358 isBlank = false;
00359 break;
00360 }
00361 }
00362
00363 if (isBlank)
00364 return;
00365 }
00366
00367 SetForegroundColor(fgcolor);
00368 SetBackgroundColor(bgcolor);
00369
00370 mosaic = false;
00371 seperation = false;
00372 conceal = false;
00373 flash = false;
00374 doubleheight = false;
00375 blink = false;
00376 hold = false;
00377 endbox = false;
00378 startbox = false;
00379 uint flof_link_count = 0;
00380 uint old_bgcolor = bgcolor;
00381
00382 if (row == 1)
00383 {
00384 for (uint x = 0; x < 8; x++)
00385 DrawBackground(x, 1);
00386 }
00387
00388 for (uint x = (row == 1 ? 8 : 0); x < (uint)kTeletextColumns; ++x)
00389 {
00390 if (startbox)
00391 {
00392 old_bgcolor = bgcolor;
00393 if (kTTColorTransparent & bgcolor)
00394 bgcolor = bgcolor & ~kTTColorTransparent;
00395 startbox = false;
00396 }
00397
00398 if (endbox)
00399 {
00400 bgcolor = old_bgcolor;
00401 endbox = false;
00402 }
00403
00404 SetForegroundColor(fgcolor);
00405 SetBackgroundColor(bgcolor);
00406
00407 ch = page[x] & 0x7F;
00408 switch (ch)
00409 {
00410 case 0x00: case 0x01: case 0x02: case 0x03:
00411 case 0x04: case 0x05: case 0x06: case 0x07:
00412 fgcolor = ch & 7;
00413 mosaic = false;
00414 conceal = false;
00415
00416 flof_link_count += (row == 25) ? 1 : 0;
00417 goto ctrl;
00418 case 0x08:
00419
00420 goto ctrl;
00421 case 0x09:
00422 flash = false;
00423 goto ctrl;
00424 case 0x0a:
00425 endbox = true;
00426 goto ctrl;
00427 case 0x0b:
00428 if (x < kTeletextColumns - 1 && ((page[x + 1] & 0x7F) != 0x0b))
00429 startbox = true;
00430 goto ctrl;
00431 case 0x0c:
00432 doubleheight = false;
00433 goto ctrl;
00434 case 0x0d:
00435 doubleheight = (row < (kTeletextRows-1)) && (x < (kTeletextColumns - 1));
00436 goto ctrl;
00437
00438 case 0x10: case 0x11: case 0x12: case 0x13:
00439 case 0x14: case 0x15: case 0x16: case 0x17:
00440 fgcolor = ch & 7;
00441 mosaic = true;
00442 conceal = false;
00443 goto ctrl;
00444 case 0x18:
00445 conceal = true;
00446 goto ctrl;
00447 case 0x19:
00448 seperation = false;
00449 goto ctrl;
00450 case 0x1a:
00451 seperation = true;
00452 goto ctrl;
00453 case 0x1c:
00454 bgcolor = kTTColorBlack;
00455 goto ctrl;
00456 case 0x1d:
00457 bgcolor = fgcolor;
00458 goto ctrl;
00459 case 0x1e:
00460 hold = true;
00461 goto ctrl;
00462 case 0x1f:
00463 hold = false;
00464 goto ctrl;
00465 case 0x0e:
00466 case 0x0f:
00467 case 0x1b:
00468 ch = ' ';
00469 break;
00470 ctrl:
00471 ch = ' ';
00472 if (hold && mosaic)
00473 ch = last_ch;
00474 break;
00475
00476 default:
00477 if ((ch >= 0x80) && (ch <=0x9f))
00478 ch = ' ';
00479 else
00480 {
00481 if (conceal && !m_teletextReader->RevealHidden())
00482 ch = ' ';
00483 }
00484 break;
00485 }
00486
00487
00488 if (flof_link_count && (flof_link_count <= 6))
00489 {
00490 const TeletextSubPage *ttpage = m_teletextReader->FindSubPage();
00491
00492 if (ttpage)
00493 {
00494 bool has_flof = ttpage->floflink[flof_link_count - 1];
00495 ch = (has_flof) ? ch : ' ';
00496 }
00497 }
00498
00499 newfgcolor = fgcolor;
00500 newbgcolor = bgcolor;
00501
00502 SetForegroundColor(newfgcolor);
00503 SetBackgroundColor(newbgcolor);
00504 if ((row != 0) || (x > 7))
00505 {
00506 if (m_teletextReader->IsTransparent())
00507 SetBackgroundColor(kTTColorTransparent);
00508
00509 DrawBackground(x, row);
00510 if (doubleheight && row < (uint)kTeletextRows)
00511 DrawBackground(x, row + 1);
00512
00513 if ((mosaic) && (ch < 0x40 || ch > 0x5F))
00514 {
00515 SetBackgroundColor(newfgcolor);
00516 DrawMosaic(x, row, ch, doubleheight);
00517 }
00518 else
00519 {
00520 char c2 = cvt_char(ch, lang);
00521 DrawCharacter(x, row, c2, doubleheight);
00522 }
00523 }
00524 }
00525 }
00526
00527 void TeletextScreen::DrawCharacter(int x, int y, QChar ch, int doubleheight)
00528 {
00529 QString line = ch;
00530 if (line == " ")
00531 return;
00532
00533 int row = y;
00534 x *= m_colWidth;
00535 y *= m_rowHeight;
00536 int height = m_rowHeight * (doubleheight ? 2 : 1);
00537 QRect rect(x, y, m_colWidth, height);
00538
00539 if (doubleheight)
00540 {
00541 gTTFont->GetFace()->setPixelSize(m_fontHeight * 2);
00542 gTTFont->GetFace()->setStretch(m_fontStretch / 2);
00543 }
00544
00545 QImage* image = GetRowImage(row, rect);
00546 if (image)
00547 {
00548 QPainter painter(image);
00549 painter.setFont(gTTFont->face());
00550 painter.setPen(gTTFont->color());
00551 painter.drawText(rect, Qt::AlignCenter, line);
00552 painter.end();
00553 }
00554
00555 if (row & 1)
00556 {
00557 row++;
00558 rect = QRect(x, y + m_rowHeight, m_colWidth, height);
00559 rect.translate(0, -m_rowHeight);
00560 image = GetRowImage(row, rect);
00561 if (image)
00562 {
00563 QPainter painter(image);
00564 painter.setFont(gTTFont->face());
00565 painter.setPen(gTTFont->color());
00566 painter.drawText(rect, Qt::AlignCenter, line);
00567 painter.end();
00568 }
00569 }
00570
00571 if (doubleheight)
00572 {
00573 gTTFont->GetFace()->setPixelSize(m_fontHeight);
00574 gTTFont->GetFace()->setStretch(m_fontStretch);
00575 }
00576 }
00577
00578 void TeletextScreen::DrawBackground(int x, int y)
00579 {
00580 int row = y;
00581 x *= m_colWidth;
00582 y *= m_rowHeight;
00583 DrawRect(row, QRect(x, y, m_colWidth, m_rowHeight));
00584 }
00585
00586 void TeletextScreen::DrawRect(int row, QRect rect)
00587 {
00588 QImage* image = GetRowImage(row, rect);
00589 if (!image)
00590 return;
00591
00592 QBrush bgfill = QBrush(m_bgColor, Qt::SolidPattern);
00593 QPainter painter(image);
00594 painter.setBrush(QBrush(bgfill));
00595 painter.setPen(QPen(Qt::NoPen));
00596 painter.drawRect(rect);
00597 painter.end();
00598 }
00599
00600 void TeletextScreen::DrawMosaic(int x, int y, int code, int doubleheight)
00601 {
00602 int row = y;
00603 x *= m_colWidth;
00604 y *= m_rowHeight;
00605
00606 int dx = (int)round(m_colWidth / 2) + 1;
00607 int dy = (int)round(m_rowHeight / 3) + 1;
00608 dy = (doubleheight) ? (2 * dy) : dy;
00609
00610 if (code & 0x10)
00611 DrawRect(row, QRect(x, y + 2*dy, dx, dy));
00612 if (code & 0x40)
00613 DrawRect(row, QRect(x + dx, y + 2*dy, dx, dy));
00614 if (code & 0x01)
00615 DrawRect(row, QRect(x, y, dx, dy));
00616 if (code & 0x02)
00617 DrawRect(row, QRect(x + dx, y, dx, dy));
00618 if (code & 0x04)
00619 DrawRect(row, QRect(x, y + dy, dx, dy));
00620 if (code & 0x08)
00621 DrawRect(row, QRect(x + dx, y + dy, dx, dy));
00622 }
00623
00624 void TeletextScreen::DrawStatus(void)
00625 {
00626 SetForegroundColor(kTTColorWhite);
00627 SetBackgroundColor(kTTColorBlack);
00628
00629 if (!m_teletextReader->IsTransparent())
00630 for (int i = 0; i < 40; ++i)
00631 DrawBackground(i, 0);
00632
00633 DrawCharacter(1, 0, 'P', 0);
00634 DrawCharacter(2, 0, m_teletextReader->GetPageInput(0), 0);
00635 DrawCharacter(3, 0, m_teletextReader->GetPageInput(1), 0);
00636 DrawCharacter(4, 0, m_teletextReader->GetPageInput(2), 0);
00637
00638 const TeletextSubPage *ttpage = m_teletextReader->FindSubPage();
00639
00640 if (!ttpage)
00641 {
00642 QString str = QObject::tr("Page Not Available",
00643 "Requested Teletext page not available");
00644 for (int i = 0; (i < 30) && i < str.length(); i++)
00645 DrawCharacter(i+10, 0, str[i], 0);
00646
00647 return;
00648 }
00649
00650 QString str = m_teletextReader->GetPage();
00651 if (str.isEmpty())
00652 return;
00653
00654 SetForegroundColor(kTTColorWhite);
00655 for (int x = 0; x < 11; x++)
00656 {
00657 if (m_teletextReader->IsTransparent())
00658 SetBackgroundColor(kTTColorTransparent);
00659 else
00660 SetBackgroundColor(kTTColorBlack);
00661
00662 DrawBackground(x * 3 + 7, 0);
00663
00664 if (str[x * 3] == '*')
00665 {
00666 str[x * 3] = ' ';
00667 SetBackgroundColor(kTTColorRed);
00668 }
00669
00670 DrawBackground(x * 3 + 8, 0);
00671 DrawBackground(x * 3 + 9, 0);
00672
00673 DrawCharacter(x * 3 + 7, 0, str[x * 3], 0);
00674 DrawCharacter(x * 3 + 8, 0, str[x * 3 + 1], 0);
00675 DrawCharacter(x * 3 + 9, 0, str[x * 3 + 2], 0);
00676 }
00677 }
00678
00679 bool TeletextScreen::InitialiseFont()
00680 {
00681 static bool initialised = false;
00682
00683 if (initialised)
00684 {
00685 return true;
00686 #if 0
00687 if (gTTFont->face().family() == font)
00688 return true;
00689 delete gTTFont;
00690 #endif // 0
00691 }
00692
00693 MythFontProperties *mythfont = new MythFontProperties();
00694 QString font = SubtitleScreen::GetTeletextFontName();
00695 if (mythfont)
00696 {
00697 QFont newfont(font);
00698 font.detach();
00699 mythfont->SetFace(newfont);
00700 gTTFont = mythfont;
00701 }
00702 else
00703 return false;
00704
00705 initialised = true;
00706 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Loaded main subtitle font '%1'")
00707 .arg(font));
00708 return true;
00709 }