MythTV  0.27pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
cc708window.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 // Copyright (c) 2003-2005, Daniel Kristjansson
3 
4 #include <cassert>
5 #include <algorithm>
6 using namespace std;
7 
8 #include "cc708window.h"
9 #include "mythlogging.h"
10 
11 /************************************************************************
12 
13  FCC Addons to EIA-708.
14 
15  * Decoders must support the standard, large, and small caption sizes
16  and must allow the caption provider to choose a size and allow the
17  viewer to choose an alternative size.
18 
19  * Decoders must support the eight fonts listed in EIA-708. Caption
20  providers may specify 1 of these 8 font styles to be used to write
21  caption text. Decoders must include the ability for consumers to
22  choose among the eight fonts. The decoder must display the font
23  chosen by the caption provider unless the viewer chooses a different
24  font.
25 
26  * Decoders must implement the same 8 character background colors
27  as those that Section 9 requires be implemented for character
28  foreground (white, black, red, green, blue, yellow, magenta and cyan).
29 
30  * Decoders must implement options for altering the appearance of
31  caption character edges.
32 
33  * Decoders must display the color chosen by the caption provider,
34  and must allow viewers to override the foreground and/or background
35  color chosen by the caption provider and select alternate colors.
36 
37  * Decoders must be capable of decoding and processing data for the
38  six standard services, but information from only one service need
39  be displayed at a given time.
40 
41  * Decoders must include an option that permits a viewer to choose a
42  setting that will display captions as intended by the caption
43  provider (a default). Decoders must also include an option that
44  allows a viewer's chosen settings to remain until the viewer
45  chooses to alter these settings, including during periods when
46  the television is turned off.
47 
48  * Cable providers and other multichannel video programming
49  distributors must transmit captions in a format that will be
50  understandable to this decoder circuitry in digital cable
51  television sets when transmitting programming to digital
52  television devices.
53 
54 ******************************************************************************/
55 
60 
61 const uint k708EffectSnap = 0;
62 const uint k708EffectFade = 1;
63 const uint k708EffectWipe = 2;
64 
65 const uint k708BorderNone = 0;
71 
76 
80 
84 
93 
94 extern const uint k708AttrEdgeNone = 0;
95 extern const uint k708AttrEdgeRaised = 1;
96 extern const uint k708AttrEdgeDepressed = 2;
97 extern const uint k708AttrEdgeUniform = 3;
98 extern const uint k708AttrEdgeLeftDropShadow = 4;
99 extern const uint k708AttrEdgeRightDropShadow = 5;
100 
103 
108 
110  : priority(0), visible(0),
111  anchor_point(0), relative_pos(0),
112  anchor_vertical(0), anchor_horizontal(0),
113  row_count(0), column_count(0),
114  row_lock(0), column_lock(0),
115  pen_style(0), window_style(0),
116 
117  fill_color(0), fill_opacity(0),
118  border_color(0), border_type(0),
119  scroll_dir(0), print_dir(0),
120  effect_dir(0), display_effect(0),
121  effect_speed(0),
122  justify(0), word_wrap(0),
123 
124  true_row_count(0), true_column_count(0),
125  text(NULL), exists(false),
126  changed(true), lock(QMutex::Recursive)
127 {
128 }
129 
130 void CC708Window::DefineWindow(int _priority, int _visible,
131  int _anchor_point, int _relative_pos,
132  int _anchor_vertical, int _anchor_horizontal,
133  int _row_count, int _column_count,
134  int _row_lock, int _column_lock,
135  int _pen_style, int _window_style)
136 {
137  // The DefineWindow command may be sent frequently to allow a
138  // caption decoder just tuning in to get in synch quickly.
139  // Usually the row_count and column_count are unchanged, but it is
140  // possible to add or remove rows or columns. Due to the
141  // one-dimensional row-major representation of characters, if the
142  // number of columns is changed, a new array must be created and
143  // the old characters copied in. If only the number of rows
144  // decreases, the array can be left unchanged. If only the number
145  // of rows increases, the old characters can be copied into the
146  // new character array directly without any index translation.
147  QMutexLocker locker(&lock);
148 
149  _row_count++;
150  _column_count++;
151 
152  priority = _priority;
153  visible = _visible;
154  anchor_point = _anchor_point;
155  relative_pos = _relative_pos;
156  anchor_vertical = _anchor_vertical;
157  anchor_horizontal = _anchor_horizontal;
158  row_lock = _row_lock;
159  column_lock = _column_lock;
160 
161  if ((!_pen_style && !exists) || _pen_style)
162  pen.SetPenStyle(_pen_style ? _pen_style : 1);
163 
164  if ((!_window_style && !exists) || _window_style)
165  SetWindowStyle(_window_style ? _window_style : 1);
166 
167  Resize(_row_count, _column_count);
168  row_count = _row_count;
169  column_count = _column_count;
171 
172  exists = true;
173  changed = true;
174 }
175 
176 // Expand the internal array of characters if necessary to accommodate
177 // the current values of row_count and column_count. Any new (space)
178 // characters exposed are given the current pen attributes. At the
179 // end, row_count and column_count are NOT updated.
180 void CC708Window::Resize(uint new_rows, uint new_columns)
181 {
182  if (!exists || text == NULL)
183  {
184  true_row_count = 0;
185  true_column_count = 0;
186  }
187  if (new_rows > true_row_count || new_columns > true_column_count)
188  {
189  // Expand the array if the new size exceeds the current capacity
190  // in either dimension.
191  CC708Character *new_text =
192  new CC708Character[new_rows * new_columns];
193  pen.column = 0;
194  pen.row = 0;
195  uint i, j;
196  for (i = 0; text && i < row_count; ++i)
197  {
198  for (j = 0; j < column_count; ++j)
199  new_text[i * new_columns + j] = text[i * true_column_count + j];
200  for (; j < new_columns; ++j)
201  new_text[i * new_columns + j].attr = pen.attr;
202  }
203  for (; i < new_rows; ++i)
204  for (j = 0; j < new_columns; ++j)
205  new_text[i * new_columns + j].attr = pen.attr;
206 
207  delete [] text;
208  text = new_text;
209  true_row_count = new_rows;
210  true_column_count = new_columns;
211  changed = true;
212  }
213  else if (new_rows > row_count || new_columns > column_count)
214  {
215  // At least one dimension expanded into existing space, so
216  // those newly exposed characters must be cleared.
217  for (uint i = 0; i < row_count; ++i)
218  for (uint j = column_count; j < new_columns; ++j)
219  {
220  text[i * true_column_count + j].character = ' ';
221  text[i * true_column_count + j].attr = pen.attr;
222  }
223  for (uint i = row_count; i < new_rows; ++i)
224  for (uint j = 0; j < new_columns; ++j)
225  {
226  text[i * true_column_count + j].character = ' ';
227  text[i * true_column_count + j].attr = pen.attr;
228  }
229  changed = true;
230  }
231  exists = true;
232 }
233 
234 
236 {
237  QMutexLocker locker(&lock);
238 
239  exists = false;
240  true_row_count = 0;
241  true_column_count = 0;
242 
243  if (text)
244  {
245  delete [] text;
246  text = NULL;
247  }
248 }
249 
251 {
252  QMutexLocker locker(&lock);
253 
254  if (!exists || !text)
255  return;
256 
257  for (uint i = 0; i < true_row_count * true_column_count; i++)
258  {
259  text[i].character = QChar(' ');
260  text[i].attr = pen.attr;
261  }
262  changed = true;
263 }
264 
266 {
267  assert(exists);
268  assert(text);
271  return text[pen.row * true_column_count + pen.column];
272 }
273 
274 vector<CC708String*> CC708Window::GetStrings(void) const
275 {
276  QMutexLocker locker(&lock);
277 
278  vector<CC708String*> list;
279 
280  CC708String *cur = NULL;
281 
282  if (!text)
283  return list;
284 
285  for (uint j = 0; j < row_count; j++)
286  {
287  for (uint i = 0; i < column_count; i++)
288  {
289  CC708Character &chr = text[j * true_column_count + i];
290  if (!cur)
291  {
292  cur = new CC708String;
293  cur->x = i;
294  cur->y = j;
295  cur->str = QString("%1").arg(chr.character);
296  cur->attr = chr.attr;
297  }
298  else if (cur->attr == chr.attr)
299  {
300  cur->str = QString("%1%2").arg(cur->str).arg(chr.character);
301  }
302  else// if (cur->attr != chr.attr)
303  {
304  list.push_back(cur);
305  cur = NULL;
306  i--;
307  }
308  }
309  if (cur)
310  {
311  list.push_back(cur);
312  cur = NULL;
313  }
314  }
315  return list;
316 }
317 
319 {
320  const uint style2justify[] =
321  {
324  };
325 
326  if ((style < 1) || (style > 7))
327  return;
328 
330  fill_opacity = ((2 == style) || (5 == style)) ?
338  effect_speed = 0;
339  justify = style2justify[style];
340  word_wrap = (style > 3) && (style < 7) ? 1 : 0;
341 
343  // It appears that ths is missused by broadcasters (FOX -- Dollhouse)
346  changed = true;
347 }
348 
349 void CC708Window::AddChar(QChar ch)
350 {
351  if (!exists)
352  return;
353 
354  QString dbg_char = ch;
355  if (ch.toLatin1() < 32)
356  dbg_char = QString("0x%1").arg( (int)ch.toLatin1(), 0,16);
357 
358  if (!IsPenValid())
359  {
360  LOG(VB_VBI, LOG_INFO,
361  QString("AddChar(%1) at (c %2, r %3) INVALID win(%4,%5)")
362  .arg(dbg_char).arg(pen.column).arg(pen.row)
363  .arg(true_column_count).arg(true_row_count));
364  return;
365  }
366 
367  if (ch.toLatin1() == 0x0D)
368  {
369  Scroll(pen.row + 1, 0);
370  changed = true;
371  return;
372  }
373  QMutexLocker locker(&lock);
374 
375  if (ch.toLatin1() == 0x08)
376  {
377  DecrPenLocation();
378  CC708Character& chr = GetCCChar();
379  chr.attr = pen.attr;
380  chr.character = QChar(' ');
381  changed = true;
382  return;
383  }
384 
385  CC708Character& chr = GetCCChar();
386  chr.attr = pen.attr;
387  chr.character = ch;
388  int c = pen.column;
389  int r = pen.row;
390  IncrPenLocation();
391  changed = true;
392 
393  LOG(VB_VBI, LOG_INFO, QString("AddChar(%1) at (c %2, r %3) -> (%4,%5)")
394  .arg(dbg_char).arg(c).arg(r).arg(pen.column).arg(pen.row));
395 }
396 
397 void CC708Window::Scroll(int row, int col)
398 {
399  QMutexLocker locker(&lock);
400 
402  return;
403 
404  if (text && (k708DirBottomToTop == scroll_dir) &&
405  (row >= (int)true_row_count))
406  {
407  for (uint j = 0; j < true_row_count - 1; j++)
408  for (uint i = 0; i < true_column_count; i++)
409  text[(true_column_count * j) + i] =
410  text[(true_column_count * (j+1)) + i];
411  //uint colsz = true_column_count * sizeof(CC708Character);
412  //memmove(text, text + colsz, colsz * (true_row_count - 1));
413 
414  CC708Character tmp(*this);
415  for (uint i = 0; i < true_column_count; i++)
416  text[(true_column_count * (true_row_count - 1)) + i] = tmp;
417 
418  pen.row = true_row_count - 1;
419  changed = true;
420  }
421  else
422  {
423  pen.row = row;
424  }
425  // TODO implement other 3 scroll directions...
426 
427  pen.column = col;
428 }
429 
431 {
432  // TODO: Scroll direction and up/down printing,
433  // and word wrap not handled yet...
434  int new_column = pen.column, new_row = pen.row;
435 
436  new_column += (print_dir == k708DirLeftToRight) ? +1 : 0;
437  new_column += (print_dir == k708DirRightToLeft) ? -1 : 0;
438  new_row += (print_dir == k708DirTopToBottom) ? +1 : 0;
439  new_row += (print_dir == k708DirBottomToTop) ? -1 : 0;
440 
441 #if 0
442  LOG(VB_VBI, LOG_INFO, QString("IncrPen dir%1: (c %2, r %3) -> (%4,%5)")
443  .arg(print_dir).arg(pen.column).arg(pen.row)
444  .arg(new_column).arg(new_row));
445 #endif
446 
448  {
449  // basic wrapping for l->r, r->l languages
450  if (!row_lock && column_lock && (new_column >= (int)true_column_count))
451  {
452  new_column = 0;
453  new_row += 1;
454  }
455  else if (!row_lock && column_lock && (new_column < 0))
456  {
457  new_column = (int)true_column_count - 1;
458  new_row -= 1;
459  }
460  Scroll(new_row, new_column);
461  }
462  else
463  {
464  pen.column = max(new_column, 0);
465  pen.row = max(new_row, 0);
466  }
467  // TODO implement other 2 scroll directions...
468 
470 }
471 
473 {
474  // TODO: Scroll direction and up/down printing,
475  // and word wrap not handled yet...
476  int new_column = pen.column, new_row = pen.row;
477 
478  new_column -= (print_dir == k708DirLeftToRight) ? +1 : 0;
479  new_column -= (print_dir == k708DirRightToLeft) ? -1 : 0;
480  new_row -= (print_dir == k708DirTopToBottom) ? +1 : 0;
481  new_row -= (print_dir == k708DirBottomToTop) ? -1 : 0;
482 
483 #if 0
484  LOG(VB_VBI, LOG_INFO, QString("DecrPen dir%1: (c %2, r %3) -> (%4,%5)")
485  .arg(print_dir).arg(pen.column).arg(pen.row)
486  .arg(new_column).arg(new_row));
487 #endif
488 
490  {
491  // basic wrapping for l->r, r->l languages
492  if (!row_lock && column_lock && (new_column >= (int)true_column_count))
493  {
494  new_column = 0;
495  new_row += 1;
496  }
497  else if (!row_lock && column_lock && (new_column < 0))
498  {
499  new_column = (int)true_column_count - 1;
500  new_row -= 1;
501  }
502  Scroll(new_row, new_column);
503  }
504  else
505  {
506  pen.column = max(new_column, 0);
507  pen.row = max(new_row, 0);
508  }
509  // TODO implement other 2 scroll directions...
510 
512 }
513 
515 {
516  Scroll(row, column);
518 }
519 
521 {
522  // basic limiting
523  uint max_col = max((int)true_column_count - 1, 0);
524  uint max_row = max((int)true_row_count - 1, 0);
525  pen.column = min(pen.column, max_col);
526  pen.row = min(pen.row, max_row);
527 }
528 
529 /***************************************************************************/
530 
532 {
533  static const uint style2font[] = { 0, 0, 1, 2, 3, 4, 3, 4 };
534 
535  if ((style < 1) || (style > 7))
536  return;
537 
540  attr.font_tag = style2font[style];
541  attr.italics = 0;
542  attr.underline = 0;
543  attr.boldface = 0;
544  attr.edge_type = 0;
548  attr.bg_opacity = (style<6) ?
551  attr.actual_fg_color = QColor();
552 }
553 
555  : attr(win.pen.attr), character(' ')
556 {
557 }
558 
560  const CC708CharacterAttribute &other) const
561 {
562  return ((pen_size == other.pen_size) &&
563  (offset == other.offset) &&
564  (text_tag == other.text_tag) &&
565  (font_tag == other.font_tag) &&
566  (edge_type == other.edge_type) &&
567  (underline == other.underline) &&
568  (italics == other.italics) &&
569  (fg_color == other.fg_color) &&
570  (fg_opacity == other.fg_opacity) &&
571  (bg_color == other.bg_color) &&
572  (bg_opacity == other.bg_opacity) &&
573  (edge_color == other.edge_color));
574 }
575 
577 {
578  // Color is expressed in 6 bits, 2 each for red, green, and blue.
579  // U.S. ATSC programs seem to use just the higher-order bit,
580  // i.e. values 0 and 2, so the last two elements of X[] are both
581  // set to the maximum 255, otherwise font colors are dim.
582  static int X[] = {0, 96, 255, 255};
583  return QColor(X[(c>>4)&3], X[(c>>2)&3], X[c&3]);
584 }