Add some Cea608

This commit is contained in:
niksedk 2023-10-29 09:39:23 +01:00
parent fb5d0380b4
commit 2769d3c2f9
13 changed files with 1378 additions and 0 deletions

View File

@ -0,0 +1,186 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Nikse.SubtitleEdit.Core.Cea608
{
public class CaptionScreen
{
public CcRow[] Rows = {
new CcRow(),
new CcRow(),
new CcRow(),
new CcRow(),
new CcRow(),
new CcRow(),
new CcRow(),
new CcRow(),
new CcRow(),
new CcRow(),
new CcRow(),
new CcRow(),
new CcRow(),
new CcRow(),
new CcRow(),
};
public int CurrentRow { get; set; } = Constants.ScreenRowCount - 1;
public int? NumberOfRollUpRows { get; set; }
public CaptionScreen()
{
Reset();
}
public SerializedRow[] Serialize()
{
var results = new List<SerializedRow>();
for (var i = 0; i < Constants.ScreenRowCount; i++)
{
var row = Rows[i];
if (row.IsEmpty())
{
continue;
}
results.Add(new SerializedRow
{
Row = i,
Position = row.FirstNonEmpty(),
Style = row.CurrentPenState.Serialize(),
Columns = row.Chars.Select(SerializeChar).ToArray()
});
}
return results.ToArray();
}
private SerializedStyledUnicodeChar SerializeChar(StyledUnicodeChar character)
{
return new SerializedStyledUnicodeChar
{
Character = character.Uchar,
Style = character.PenState.Serialize(),
};
}
public void Reset()
{
for (var i = 0; i < Constants.ScreenRowCount; i++)
{
Rows[i].Clear();
}
CurrentRow = Constants.ScreenRowCount - 1;
}
public bool Equals(CaptionScreen other)
{
var equal = true;
for (var i = 0; i < Constants.ScreenRowCount; i++)
{
if (!Rows[i].Equals(other.Rows[i]))
{
equal = false;
break;
}
}
return equal;
}
public void Copy(CaptionScreen other)
{
for (var i = 0; i < Constants.ScreenRowCount; i++)
{
Rows[i].Copy(other.Rows[i]);
}
}
public bool IsEmpty()
{
var empty = true;
for (var i = 0; i < Constants.ScreenRowCount; i++)
{
if (!Rows[i].IsEmpty())
{
empty = false;
break;
}
}
return empty;
}
public void BackSpace()
{
Rows[CurrentRow].BackSpace();
}
public void ClearToEndOfRow()
{
Rows[CurrentRow].ClearToEndOfRow();
}
public void InsertChar(int character)
{
Rows[CurrentRow].InsertChar(character);
}
public void SetPen(SerializedPenState styles)
{
Rows[CurrentRow].SetPenStyles(styles);
}
public void MoveCursor(int relPos)
{
Rows[CurrentRow].MoveCursor(relPos);
}
public void SetPac(PacData pacData)
{
var newRow = pacData.Row - 1;
CurrentRow = newRow;
var row = Rows[CurrentRow];
if (pacData.Indent != null)
{
var indent = pacData.Indent;
var prevPos = Math.Max(indent.Value - 1, 0);
row.Position = pacData.Indent.Value;
pacData.Color = row.Chars[prevPos].PenState.Foreground;
}
SetPen(new SerializedPenState
{
Foreground = pacData.Color ?? Constants.ColorWhite,
Underline = pacData.Underline,
Italics = pacData.Italics ?? false,
Background = Constants.ColorBlack,
Flash = false,
});
}
public void SetBkgData(SerializedPenState bkgData)
{
BackSpace();
SetPen(bkgData);
InsertChar(0x20); // Space
}
public void SetRollUpRows(int nrRows)
{
NumberOfRollUpRows = nrRows;
}
public void RollUp()
{
// if the row is empty we have nothing to roll-up
if (NumberOfRollUpRows == null || Rows[CurrentRow].IsEmpty())
{
return;
}
var rows = Rows.ToList();
rows.RemoveAt(CurrentRow - NumberOfRollUpRows.Value + 1);
rows.Add(new CcRow());
Rows = rows.ToArray();
}
}
}

View File

