Working on vobsub export (not working yet)

git-svn-id: https://subtitleedit.googlecode.com/svn/trunk@935 99eadd0c-20b8-1223-b5c4-2a2b2df33de2
This commit is contained in:
niksedk 2012-01-19 18:57:49 +00:00
parent 39e15a02db
commit f49ceeb1c3
7 changed files with 299 additions and 126 deletions

View File

@ -31,6 +31,7 @@ namespace Nikse.SubtitleEdit.Forms
public byte[] Buffer { get; set; }
public int ScreenWidth { get; set; }
public int ScreenHeight { get; set; }
public bool Saved { get; set; }
}
Subtitle _subtitle;
@ -90,6 +91,7 @@ namespace Nikse.SubtitleEdit.Forms
ScreenWidth = screenWidth,
ScreenHeight = screenHeight,
Bitmap = null,
Saved = false,
};
if (index < _subtitle.Paragraphs.Count)
{
@ -104,6 +106,7 @@ namespace Nikse.SubtitleEdit.Forms
private void buttonExport_Click(object sender, EventArgs e)
{
buttonExport.Enabled = false;
SetupImageParameters();
if (!string.IsNullOrEmpty(_fileName))
@ -128,17 +131,6 @@ namespace Nikse.SubtitleEdit.Forms
_exportType == "VOBSUB" && saveFileDialog1.ShowDialog(this) == DialogResult.OK ||
_exportType == "BDNXML" && folderBrowserDialog1.ShowDialog(this) == DialogResult.OK)
{
FileStream binarySubtitleFile = null;
VobSubWriter vobSubWriter = null;
if (_exportType == "BLURAYSUP")
binarySubtitleFile = new FileStream(saveFileDialog1.FileName, FileMode.Create);
else if (_exportType == "VOBSUB")
vobSubWriter = new VobSubWriter(saveFileDialog1.FileName);
progressBar1.Value = 0;
progressBar1.Maximum = _subtitle.Paragraphs.Count-1;
progressBar1.Visible = true;
int width = 1920;
int height = 1080;
if (comboBoxResolution.SelectedIndex == 1)
@ -162,6 +154,17 @@ namespace Nikse.SubtitleEdit.Forms
height = 480;
}
FileStream binarySubtitleFile = null;
VobSubWriter vobSubWriter = null;
if (_exportType == "BLURAYSUP")
binarySubtitleFile = new FileStream(saveFileDialog1.FileName, FileMode.Create);
else if (_exportType == "VOBSUB")
vobSubWriter = new VobSubWriter(saveFileDialog1.FileName, width, height, 15, 32, _subtitleColor, _borderColor);
progressBar1.Value = 0;
progressBar1.Maximum = _subtitle.Paragraphs.Count-1;
progressBar1.Visible = true;
const int border = 25;
int imagesSavedCount = 0;
var sb = new StringBuilder();
@ -256,40 +259,49 @@ namespace Nikse.SubtitleEdit.Forms
MessageBox.Show(string.Format(Configuration.Settings.Language.ExportPngXml.XImagesSavedInY, imagesSavedCount, folderBrowserDialog1.SelectedPath));
}
}
buttonExport.Enabled = true;
}
private int WriteParagraph(int width, StringBuilder sb, int border, int height, int imagesSavedCount,
VobSubWriter vobSubWriter, FileStream binarySubtitleFile, MakeBitmapParameter paramEqual,
VobSubWriter vobSubWriter, FileStream binarySubtitleFile, MakeBitmapParameter param,
int i)
{
if (paramEqual.Bitmap != null)
if (param.Bitmap != null)
{
if (_exportType == "BLURAYSUP")
{
binarySubtitleFile.Write(paramEqual.Buffer, 0, paramEqual.Buffer.Length);
if (!param.Saved)
binarySubtitleFile.Write(param.Buffer, 0, param.Buffer.Length);
param.Saved = true;
}
else if (_exportType == "VOBSUB")
{
vobSubWriter.WriteParagraph(paramEqual.P, paramEqual.Bitmap);
if (!param.Saved)
vobSubWriter.WriteParagraph(param.P, param.Bitmap);
param.Saved = true;
}
else
{
string numberString = string.Format("{0:0000}", i);
string fileName = Path.Combine(folderBrowserDialog1.SelectedPath, numberString + ".png");
paramEqual.Bitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Png);
imagesSavedCount++;
if (!param.Saved)
{
string numberString = string.Format("{0:0000}", i);
string fileName = Path.Combine(folderBrowserDialog1.SelectedPath, numberString + ".png");
param.Bitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Png);
imagesSavedCount++;
//<Event InTC="00:00:24:07" OutTC="00:00:31:13" Forced="False">
// <Graphic Width="696" Height="111" X="612" Y="930">subtitle_exp_0001.png</Graphic>
//</Event>
sb.AppendLine("<Event InTC=\"" + BdnXmlTimeCode(paramEqual.P.StartTime) + "\" OutTC=\"" +
BdnXmlTimeCode(paramEqual.P.EndTime) + "\" Forced=\"False\">");
int x = (width - paramEqual.Bitmap.Width)/2;
int y = height - (paramEqual.Bitmap.Height + border);
sb.AppendLine(" <Graphic Width=\"" + paramEqual.Bitmap.Width.ToString() + "\" Height=\"" +
paramEqual.Bitmap.Height.ToString() + "\" X=\"" + x.ToString() + "\" Y=\"" + y.ToString() +
"\">" + numberString + ".png</Graphic>");
sb.AppendLine("</Event>");
//<Event InTC="00:00:24:07" OutTC="00:00:31:13" Forced="False">
// <Graphic Width="696" Height="111" X="612" Y="930">subtitle_exp_0001.png</Graphic>
//</Event>
sb.AppendLine("<Event InTC=\"" + BdnXmlTimeCode(param.P.StartTime) + "\" OutTC=\"" +
BdnXmlTimeCode(param.P.EndTime) + "\" Forced=\"False\">");
int x = (width - param.Bitmap.Width) / 2;
int y = height - (param.Bitmap.Height + border);
sb.AppendLine(" <Graphic Width=\"" + param.Bitmap.Width.ToString() + "\" Height=\"" +
param.Bitmap.Height.ToString() + "\" X=\"" + x.ToString() + "\" Y=\"" + y.ToString() +
"\">" + numberString + ".png</Graphic>");
sb.AppendLine("</Event>");
param.Saved = true;
}
}
}
return imagesSavedCount;
@ -629,7 +641,10 @@ namespace Nikse.SubtitleEdit.Forms
comboBoxResolution.SelectedIndex = 0;
if (exportType == "VOBSUB")
{
comboBoxBorderWidth.SelectedIndex = 3;
comboBoxResolution.SelectedIndex = 3;
}
foreach (var x in FontFamily.Families)
{

View File

@ -963,7 +963,6 @@
this.vobSubsubidxToolStripMenuItem.Name = "vobSubsubidxToolStripMenuItem";
this.vobSubsubidxToolStripMenuItem.Size = new System.Drawing.Size(245, 22);
this.vobSubsubidxToolStripMenuItem.Text = "VobSub (sub/idx)...";
this.vobSubsubidxToolStripMenuItem.Visible = false;
this.vobSubsubidxToolStripMenuItem.Click += new System.EventHandler(this.vobSubsubidxToolStripMenuItem_Click);
//
// toolStripMenuItemCavena890

View File

@ -681,7 +681,7 @@
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAD2
CAAAAk1TRnQBSQFMAgEBAgEAAbgBDgG4AQ4BEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
CAAAAk1TRnQBSQFMAgEBAgEAAcABDgHAAQ4BEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
AwABQAMAARADAAEBAQABCAYAAQQYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA
AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5
AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA

View File

@ -1,6 +1,7 @@
using System;
using System.Drawing;
using System.Drawing.Imaging;
using Nikse.SubtitleEdit.Logic.VobSub;
namespace Nikse.SubtitleEdit.Logic
{
@ -93,47 +94,74 @@ namespace Nikse.SubtitleEdit.Logic
emphasis2Buffer[0] = (byte)emphasis2.B;
emphasis2Buffer[1] = (byte)emphasis2.G;
emphasis2Buffer[2] = (byte)emphasis2.R;
emphasis2Buffer[3] = (byte)emphasis2.A;
emphasis2Buffer[3] = (byte)emphasis2.A;
for (int i = 0; i < _bitmapData.Length; i += 4)
{
int smallestDiff = 10000; // Math.Abs(backgroundBuffer[0] - _bitmapData[i]) + Math.Abs(backgroundBuffer[1] - _bitmapData[i + 1]) + Math.Abs(backgroundBuffer[2] - _bitmapData[i + 2]) + Math.Abs(backgroundBuffer[3] - _bitmapData[i + 3]);
{
int smallestDiff = 10000;
byte[] buffer = backgroundBuffer;
if (backgroundBuffer[3] == 0 && _bitmapData[i+3] < 10) // transparent
if (backgroundBuffer[3] == 0 && _bitmapData[i + 3] < 10) // transparent
{
smallestDiff = 0;
int patternDiff = Math.Abs(patternBuffer[0] - _bitmapData[i]) + Math.Abs(patternBuffer[1] - _bitmapData[i + 1]) + Math.Abs(patternBuffer[2] - _bitmapData[i + 2]) + Math.Abs(patternBuffer[3] - _bitmapData[i + 3]);
if (patternDiff < smallestDiff)
{
smallestDiff = patternDiff;
buffer = patternBuffer;
}
int emphasis1Diff = Math.Abs(emphasis1Buffer[0] - _bitmapData[i]) + Math.Abs(emphasis1Buffer[1] - _bitmapData[i + 1]) + Math.Abs(emphasis1Buffer[2] - _bitmapData[i + 2]) + Math.Abs(emphasis1Buffer[3] - _bitmapData[i + 3]);
if (emphasis1Diff < smallestDiff)
else
{
smallestDiff = emphasis1Diff;
buffer = emphasis1Buffer;
}
int patternDiff = Math.Abs(patternBuffer[0] - _bitmapData[i]) + Math.Abs(patternBuffer[1] - _bitmapData[i + 1]) + Math.Abs(patternBuffer[2] - _bitmapData[i + 2]) + Math.Abs(patternBuffer[3] - _bitmapData[i + 3]);
if (patternDiff < smallestDiff)
{
smallestDiff = patternDiff;
buffer = patternBuffer;
}
int emphasis2Diff = Math.Abs(emphasis2Buffer[0] - _bitmapData[i]) + Math.Abs(emphasis2Buffer[1] - _bitmapData[i + 1]) + Math.Abs(emphasis2Buffer[2] - _bitmapData[i + 2]) + Math.Abs(emphasis2Buffer[3] - _bitmapData[i + 3]);
if (emphasis2Diff < smallestDiff)
{
smallestDiff = emphasis2Diff;
buffer = emphasis2Buffer;
}
else if (_bitmapData[i + 3] >= 10 && _bitmapData[i + 3] < 90) // anti-alias
{
smallestDiff = emphasis2Diff;
buffer = emphasis2Buffer;
}
int emphasis1Diff = Math.Abs(emphasis1Buffer[0] - _bitmapData[i]) + Math.Abs(emphasis1Buffer[1] - _bitmapData[i + 1]) + Math.Abs(emphasis1Buffer[2] - _bitmapData[i + 2]) + Math.Abs(emphasis1Buffer[3] - _bitmapData[i + 3]);
if (emphasis1Diff < smallestDiff)
{
smallestDiff = emphasis1Diff;
buffer = emphasis1Buffer;
}
int emphasis2Diff = Math.Abs(emphasis2Buffer[0] - _bitmapData[i]) + Math.Abs(emphasis2Buffer[1] - _bitmapData[i + 1]) + Math.Abs(emphasis2Buffer[2] - _bitmapData[i + 2]) + Math.Abs(emphasis2Buffer[3] - _bitmapData[i + 3]);
if (emphasis2Diff < smallestDiff)
{
smallestDiff = emphasis2Diff;
buffer = emphasis2Buffer;
}
else if (_bitmapData[i + 3] >= 10 && _bitmapData[i + 3] < 90) // anti-alias
{
smallestDiff = emphasis2Diff;
buffer = emphasis2Buffer;
}
}
Buffer.BlockCopy(buffer, 0, _bitmapData, i, 4);
}
}
public RunLengthTwoParts RunLengthEncodeForDvd(Color background, Color pattern, Color emphasis1, Color emphasis2)
{
byte[] backgroundBuffer = new byte[4];
backgroundBuffer[0] = (byte)background.B;
backgroundBuffer[1] = (byte)background.G;
backgroundBuffer[2] = (byte)background.R;
backgroundBuffer[3] = (byte)background.A;
byte[] patternBuffer = new byte[4];
patternBuffer[0] = (byte)pattern.B;
patternBuffer[1] = (byte)pattern.G;
patternBuffer[2] = (byte)pattern.R;
patternBuffer[3] = (byte)pattern.A;
byte[] emphasis1Buffer = new byte[4];
emphasis1Buffer[0] = (byte)emphasis1.B;
emphasis1Buffer[1] = (byte)emphasis1.G;
emphasis1Buffer[2] = (byte)emphasis1.R;
emphasis1Buffer[3] = (byte)emphasis1.A;
byte[] emphasis2Buffer = new byte[4];
emphasis2Buffer[0] = (byte)emphasis2.B;
emphasis2Buffer[1] = (byte)emphasis2.G;
emphasis2Buffer[2] = (byte)emphasis2.R;
emphasis2Buffer[3] = (byte)emphasis2.A;
byte[] bufferEqual = new byte[Width * Height];
byte[] bufferUnEqual = new byte[Width * Height];
int indexBufferEqual = 0;
@ -163,7 +191,7 @@ namespace Nikse.SubtitleEdit.Logic
for (int x = 0; x < Width; x++)
{
int color = GetDvdColor(x, y, background, pattern, emphasis1, emphasis2);
int color = GetDvdColor(x, y, backgroundBuffer, patternBuffer, emphasis1Buffer, emphasis2Buffer);
if (lastColor == -1)
{
@ -203,7 +231,7 @@ namespace Nikse.SubtitleEdit.Logic
twoParts.Buffer1 = new byte[indexBufferEqual];
Buffer.BlockCopy(bufferEqual, 0, twoParts.Buffer1, 0, indexBufferEqual);
twoParts.Buffer2 = new byte[indexBufferUnEqual];
Buffer.BlockCopy(bufferEqual, 0, twoParts.Buffer2, 0, indexBufferUnEqual);
Buffer.BlockCopy(bufferUnEqual, 0, twoParts.Buffer2, 0, indexBufferUnEqual);
return twoParts;
}
@ -252,22 +280,26 @@ namespace Nikse.SubtitleEdit.Logic
index++;
byte secondNibble = (byte)(n & Nikse.SubtitleEdit.Logic.VobSub.Helper.B11111111);
buffer[index] = (byte)secondNibble;
index++;
}
}
private void WriteThreeNibbles(byte[] buffer, int count, int color, ref int index, ref bool indexHalfNibble)
{
byte n = (byte)((count << 2) + color);
//Value Bits n=length, c=color
//16-63 12 0 0 0 0 n n n n n n c c (one and a half byte)
ushort n = (ushort)((count << 2) + color);
if (indexHalfNibble)
{
index++; // there should already zeroes in last nibble
buffer[index] = (byte)n;
index++;
buffer[index] = n;
}
else
{
buffer[index] = (byte)(n >> 4);
index++;
buffer[index] = (byte)(n << 4);
buffer[index] = (byte)((n & Helper.B00011111) << 4);
}
indexHalfNibble = !indexHalfNibble;
}
@ -275,14 +307,16 @@ namespace Nikse.SubtitleEdit.Logic
private void WriteTwoNibbles(byte[] buffer, int count, int color, ref int index, bool indexHalfNibble)
{
//Value Bits n=length, c=color
//4-15 8 0 0 n n n n c c (one byte)
byte n = (byte)((count << 2) + color);
if (indexHalfNibble)
{
byte firstNibble = (byte)(n >> 4);
buffer[index] = (byte)(buffer[index] & firstNibble);
byte secondNibble = (byte)(n << 4);
buffer[index] = (byte)(buffer[index] | firstNibble);
byte secondNibble = (byte)((n & Helper.B00001111) << 4);
index++;
buffer[index] = (byte)secondNibble;
buffer[index] = secondNibble;
}
else
{
@ -296,7 +330,7 @@ namespace Nikse.SubtitleEdit.Logic
byte n = (byte)((count << 2) + color);
if (indexHalfNibble)
{
buffer[index] = (byte)(buffer[index] & n);
buffer[index] = (byte)(buffer[index] | n);
index++;
}
else
@ -306,15 +340,20 @@ namespace Nikse.SubtitleEdit.Logic
indexHalfNibble = !indexHalfNibble;
}
private int GetDvdColor(int x, int y, Color background, Color pattern, Color emphasis1, Color emphasis2)
private int GetDvdColor(int x, int y, byte[] background, byte[] pattern, byte[] emphasis1, byte[] emphasis2)
{
Color c = GetPixelNext();
if (emphasis2 == c)
return 3;
else if (emphasis1 == c)
return 2;
if (pattern == c)
_pixelAddress += 4;
int a = _bitmapData[_pixelAddress + 3];
int r = _bitmapData[_pixelAddress + 2];
int g = _bitmapData[_pixelAddress + 1];
int b = _bitmapData[_pixelAddress];
if (pattern[0] == _bitmapData[_pixelAddress] && pattern[1] == _bitmapData[_pixelAddress + 1] && pattern[2] == _bitmapData[_pixelAddress + 2] && pattern[3] == _bitmapData[_pixelAddress + 3])
return 1;
if (emphasis1[0] == _bitmapData[_pixelAddress] && emphasis1[1] == _bitmapData[_pixelAddress + 1] && emphasis1[2] == _bitmapData[_pixelAddress + 2] && emphasis1[3] == _bitmapData[_pixelAddress + 3])
return 2;
if (emphasis2[0] == _bitmapData[_pixelAddress] && emphasis2[1] == _bitmapData[_pixelAddress + 1] && emphasis2[2] == _bitmapData[_pixelAddress + 2] && emphasis2[3] == _bitmapData[_pixelAddress + 3])
return 3;
return 0;
}

View File

@ -35,8 +35,7 @@ namespace Nikse.SubtitleEdit.Logic.VobSub
string[] colors = s.Split(", ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
foreach (string hex in colors)
{
if (hex.Length == 6)
Palette.Add(HexToColor(hex));
Palette.Add(HexToColor(hex));
}
}
else if (line.ToLower().StartsWith("id:") && line.Length > 4)
@ -63,10 +62,23 @@ namespace Nikse.SubtitleEdit.Logic.VobSub
private static Color HexToColor(string hex)
{
int r = Convert.ToInt32(hex.Substring(0, 2), 16);
int g = Convert.ToInt32(hex.Substring(2, 2), 16);
int b = Convert.ToInt32(hex.Substring(4, 2), 16);
return Color.FromArgb(r, g, b);
hex = hex.TrimStart('#').Trim();
if (hex.Length == 6)
{
int r = Convert.ToInt32(hex.Substring(0, 2), 16);
int g = Convert.ToInt32(hex.Substring(2, 2), 16);
int b = Convert.ToInt32(hex.Substring(4, 2), 16);
return Color.FromArgb(r, g, b);
}
else if (hex.Length == 8)
{
int a = Convert.ToInt32(hex.Substring(0, 2), 16);
int r = Convert.ToInt32(hex.Substring(2, 2), 16);
int g = Convert.ToInt32(hex.Substring(4, 2), 16);
int b = Convert.ToInt32(hex.Substring(6, 2), 16);
return Color.FromArgb(a, r, g, b);
}
return Color.Red;
}
private static IdxParagraph GetTimeCodeAndFilePosition(string line)

View File

@ -142,11 +142,10 @@ namespace Nikse.SubtitleEdit.Logic.VobSub
case (int)DisplayControlCommand.SetDisplayArea: // 5
if (_data.Length > commandIndex + 6)
{
string binary = Helper.GetBinaryString(_data, commandIndex + 1, 6);
int startingX = (int)Helper.GetUInt32FromBinaryString(binary.Substring(0, 12));
int endingX = (int)Helper.GetUInt32FromBinaryString(binary.Substring(12, 12));
int startingY = (int)Helper.GetUInt32FromBinaryString(binary.Substring(24, 12));
int endingY = (int)Helper.GetUInt32FromBinaryString(binary.Substring(36, 12));
int startingX = (_data[commandIndex + 1] << 8 | _data[commandIndex + 2]) >> 4;
int endingX = (_data[commandIndex + 2] & Helper.B00001111) << 8 | _data[commandIndex + 3];
int startingY = (_data[commandIndex + 4] << 8 | _data[commandIndex + 5]) >> 4;
int endingY = (_data[commandIndex + 5] & Helper.B00001111) << 8 | _data[commandIndex + 6];
ImageDisplayArea = new Rectangle(startingX, startingY, endingX - startingX, endingY - startingY);
}
commandIndex += 7;

View File

@ -7,6 +7,8 @@ namespace Nikse.SubtitleEdit.Logic.VobSub
{
public class VobSubWriter
{
private const int PacketizedElementaryStreamMaximumLength = 2028;
/// <summary>
/// 14 bytes Mpeg 2 pack header
/// </summary>
@ -30,10 +32,9 @@ namespace Nikse.SubtitleEdit.Logic.VobSub
0x00, 0x00, // 18-19=PES packet length
0x81, // 20=Flags: PES scrambling control, PES priority, data alignment indicator, copyright, original or copy
0x81, // 21=Flags: PTS DTS flags, ESCR flag, ES rate flag, DSM trick mode flag, additional copy info flag, PES CRC flag, PES extension flag
0x05, // 22=PES header data length
0x08, // 22=PES header data length
};
/// <summary>
/// 9 bytes packetized elementary stream header (PES)
/// </summary>
@ -60,88 +61,191 @@ namespace Nikse.SubtitleEdit.Logic.VobSub
private string _subFileName;
private FileStream _subFile;
StringBuilder _idx = new StringBuilder();
int _screenWidth = 720;
int _screenHeight = 480;
int _bottomMargin = 15;
int _languageStreamId = 32;
public VobSubWriter(string subFileName)
Color _background = Color.Transparent;
Color _pattern = Color.White;
Color _emphasis1 = Color.Black;
Color _emphasis2 = Color.FromArgb(240, Color.Black);
public VobSubWriter(string subFileName, int screenWidth, int screenHeight, int bottomMargin, int languageStreamId, Color pattern, Color emphasis1)
{
_subFileName = subFileName;
_screenWidth = screenWidth;
_screenHeight = screenHeight;
_bottomMargin = bottomMargin;
_languageStreamId = languageStreamId;
_pattern = pattern;
_emphasis1 = emphasis1;
_emphasis2 = Color.FromArgb(240, emphasis1);
_idx = CreateIdxHeader();
_subFile = new FileStream(subFileName, FileMode.Create);
}
public void WriteEndianWord(int i)
{
_subFile.WriteByte((byte)(i / 256));
_subFile.WriteByte((byte)(i % 256));
}
private byte[] GetImageBuffer()
{
var buffer = new byte[];
return buffer;
}
public void WriteParagraph(Paragraph p, Bitmap bmp)
{
// timestamp: 00:00:33:900, filepos: 000000000
_idx.AppendLine(string.Format("timestamp: {0:00}:{1:00}:{2:00}:{3:000}, filepos: {4}", p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, p.StartTime.Milliseconds, _subFile.Position.ToString("X").PadLeft(9, '0')));
_idx.AppendLine(string.Format("timestamp: {0:00}:{1:00}:{2:00}:{3:000}, filepos: {4}", p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, p.StartTime.Milliseconds, _subFile.Position.ToString("X").PadLeft(9, '0')));
// write binary vobsub file (duration + image)
long start = _subFile.Position;
_subFile.Write(Mpeg2PackHeaderBuffer, 0, Mpeg2PackHeaderBuffer.Length);
NikseBitmap nbmp = new NikseBitmap(bmp);
nbmp.ConverToFourColors(Color.Transparent, Color.White, Color.FromArgb(200, 0, 0, 0), Color.FromArgb(200, 25, 25, 25));
var outBmp = nbmp.GetBitmap();
outBmp.Save(@"D:\download\-_-" + p.Number.ToString() + ".bmp");
bmp.Save(@"D:\download\-__" + p.Number.ToString() + ".bmp");
outBmp.Dispose();
var twoPartBuffer = nbmp.RunLengthEncodeForDvd(Color.Transparent, Color.White, Color.FromArgb(200, 0, 0, 0), Color.FromArgb(200, 25, 25, 25));
var nbmp = new NikseBitmap(bmp);
nbmp.ConverToFourColors(_background, _pattern, _emphasis1, _emphasis2);
var twoPartBuffer = nbmp.RunLengthEncodeForDvd(_background, _pattern, _emphasis1, _emphasis2);
// PES size
int length = Mpeg2PackHeaderBuffer.Length + PacketizedElementaryStreamHeaderBufferFirst.Length + 10 + twoPartBuffer.Length;
//block_size = 0x800 - header_size;
//long j = (header_size - 20) + block_size;
//SubHeader[18] = (byte)(j / 0x100);
//SubHeader[19] = (byte)(j % 0x100);
int length = Mpeg2PackHeaderBuffer.Length + PacketizedElementaryStreamHeaderBufferFirst.Length + twoPartBuffer.Length + 31;
PacketizedElementaryStreamHeaderBufferFirst[4] = (byte)(length / 256);
PacketizedElementaryStreamHeaderBufferFirst[5] = (byte)(length % 256);
_subFile.Write(PacketizedElementaryStreamHeaderBufferFirst, 0, PacketizedElementaryStreamHeaderBufferFirst.Length);
// PTS (timestamp)
FillPTS(p.StartTime);
_subFile.Write(PresentationTimeStampBuffer, 0, PresentationTimeStampBuffer.Length);
_subFile.WriteByte(0x32); //sub-stream number
_subFile.WriteByte(0x1e); // ??
_subFile.WriteByte(0x60); // ??
_subFile.WriteByte(0x3a); // ??
_subFile.WriteByte((byte)_languageStreamId); //sub-stream number, 32=english
// sup picture datasize
WriteEndianWord(twoPartBuffer.Length + 42);
// first display control sequence table address
int startDisplayControlSequenceTableAddress = twoPartBuffer.Length + 4;
WriteEndianWord(startDisplayControlSequenceTableAddress);
if (twoPartBuffer.Length < 0x800 - (_subFile.Position - start))
{
// Write image
int imageTopFieldDataAddress = (int) (4);
_subFile.Write(twoPartBuffer.Buffer1, 0, twoPartBuffer.Buffer1.Length);
int imageBottomFieldDataAddress = 4 + twoPartBuffer.Buffer1.Length;
_subFile.Write(twoPartBuffer.Buffer2, 0, twoPartBuffer.Buffer2.Length);
}
else
{
System.Windows.Forms.MessageBox.Show("Too long for payload!!!");
}
// HeaderDataLength = buffer[index + 8];
// Write zero delay
_subFile.WriteByte(0);
_subFile.WriteByte(0);
// language id
// int id = buffer[9 + HeaderDataLength];
// if (id >= 0x20 && id <= 0x40) // x3f 0r x40 ?
// SubPictureStreamId = id;
// next display control sequence table address (use current is last)
WriteEndianWord(startDisplayControlSequenceTableAddress+24); // start of display control sequence table address
// Control command 1 = ForcedStartDisplay
_subFile.WriteByte(1);
// Control command 3 = SetColor
WriteColors(_subFile); // 3 bytes
// Control command 4 = SetContrast
WriteContrast(_subFile); // 3 bytes
// Control command 5 = SetDisplayArea
WriteDisplayArea(_subFile, nbmp); // 7 bytes
// Control command 6 = SetPixelDataAddress
WritePixelDataAddress(_subFile, imageTopFieldDataAddress, imageBottomFieldDataAddress); // 5 bytes
// Control command exit
_subFile.WriteByte(255); // 1 bytes
// Control Sequence Table
// Write delay - subtitle duration
WriteEndianWord((int)((Convert.ToInt32(p.Duration.TotalMilliseconds * 90.0 - 1023) >> 10))); //
// next display control sequence table address (use current is last)
WriteEndianWord(startDisplayControlSequenceTableAddress+24); // start of display control sequence table address
// Control command 2 = StopDisplay
_subFile.WriteByte(2);
}
//else
//{
// System.Windows.Forms.MessageBox.Show("Too long for payload!!!");
//}
for (long i = _subFile.Position - start; i < 0x800; i++) // 2048 packet size - pad with 0xff
_subFile.WriteByte(0xff);
}
private void WritePixelDataAddress(FileStream _subFile, int imageTopFieldDataAddress, int imageBottomFieldDataAddress)
{
_subFile.WriteByte(6);
WriteEndianWord(imageTopFieldDataAddress);
WriteEndianWord(imageBottomFieldDataAddress);
}
private void WriteDisplayArea(FileStream _subFile, NikseBitmap nbmp)
{
_subFile.WriteByte(5);
// Write 6 bytes of area - starting X, ending X, starting Y, ending Y, each 12 bits
ushort startX = (ushort) ((_screenWidth - nbmp.Width) / 2);
ushort endX = (ushort)(startX + nbmp.Width-1);
ushort startY = (ushort)(_screenHeight - nbmp.Height - _bottomMargin);
ushort endY = (ushort)(startY + nbmp.Height-1);
WriteEndianWord((ushort)(startX << 4 | endX >> 8)); // 16 - 12 start x + 4 end x
WriteEndianWord((ushort)(endX << 8 | startY >> 4)); // 16 - 8 endx + 8 starty
WriteEndianWord((ushort)(startY << 12 | endY)); // 16 - 4 start y + 12 end y
}
/// <summary>
/// Directly provides the four contrast (alpha blend) values to associate with the four pixel values. One nibble per pixel value for a total of 2 bytes. 0x0 = transparent, 0xF = opaque
/// </summary>
/// <param name="_subFile"></param>
private void WriteContrast(FileStream _subFile)
{
_subFile.WriteByte(4);
_subFile.WriteByte((byte)((_emphasis2.A << 4) | _emphasis1.A)); // emphasis2 + emphasis1
_subFile.WriteByte((byte)((_pattern.A << 4) | _background.A)); // pattern + background
}
/// <summary>
/// provides four indices into the CLUT for the current PGC to associate with the four pixel values. One nibble per pixel value for a total of 2 bytes.
/// </summary>
private void WriteColors(FileStream _subFile)
{
// Index to palette
byte emphasis2 = 3;
byte emphasis1 = 2;
byte pattern = 1;
byte background = 0;
_subFile.WriteByte(3);
_subFile.WriteByte((byte)((emphasis2 << 4) | emphasis1)); // emphasis2 + emphasis1
_subFile.WriteByte((byte)((pattern << 4) | background)); // pattern + background
}
/// <summary>
/// Write the 5 PTS bytes to buffer
/// </summary>
private void FillPTS(TimeCode timeCode)
{
string pre = "0011"; // 0011 or 0010 ?
// pre = "0010";
long newPts = (long)(timeCode.TotalMilliseconds); // TODO: Calculation from milliseconds
string bString = Convert.ToString(newPts, 2).PadLeft(33, '0');
string fiveBytesString = pre + bString.Substring(0, 3) + "1" + bString.Substring(3, 15) + "1" + bString.Substring(18, 15) + "1";
for (int i = 0; i < 5; i++)
{
byte b = Convert.ToByte(fiveBytesString.Substring((i * 8), 8), 2);
@ -164,7 +268,7 @@ namespace Nikse.SubtitleEdit.Logic.VobSub
private StringBuilder CreateIdxHeader()
{
StringBuilder sb = new StringBuilder();
var sb = new StringBuilder();
sb.AppendLine(@"# VobSub index file, v7 (do not modify this line!)
#
# To repair desyncronization, you can insert gaps this way:
@ -187,7 +291,7 @@ namespace Nikse.SubtitleEdit.Logic.VobSub
# Settings
# Original frame size
size: 720x480
size: " + _screenWidth + "x" + _screenHeight + @"
# Origin, relative to the upper-left corner, can be overloaded by aligment
org: 0, 0
@ -215,7 +319,7 @@ time offset: 0
forced subs: OFF
# The original palette of the DVD
palette: 000000, ffffff, 000000, 191919, 828282, 828282, 828282, ffffff, 828282, bababa, 828282, 828282, 828282, 828282, 828282, 828282
palette: 00000000, " + ToHexColor(_pattern) + ", " + ToHexColor(_emphasis1) + ", " + ToHexColor(_emphasis2) + @", 828282, 828282, 828282, ffffff, 828282, bababa, 828282, 828282, 828282, 828282, 828282, 828282
# Custom colors (transp idxs and the four colors)
custom colors: OFF, tridx: 0000, colors: 000000, 000000, 000000, 000000
@ -230,5 +334,10 @@ id: en, index: 0
return sb;
}
}
private string ToHexColor(Color c)
{
return (c.A.ToString("X2") + c.R.ToString("X2") + c.G.ToString("X2") + c.B.ToString("X2")).ToLower();
}
}
}