public void WriteParagraph(Paragraph p, Bitmap bmp, ContentAlignment alignment, Point?overridePosition = null) // inspired by code from SubtitleCreator { // timestamp: 00:00:33:900, filepos: 000000000 _idx.AppendLine($"timestamp: {p.StartTime.Hours:00}:{p.StartTime.Minutes:00}:{p.StartTime.Seconds:00}:{p.StartTime.Milliseconds:000}, filepos: {_subFile.Position.ToString("X").PadLeft(9, '0').ToLowerInvariant()}"); var nbmp = new NikseBitmap(bmp); _emphasis2 = nbmp.ConvertToFourColors(_background, _pattern, _emphasis1, _useInnerAntiAliasing); var twoPartBuffer = nbmp.RunLengthEncodeForDvd(_background, _pattern, _emphasis1, _emphasis2); var imageBuffer = GetSubImageBuffer(twoPartBuffer, nbmp, p, alignment, overridePosition); int bufferIndex = 0; byte vobSubId = (byte)_languageStreamId; var mwsub = new MemWriter(200000); byte[] subHeader = new byte[30]; byte[] ts = new byte[4]; // Lent from "Son2VobSub" by Alain Vielle and Petr Vyskocil // And also from Sup2VobSub by Emmel subHeader[0] = 0x00; // MPEG 2 PACK HEADER subHeader[1] = 0x00; subHeader[2] = 0x01; subHeader[3] = 0xba; subHeader[4] = 0x44; subHeader[5] = 0x02; subHeader[6] = 0xc4; subHeader[7] = 0x82; subHeader[8] = 0x04; subHeader[9] = 0xa9; subHeader[10] = 0x01; subHeader[11] = 0x89; subHeader[12] = 0xc3; subHeader[13] = 0xf8; subHeader[14] = 0x00; // PES subHeader[15] = 0x00; subHeader[16] = 0x01; subHeader[17] = 0xbd; int packetSize = imageBuffer.Length; long toWrite = packetSize; // Image buffer + control sequence length bool header0 = true; while (toWrite > 0) { long headerSize; if (header0) { header0 = false; // This is only for first packet subHeader[20] = 0x81; // mark as original subHeader[21] = 0x80; // first packet: PTS subHeader[22] = 0x05; // PES header data length // PTS (90kHz): //-------------- subHeader[23] = (byte)((ts[3] & 0xc0) >> 5 | 0x21); subHeader[24] = (byte)((ts[3] & 0x3f) << 2 | (ts[2] & 0xc0) >> 6); subHeader[25] = (byte)((ts[2] & 0x3f) << 2 | (ts[1] & 0x80) >> 6 | 0x01); subHeader[26] = (byte)((ts[1] & 0x7f) << 1 | (ts[0] & 0x80) >> 7); subHeader[27] = (byte)((ts[0] & 0x7f) << 1 | 0x01); const string pre = "0010"; // 0011 or 0010 ? (KMPlayer will not understand 0011!!!) long newPts = (long)(p.StartTime.TotalSeconds * 90000.0); 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++) { subHeader[23 + i] = Convert.ToByte(fiveBytesString.Substring(i * 8, 8), 2); } subHeader[28] = vobSubId; headerSize = 29; } else { subHeader[20] = 0x81; // mark as original subHeader[21] = 0x00; // no PTS subHeader[22] = 0x00; // header data length subHeader[23] = vobSubId; headerSize = 24; } if ((toWrite + headerSize) <= 0x800) { // write whole image in one 0x800 part long j = (headerSize - 20) + toWrite; subHeader[18] = (byte)(j / 0x100); subHeader[19] = (byte)(j % 0x100); // First Write header for (int x = 0; x < headerSize; x++) { mwsub.WriteByte(subHeader[x]); } // Write Image Data for (int x = 0; x < toWrite; x++) { mwsub.WriteByte(imageBuffer[bufferIndex++]); } // Pad remaining space long paddingSize = 0x800 - headerSize - toWrite; for (int x = 0; x < paddingSize; x++) { mwsub.WriteByte(0xff); } toWrite = 0; } else { // write multiple parts long blockSize = 0x800 - headerSize; long j = (headerSize - 20) + blockSize; subHeader[18] = (byte)(j / 0x100); subHeader[19] = (byte)(j % 0x100); // First Write header for (int x = 0; x < headerSize; x++) { mwsub.WriteByte(subHeader[x]); } // Write Image Data for (int x = 0; x < blockSize; x++) { mwsub.WriteByte(imageBuffer[bufferIndex++]); } toWrite -= blockSize; } } // Write whole memory stream to file long endPosition = mwsub.GetPosition(); mwsub.GotoBegin(); _subFile.Write(mwsub.GetBuf(), 0, (int)endPosition); }
// inspired by code from SubtitleCreator public void WriteParagraph(Paragraph p, Bitmap bmp, ContentAlignment alignment) { // 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').ToLower())); var nbmp = new NikseBitmap(bmp); _emphasis2 = nbmp.ConverToFourColors(_background, _pattern, _emphasis1, _useInnerAntialiasing); var twoPartBuffer = nbmp.RunLengthEncodeForDvd(_background, _pattern, _emphasis1, _emphasis2); var imageBuffer = GetSubImageBuffer(twoPartBuffer, nbmp, p, alignment); int bufferIndex = 0; byte vobSubId = (byte)_languageStreamId; var mwsub = new MemWriter(200000); byte[] subHeader = new byte[30]; byte[] ts = new byte[4]; // Lended from "Son2VobSub" by Alain Vielle and Petr Vyskocil // And also from Sup2VobSub by Emmel subHeader[0] = 0x00; // MPEG 2 PACK HEADER subHeader[1] = 0x00; subHeader[2] = 0x01; subHeader[3] = 0xba; subHeader[4] = 0x44; subHeader[5] = 0x02; subHeader[6] = 0xc4; subHeader[7] = 0x82; subHeader[8] = 0x04; subHeader[9] = 0xa9; subHeader[10] = 0x01; subHeader[11] = 0x89; subHeader[12] = 0xc3; subHeader[13] = 0xf8; subHeader[14] = 0x00; // PES subHeader[15] = 0x00; subHeader[16] = 0x01; subHeader[17] = 0xbd; int packetSize = imageBuffer.Length; long toWrite = packetSize; // Image buffer + control sequence length bool header0 = true; while (toWrite > 0) { long headerSize; if (header0) { header0 = false; // This is only for first packet subHeader[20] = 0x81; // mark as original subHeader[21] = 0x80; // first packet: PTS subHeader[22] = 0x05; // PES header data length // PTS (90kHz): //-------------- subHeader[23] = (byte)((ts[3] & 0xc0) >> 5 | 0x21); subHeader[24] = (byte)((ts[3] & 0x3f) << 2 | (ts[2] & 0xc0) >> 6); subHeader[25] = (byte)((ts[2] & 0x3f) << 2 | (ts[1] & 0x80) >> 6 | 0x01); subHeader[26] = (byte)((ts[1] & 0x7f) << 1 | (ts[0] & 0x80) >> 7); subHeader[27] = (byte)((ts[0] & 0x7f) << 1 | 0x01); const string pre = "0010"; // 0011 or 0010 ? (KMPlayer will not understand 0011!!!) long newPts = (long)(p.StartTime.TotalSeconds * 90000.0 + 0.5); 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++) { subHeader[23 + i] = Convert.ToByte(fiveBytesString.Substring((i * 8), 8), 2); } subHeader[28] = vobSubId; headerSize = 29; } else { subHeader[20] = 0x81; // mark as original subHeader[21] = 0x00; // no PTS subHeader[22] = 0x00; // header data length subHeader[23] = vobSubId; headerSize = 24; } if ((toWrite + headerSize) <= 0x800) { // write whole image in one 0x800 part long j = (headerSize - 20) + toWrite; subHeader[18] = (byte)(j / 0x100); subHeader[19] = (byte)(j % 0x100); // First Write header for (int x = 0; x < headerSize; x++) mwsub.WriteByte(subHeader[x]); // Write Image Data for (int x = 0; x < toWrite; x++) mwsub.WriteByte(imageBuffer[bufferIndex++]); // Pad remaining space long paddingSize = 0x800 - headerSize - toWrite; for (int x = 0; x < paddingSize; x++) mwsub.WriteByte(0xff); toWrite = 0; } else { // write multiple parts long blockSize = 0x800 - headerSize; long j = (headerSize - 20) + blockSize; subHeader[18] = (byte)(j / 0x100); subHeader[19] = (byte)(j % 0x100); // First Write header for (int x = 0; x < headerSize; x++) mwsub.WriteByte(subHeader[x]); // Write Image Data for (int x = 0; x < blockSize; x++) mwsub.WriteByte(imageBuffer[bufferIndex++]); toWrite -= blockSize; } } // Write whole memory stream to file long endPosition = mwsub.GetPosition(); mwsub.GotoBegin(); _subFile.Write(mwsub.GetBuf(), 0, (int)endPosition); }