@ -0,0 +1,289 @@
using System;
using System.Collections.Generic;
namespace Nikse.SubtitleEdit.Core.Cea608
{
public class CcDataC608Parser
{
public delegate void DisplayScreenDelegate(DataOutput data);
public Cea608Channel[] Channels { get; set; }
public int? CurrentChannelNumber { get; set; }
public int? LastTime { get; set; }
private int? _lastCmdA;
private int? _lastCmdB;
public DisplayScreenDelegate DisplayScreen { get; set; }
public CcDataC608Parser()
{
Channels = new[]
{
new Cea608Channel(1, this),
new Cea608Channel(2, this),
};
CurrentChannelNumber = -1;
}
public void AddData(int t, int[] byteList)
{
LastTime = t;
for (var i = 0; i < byteList.Length; i += 2)
{
var a = byteList[i] & 0x7f;
var b = byteList[i + 1] & 0x7f;
if (a == 0 && b == 0)
{
continue;
}
if (!(ParseCmd(a, b) ||
ParseMidRow(a, b) ||
ParsePac(a, b) ||
ParseBackgroundAttributes(a, b)))
{
ParseCharacters(a, b);
}
}
}
private void ParseCharacters(int ccData1, int ccData2)
{
var charsFound = ParseChars(ccData1, ccData2);
if (charsFound.Length > 0)
{
if (CurrentChannelNumber != null && CurrentChannelNumber >= 0)
{
var channel = Channels[CurrentChannelNumber.Value - 1];
channel.InsertChars(charsFound);
}
else
{
System.Diagnostics.Debug.WriteLine("No channel found yet. TEXT-MODE?");
}
}
}
public int[] ParseChars(int a, int b)
{
var charCodes = new List<int>();
int charCode1;
if (a >= 0x19)
{
charCode1 = a - 8;
}
else
{
charCode1 = a;
}
if (0x11 <= charCode1 && charCode1 <= 0x13)
{
// Special character
int oneCode;
if (charCode1 == 0x11)
{
oneCode = b + 0x50;
}
else if (charCode1 == 0x12)
{
oneCode = b + 0x70;
}
else
{
oneCode = b + 0x90;
}
charCodes.Add(oneCode);
}
else if (0x20 <= a && a <= 0x7f)
{
charCodes.Add(a);
if (b > 0)
{
charCodes.Add(b);
}
}
return charCodes.ToArray();
}
private static bool HasCmd(int ccData1, int ccData2)
{
return ((ccData1 == 0x14 || ccData1 == 0x1C) && (0x20 <= ccData2 && ccData2 <= 0x2F)) ||
((ccData1 == 0x17 || ccData1 == 0x1F) && (0x21 <= ccData2 && ccData2 <= 0x23));
}
public bool ParseCmd(int a, int b)
{
if (HasCmd(a, b))
{
// Duplicate CMD commands get skipped once
if (_lastCmdA == a && _lastCmdB == b)
{
_lastCmdA = null;
_lastCmdB = null;
return true;
}
int chNr;
if (a == 0x14 || a == 0x17)
{
chNr = 1;
}
else
{
chNr = 2; // (a == 0x1C || a== 0x1f)
}
Channels[chNr - 1].RunCmd(a, b);
CurrentChannelNumber = chNr;
_lastCmdA = a;
_lastCmdB = b;
return true;
}
return false;
}
public bool ParseMidRow(int a, int b)
{
if (((a == 0x11) || (a == 0x19)) && 0x20 <= b && b <= 0x2f)
{
int chNr;
if (a == 0x11)
{
chNr = 1;
}
else
{
chNr = 2;
}
var channel = Channels[chNr - 1];
channel.cc_MidRow(b);
return true;
}
return false;
}
private static bool HasPac(int ccData1, int ccData2)
{
return (((0x11 <= ccData1 && ccData1 <= 0x17) || (0x19 <= ccData1 && ccData1 <= 0x1F)) && (0x40 <= ccData2 && ccData2 <= 0x7F)) ||
((ccData1 == 0x10 || ccData1 == 0x18) && (0x40 <= ccData2 && ccData2 <= 0x5F));
}
public bool ParsePac(int a, int b)
{
if (HasPac(a, b))
{
var chNr = (a <= 0x17) ? 1 : 2;
int row;
if (0x40 <= b && b <= 0x5F)
{
row = (chNr == 1) ? Constants.Channel1RowsMap[a] : Constants.Channel2RowsMap[a];
}
else
{ // 0x60 <= b <= 0x7F
row = (chNr == 1) ? (Constants.Channel1RowsMap[a] + 1) : (Constants.Channel2RowsMap[a] + 1);
}
var pacData = InterpretPac(row, b);
var channel = Channels[chNr - 1];
channel.SetPac(pacData);
CurrentChannelNumber = chNr;
return true;
}
return false;
}
public PacData InterpretPac(int row, int b)
{
var pacData = new PacData
{
Color = null,
Italics = false,
Indent = null,
Underline = false,
Row = row,
};
int pacIndex;
if (b > 0x5F)
{
pacIndex = b - 0x60;
}
else
{
pacIndex = b - 0x40;
}
pacData.Underline = (pacIndex & 1) == 1;
if (pacIndex <= 0xd)
{
pacData.Color = Constants.PacDataColors[(int)Math.Floor(pacIndex / 2.0)];
}
else if (pacIndex <= 0xf)
{
pacData.Italics = true;
pacData.Color = Constants.ColorWhite;
}
else
{
pacData.Indent = (int)((Math.Floor((pacIndex - 0x10) / 2.0)) * 4);
}
return pacData;
}
public bool ParseBackgroundAttributes(int a, int b)
{
var bkgData = new SerializedPenState();
if (!HasBackgroundAttributes(a, b))
{
return false;
}
if (a == 0x10 || a == 0x18)
{
var index = (int)Math.Round(Math.Floor((b - 0x20) / 2.0));
bkgData.Background = Constants.PacDataColors[index];
if (b % 2 == 1)
{
bkgData.Background += "_semi";
}
}
else if (b == 0x2d)
{
bkgData.Background = Constants.ColorTransparent;
}
else
{
bkgData.Foreground = Constants.ColorBlack;
if (b == 0x2f)
{
bkgData.Underline = true;
}
}
var chNr = (a < 0x18) ? 1 : 2;
var channel = Channels[chNr - 1];
channel.SetBkgData(bkgData);
return true;
}
private static bool HasBackgroundAttributes(int ccData1, int ccData2)
{
return (((ccData1 == 0x10 || ccData1 == 0x18) && (0x20 <= ccData2 && ccData2 <= 0x2f)) ||
((ccData1 == 0x17 || ccData1 == 0x1f) && (0x2d <= ccData2 && ccData2 <= 0x2f)));
}
}
}

167
src/libse/Cea608/CcRow.cs Normal file
View File

@ -0,0 +1,167 @@
namespace Nikse.SubtitleEdit.Core.Cea608
{
public class CcRow
{
public int Position { get; set; }
public PenState CurrentPenState = new PenState();
public StyledUnicodeChar[] Chars { get; } =
{
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
new StyledUnicodeChar(),
};
public bool Equals(CcRow other)
{
for (var i = 0; i < Constants.ScreenColCount; i++)
{
if (!Chars[i].Equals(other.Chars[i]))
{
if (!Chars[i].Equals(other.Chars[i]))
{
return false;
}
}
}
return true;
}
public void Copy(CcRow other)
{
for (var i = 0; i < Constants.ScreenColCount; i++)
{
Chars[i].Copy(other.Chars[i]);
}
}
public int FirstNonEmpty()
{
for (var i = 0; i < Constants.ScreenColCount; i++)
{
if (!Chars[i].IsEmpty())
{
return i;
}
}
return -1;
}
public bool IsEmpty()
{
for (var i = 0; i < Constants.ScreenColCount; i++)
{
if (!Chars[i].IsEmpty())
{
return false;
}
}
return true;
}
public void MoveCursor(int relPos)
{
var newPos = Position + relPos;
if (relPos > 1)
{
for (var i = Position + 1; i < newPos + 1; i++)
{
Chars[i].SetPenState(CurrentPenState);
}
}
Position = newPos;
}
public void BackSpace()
{
MoveCursor(-1);
Chars[Position].SetChar(Constants.EmptyChar, CurrentPenState);
}
public void InsertChar(int b)
{
if (b >= 0x90)
{ // Extended char
BackSpace();
}
var ch = GetCharForByte(b);
Chars[Position].SetChar(ch, CurrentPenState);
MoveCursor(1);
}
/// <summary>
/// Get Unicode Character from CEA-608 byte code.
/// </summary>
public static string GetCharForByte(int byteValue)
{
if (Constants.ExtendedCharCodes.TryGetValue(byteValue, out var v))
{
return char.ConvertFromUtf32(v);
}
return char.ConvertFromUtf32(byteValue);
}
public void ClearFromPos(int startPos)
{
for (var i = startPos; i < Constants.ScreenColCount; i++)
{
Chars[i].Reset();
}
}
public void Clear()
{
ClearFromPos(0);
Position = 0;
CurrentPenState.Reset();
}
public void ClearToEndOfRow()
{
ClearFromPos(Position);
}
public void SetPenStyles(SerializedPenState styles)
{
CurrentPenState.SetStyles(styles);
var currentChar = Chars[Position];
currentChar.SetPenState(CurrentPenState);
}
}
}

View File

@ -0,0 +1,11 @@
namespace Nikse.SubtitleEdit.Core.Cea608
{
public class CcStyle
{
public string Foreground { get; set; }
public bool? Underline { get; set; }
public bool? Italics { get; set; }
public string Background { get; set; }
public bool? Flash { get; set; }
}
}

View File

@ -0,0 +1,365 @@
using System;
namespace Nikse.SubtitleEdit.Core.Cea608
{
public class Cea608Channel
{
public CaptionScreen DisplayedMemory { get; set; }
public CaptionScreen NonDisplayedMemory { get; set; }
public CaptionScreen LastOutputScreen { get; set; }
public CaptionScreen WriteScreen { get; set; }
public CcRow CurrentRollUpRow { get; set; }
public int? CueStartTime { get; set; }
public string Mode { get; set; }
public int ChannelNumber { get; set; }
public CcDataC608Parser Parser { get; set; }
public Cea608Channel()
{
DisplayedMemory = new CaptionScreen();
NonDisplayedMemory = new CaptionScreen();
LastOutputScreen = new CaptionScreen();
}
public Cea608Channel(int chNr, CcDataC608Parser parser)
{
Parser = parser;
ChannelNumber = chNr;
DisplayedMemory = new CaptionScreen();
NonDisplayedMemory = new CaptionScreen();
LastOutputScreen = new CaptionScreen();
CurrentRollUpRow = DisplayedMemory.Rows[Constants.ScreenRowCount - 1];
WriteScreen = DisplayedMemory;
CueStartTime = null;
}
private void CmdMap(int cmd)
{
switch (cmd)
{
case 0x20:
cc_RCL();
break;
case 0x21:
cc_BS();
break;
case 0x22:
cc_AOF();
break;
case 0x23:
cc_AON();
break;
case 0x24:
cc_DER();
break;
case 0x25:
cc_RU(2);
break;
case 0x26:
cc_RU(3);
break;
case 0x27:
cc_RU(4);
break;
case 0x28:
cc_FON();
break;
case 0x29:
cc_RDC();
break;
case 0x2A:
cc_TR();
break;
case 0x2B:
cc_RTD();
break;
case 0x2C:
cc_EDM();
break;
case 0x2D:
cc_CR();
break;
case 0x2E:
cc_ENM();
break;
case 0x2F:
cc_EOC();
break;
default:
Console.WriteLine("Command not found");
break;
}
}
public void Reset()
{
Mode = null;
DisplayedMemory.Reset();
NonDisplayedMemory.Reset();
LastOutputScreen.Reset();
CurrentRollUpRow = DisplayedMemory.Rows[Constants.ScreenRowCount - 1];
WriteScreen = DisplayedMemory;
CueStartTime = null;
}
public void SetPac(PacData pacData)
{
WriteScreen.SetPac(pacData);
}
public void RunCmd(int ccData1, int ccData2)
{
if (ccData1 == 0x14 || ccData1 == 0x1C)
{
CmdMap(ccData2);
}
else
{ // a == 0x17 || a == 0x1F
cc_TO(ccData2 - 0x20);
}
}
public void SetBkgData(SerializedPenState bkgData)
{
WriteScreen.SetBkgData(bkgData);
}
public void SetMode(string newMode)
{
if (newMode == Mode)
{
return;
}
Mode = newMode;
if (Mode == "MODE_POP-ON")
{
WriteScreen = NonDisplayedMemory;
}
else
{
WriteScreen = DisplayedMemory;
WriteScreen.Reset();
}
if (Mode != "MODE_ROLL-UP")
{
DisplayedMemory.NumberOfRollUpRows = null;
NonDisplayedMemory.NumberOfRollUpRows = null;
}
Mode = newMode;
}
public void InsertChars(int[] chars)
{
foreach (var ch in chars)
{
WriteScreen.InsertChar(ch);
}
if (Mode == "MODE_PAINT-ON" || Mode == "MODE_ROLL-UP")
{
OutputDataUpdate();
}
}
/// <summary>
/// Resume Caption Loading (switch mode to Pop On).
/// </summary>
public void cc_RCL()
{
SetMode("MODE_POP-ON");
}
/// <summary>
/// BackSpace.
/// </summary>
public void cc_BS()
{
if (Mode == "MODE_TEXT")
{
return;
}
WriteScreen.BackSpace();
if (WriteScreen == DisplayedMemory)
{
OutputDataUpdate();
}
}
/// <summary>
/// Reserved (formerly Alarm Off).
/// </summary>
public void cc_AOF()
{
}
/// <summary>
/// Reserved (formerly Alarm On).
/// </summary>
public void cc_AON()
{
}
/// <summary>
/// Delete to End of Row.
/// </summary>
public void cc_DER()
{
WriteScreen.ClearToEndOfRow();
OutputDataUpdate();
}
/// <summary>
/// Roll-Up Captions-2,3,or 4 Rows.
/// </summary>
public void cc_RU(int nrRows)
{
WriteScreen = DisplayedMemory;
SetMode("MODE_ROLL-UP");
WriteScreen.SetRollUpRows(nrRows);
}
/// <summary>
/// Flash On.
/// </summary>
public void cc_FON()
{
WriteScreen.SetPen(new PacData { Flash = true });
}
/// <summary>
/// Resume Direct Captioning (switch mode to PaintOn).
/// </summary>
public void cc_RDC()
{
SetMode("MODE_PAINT-ON");
}
/// <summary>
/// Text Restart in text mode (not supported, however).
/// </summary>
public void cc_TR()
{
SetMode("MODE_TEXT");
}
/// <summary>
/// Resume Text Display in Text mode (not supported, however)
/// </summary>
public void cc_RTD()
{
SetMode("MODE_TEXT");
}
/// <summary>
/// Erase Displayed Memory.
/// </summary>
public void cc_EDM()
{
DisplayedMemory.Reset();
OutputDataUpdate();
}
/// <summary>
/// Carriage Return.
/// </summary>
public void cc_CR()
{
WriteScreen.RollUp();
OutputDataUpdate(true);
}
/// <summary>
/// Erase Non-Displayed Memory.
/// </summary>
public void cc_ENM()
{
NonDisplayedMemory.Reset();
}
/// <summary>
/// End of Caption (Flip Memories).
/// </summary>
public void cc_EOC()
{
if (Mode == "MODE_POP-ON")
{
(DisplayedMemory, NonDisplayedMemory) = (NonDisplayedMemory, DisplayedMemory);
WriteScreen = NonDisplayedMemory;
}
OutputDataUpdate();
}
/// <summary>
/// Tab Offset 1,2, or 3 columns
/// </summary>
public void cc_TO(int nrCols)
{
WriteScreen.MoveCursor(nrCols);
}
/// <summary>
/// Parse MIDROW command
/// </summary>
public void cc_MidRow(int secondByte)
{
//
var styles = new SerializedPenState { Flash = false };
styles.Underline = secondByte % 2 == 1;
styles.Italics = secondByte >= 0x2e;
if (!styles.Italics.Value)
{
var colorIndex = (int)Math.Floor(secondByte / 2.0) - 0x10;
styles.Foreground = Constants.PacDataColors[colorIndex];
}
else
{
styles.Foreground = Constants.ColorWhite;
}
WriteScreen.SetPen(styles);
}
public void OutputDataUpdate(bool rolling = false)
{
var t = Parser.LastTime;
if (t == null)
{
return;
}
if (CueStartTime == null && !DisplayedMemory.IsEmpty())
{ // Start of a new cue
CueStartTime = t;
}
else if (!DisplayedMemory.Equals(LastOutputScreen))
{
if (CueStartTime != null && !LastOutputScreen.IsEmpty())
{
Parser.DisplayScreen?.Invoke(new DataOutput
{
Channel = ChannelNumber,
Roll = rolling,
Start = CueStartTime ?? 0,
End = t ?? 0,
Screen = LastOutputScreen.Serialize(),
});
}
CueStartTime = DisplayedMemory.IsEmpty() ? null : t;
}
LastOutputScreen.Copy(DisplayedMemory);
}
}
}

View File

@ -0,0 +1,161 @@
using System.Collections.Generic;
namespace Nikse.SubtitleEdit.Core.Cea608
{
public static class Constants
{
public const string ColorWhite = "white";
public const string ColorGreen = "green";
public const string ColorBlue = "blue";
public const string ColorCyan = "cyan";
public const string ColorRed = "red";
public const string ColorYellow = "yellow";
public const string ColorMagenta = "magenta";
public const string ColorBlack = "black";
public const string ColorTransparent = "transparent";
public const string EmptyChar = " ";
public static string[] PacDataColors = new string[]
{
ColorWhite,
ColorGreen,
ColorBlue,
ColorCyan,
ColorRed,
ColorYellow,
ColorMagenta,
ColorBlack,
ColorTransparent
};
public const int ScreenRowCount = 15;
public const int ScreenColCount = 32;
public static Dictionary<int, int> ExtendedCharCodes = new Dictionary<int, int>
{
{ 0x2a, 0xe1 }, // lowercase a, acute accent
{ 0x5c, 0xe9 }, // lowercase e, acute accent
{ 0x5e, 0xed }, // lowercase i, acute accent
{ 0x5f, 0xf3 }, // lowercase o, acute accent
{ 0x60, 0xfa }, // lowercase u, acute accent
{ 0x7b, 0xe7 }, // lowercase c with cedilla
{ 0x7c, 0xf7 }, // division symbol
{ 0x7d, 0xd1 }, // uppercase N tilde
{ 0x7e, 0xf1 }, // lowercase n tilde
{ 0x7f, 0x2588 }, // Full block
// THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
// THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F
// THIS MEANS THAT \x50 MUST BE ADDED TO THE VALUES
{ 0x80, 0xae }, // Registered symbol (R)
{ 0x81, 0xb0 }, // degree sign
{ 0x82, 0xbd }, // 1/2 symbol
{ 0x83, 0xbf }, // Inverted (open) question mark
{ 0x84, 0x2122 }, // Trademark symbol (TM)
{ 0x85, 0xa2 }, // Cents symbol
{ 0x86, 0xa3 }, // Pounds sterling
{ 0x87, 0x266a }, // Music 8'th note
{ 0x88, 0xe0 }, // lowercase a, grave accent
{ 0x89, 0x20 }, // transparent space (regular)
{ 0x8a, 0xe8 }, // lowercase e, grave accent
{ 0x8b, 0xe2 }, // lowercase a, circumflex accent
{ 0x8c, 0xea }, // lowercase e, circumflex accent
{ 0x8d, 0xee }, // lowercase i, circumflex accent
{ 0x8e, 0xf4 }, // lowercase o, circumflex accent
{ 0x8f, 0xfb }, // lowercase u, circumflex accent
// THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
// THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F
{ 0x90, 0xc1 }, // capital letter A with acute
{ 0x91, 0xc9 }, // capital letter E with acute
{ 0x92, 0xd3 }, // capital letter O with acute
{ 0x93, 0xda }, // capital letter U with acute
{ 0x94, 0xdc }, // capital letter U with diaresis
{ 0x95, 0xfc }, // lowercase letter U with diaeresis
{ 0x96, 0x2018 }, // opening single quote
{ 0x97, 0xa1 }, // inverted exclamation mark
{ 0x98, 0x2a }, // asterisk
{ 0x99, 0x2019 }, // closing single quote
{ 0x9a, 0x2501 }, // box drawings heavy horizontal
{ 0x9b, 0xa9 }, // copyright sign
{ 0x9c, 0x2120 }, // Service mark
{ 0x9d, 0x2022 }, // (round) bullet
{ 0x9e, 0x201c }, // Left double quotation mark
{ 0x9f, 0x201d }, // Right double quotation mark
{ 0xa0, 0xc0 }, // uppercase A, grave accent
{ 0xa1, 0xc2 }, // uppercase A, circumflex
{ 0xa2, 0xc7 }, // uppercase C with cedilla
{ 0xa3, 0xc8 }, // uppercase E, grave accent
{ 0xa4, 0xca }, // uppercase E, circumflex
{ 0xa5, 0xcb }, // capital letter E with diaresis
{ 0xa6, 0xeb }, // lowercase letter e with diaresis
{ 0xa7, 0xce }, // uppercase I, circumflex
{ 0xa8, 0xcf }, // uppercase I, with diaresis
{ 0xa9, 0xef }, // lowercase i, with diaresis
{ 0xaa, 0xd4 }, // uppercase O, circumflex
{ 0xab, 0xd9 }, // uppercase U, grave accent
{ 0xac, 0xf9 }, // lowercase u, grave accent
{ 0xad, 0xdb }, // uppercase U, circumflex
{ 0xae, 0xab }, // left-pointing double angle quotation mark
{ 0xaf, 0xbb }, // right-pointing double angle quotation mark
// THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
// THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F
{ 0xb0, 0xc3 }, // Uppercase A, tilde
{ 0xb1, 0xe3 }, // Lowercase a, tilde
{ 0xb2, 0xcd }, // Uppercase I, acute accent
{ 0xb3, 0xcc }, // Uppercase I, grave accent
{ 0xb4, 0xec }, // Lowercase i, grave accent
{ 0xb5, 0xd2 }, // Uppercase O, grave accent
{ 0xb6, 0xf2 }, // Lowercase o, grave accent
{ 0xb7, 0xd5 }, // Uppercase O, tilde
{ 0xb8, 0xf5 }, // Lowercase o, tilde
{ 0xb9, 0x7b }, // Open curly brace
{ 0xba, 0x7d }, // Closing curly brace
{ 0xbb, 0x5c }, // Backslash
{ 0xbc, 0x5e }, // Caret
{ 0xbd, 0x5f }, // Underscore
{ 0xbe, 0x7c }, // Pipe (vertical line)
{ 0xbf, 0x223c }, // Tilde operator
{ 0xc0, 0xc4 }, // Uppercase A, umlaut
{ 0xc1, 0xe4 }, // Lowercase A, umlaut
{ 0xc2, 0xd6 }, // Uppercase O, umlaut
{ 0xc3, 0xf6 }, // Lowercase o, umlaut
{ 0xc4, 0xdf }, // Esszett (sharp S)
{ 0xc5, 0xa5 }, // Yen symbol
{ 0xc6, 0xa4 }, // Generic currency sign
{ 0xc7, 0x2503 }, // Box drawings heavy vertical
{ 0xc8, 0xc5 }, // Uppercase A, ring
{ 0xc9, 0xe5 }, // Lowercase A, ring
{ 0xca, 0xd8 }, // Uppercase O, stroke
{ 0xcb, 0xf8 }, // Lowercase o, strok
{ 0xcc, 0x250f }, // Box drawings heavy down and right
{ 0xcd, 0x2513 }, // Box drawings heavy down and left
{ 0xce, 0x2517 }, // Box drawings heavy up and right
{ 0xcf, 0x251b } // Box drawings heavy up and left
};
public static Dictionary<int, int> Channel1RowsMap = new Dictionary<int, int>
{
{ 0x11, 1 },
{ 0x12, 3 },
{ 0x15, 5 },
{ 0x16, 7 },
{ 0x17, 9 },
{ 0x10, 11 },
{ 0x13, 12 },
{ 0x14, 14 }
};
public static Dictionary<int, int> Channel2RowsMap = new Dictionary<int, int>
{
{ 0x19, 1 },
{ 0x1A, 3 },
{ 0x1D, 5 },
{ 0x1E, 7 },
{ 0x1F, 9 },
{ 0x18, 11 },
{ 0x1B, 12 },
{ 0x1C, 14 }
};
}
}

View File

@ -0,0 +1,11 @@
namespace Nikse.SubtitleEdit.Core.Cea608
{
public class DataOutput
{
public int Channel { get; set; }
public bool Roll { get; set; }
public int End { get; set; }
public int Start { get; set; }
public SerializedRow[] Screen { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace Nikse.SubtitleEdit.Core.Cea608
{
public class PacData: SerializedPenState
{
public int Row { get; set; }
public string Color { get; set; }
public int? Indent { get; set; }
}
}

View File

@ -0,0 +1,93 @@
namespace Nikse.SubtitleEdit.Core.Cea608
{
public class PenState
{
public string Foreground { get; set; } = Constants.ColorWhite;
public bool Underline;
public bool Italics { get; set; }
public string Background { get; set; } = Constants.ColorBlack;
public bool Flash { get; set; }
public PenState()
{
}
public PenState(string foreground, bool underline, bool italics, string background, bool flash1)
{
Foreground = foreground;
Underline = underline;
Italics = italics;
Background = background;
Flash = flash1;
}
public void Reset()
{
Foreground = Constants.ColorWhite;
Underline = false;
Italics = false;
Background = Constants.ColorBlack;
Flash = false;
}
public CcStyle Serialize()
{
return new CcStyle
{
Foreground = Foreground,
Underline = Underline,
Italics = Italics,
Flash = Flash,
Background = Background
};
}
public void Copy(PenState newPenState)
{
Foreground = newPenState.Foreground;
Underline = newPenState.Underline;
Italics = newPenState.Italics;
Background = newPenState.Background;
Flash = newPenState.Flash;
}
public bool Equals(PenState other)
{
return Foreground == other.Foreground &&
Underline == other.Underline &&
Italics == other.Italics &&
Background == other.Background &&
Flash == other.Flash;
}
public bool IsDefault()
{
return Foreground == Constants.ColorWhite && !Underline && !Italics && Background == Constants.ColorBlack && !Flash;
}
public void SetStyles(SerializedPenState styles)
{
if (styles.Foreground != null)
{
Foreground = styles.Foreground;
}
if (styles.Underline != null)
{
Underline = styles.Underline.Value;
}
if (styles.Background != null)
{
Background = styles.Background;
}
if (styles.Flash != null)
{
Flash = styles.Flash.Value;
}
if (styles.Italics != null)
{
Italics = styles.Italics.Value;
}
}
}
}

View File

@ -0,0 +1,11 @@
namespace Nikse.SubtitleEdit.Core.Cea608
{
public class SerializedPenState
{
public string Foreground { get; set; }
public bool? Underline { get; set; }
public bool? Italics { get; set; }
public string Background { get; set; }
public bool? Flash { get; set; }
}
}

View File

@ -0,0 +1,15 @@
namespace Nikse.SubtitleEdit.Core.Cea608
{
public class SerializedRow
{
public int Row { get; set; }
/// <summary>
/// Column indentation.
/// </summary>
public int Position { get; set; }
public CcStyle Style { get; set; }
public SerializedStyledUnicodeChar[] Columns { get; set; }
}
}

View File

@ -0,0 +1,8 @@
namespace Nikse.SubtitleEdit.Core.Cea608
{
public class SerializedStyledUnicodeChar
{
public CcStyle Style { get; set; }
public string Character { get; set; }
}
}

View File

@ -0,0 +1,52 @@
namespace Nikse.SubtitleEdit.Core.Cea608
{
public class StyledUnicodeChar
{
public PenState PenState { get; set; }
public string Uchar { get; set; } = Constants.EmptyChar;
public string Foreground { get; set; }
public bool? Underline { get; set; }
public bool? Italics { get; set; }
public string Background { get; set; }
public bool? Flash { get; set; }
public StyledUnicodeChar()
{
PenState = new PenState(null, false, false, null, false);
}
public void Reset()
{
Uchar = Constants.EmptyChar;
PenState.Reset();
}
public void SetChar(string uchar, PenState newPenState)
{
Uchar = uchar;
PenState.Copy(newPenState);
}
public void SetPenState(PenState newPenState)
{
PenState.Copy(newPenState);
}
public bool Equals(StyledUnicodeChar other)
{
return Uchar == other.Uchar && PenState.Equals(other.PenState);
}
public void Copy(StyledUnicodeChar newChar)
{
Uchar = newChar.Uchar;
PenState.Copy(newChar.PenState);
}
public bool IsEmpty()
{
return Uchar == Constants.EmptyChar && PenState.IsDefault();
}
}
}