2015-08-26 22:42:40 +02:00
using System ;
using System.Collections.Generic ;
using System.Drawing ;
namespace Nikse.SubtitleEdit.Core.VobSub
{
/// <summary>
/// Subtitle Picture - see http://www.mpucoder.com/DVD/spu.html for more info
/// </summary>
public class SubPicture
{
private enum DisplayControlCommand
{
ForcedStartDisplay = 0 ,
StartDisplay = 1 ,
StopDisplay = 2 ,
SetColor = 3 ,
SetContrast = 4 ,
SetDisplayArea = 5 ,
SetPixelDataAddress = 6 ,
ChangeColorAndContrast = 7 ,
End = 0xFF ,
}
public readonly int SubPictureDateSize ;
public TimeSpan Delay ;
2018-02-06 20:34:15 +01:00
public int BufferSize = > _data . Length ;
2015-08-26 22:42:40 +02:00
private readonly byte [ ] _data ;
public Rectangle ImageDisplayArea ;
public bool Forced { get ; private set ; }
2018-02-06 20:34:15 +01:00
private readonly int _pixelDataAddressOffset ;
private readonly int _startDisplayControlSequenceTableAddress ;
2015-08-26 22:42:40 +02:00
public SubPicture ( byte [ ] data )
{
_data = data ;
SubPictureDateSize = Helper . GetEndianWord ( _data , 0 ) ;
_startDisplayControlSequenceTableAddress = Helper . GetEndianWord ( _data , 2 ) ;
ParseDisplayControlCommands ( false , null , null , false ) ;
}
/// <summary>
/// For SP packet with DVD subpictures
/// </summary>
/// <param name="data">Byte data buffer</param>
/// <param name="startDisplayControlSequenceTableAddress">Address of first control sequence in data</param>
/// <param name="pixelDataAddressOffset">Bitmap pixel data address offset</param>
public SubPicture ( byte [ ] data , int startDisplayControlSequenceTableAddress , int pixelDataAddressOffset )
{
_data = data ;
SubPictureDateSize = _data . Length ;
_startDisplayControlSequenceTableAddress = startDisplayControlSequenceTableAddress ;
_pixelDataAddressOffset = pixelDataAddressOffset ;
ParseDisplayControlCommands ( false , null , null , false ) ;
}
/// <summary>
/// Generates the current subtitle image
/// </summary>
/// <param name="colorLookupTable">The Color LookUp Table (CLUT), if null then only the four colors are used (should contain 16 elements if not null)</param>
/// <param name="background">Background color</param>
/// <param name="pattern">Color</param>
/// <param name="emphasis1">Color</param>
/// <param name="emphasis2">Color</param>
/// <param name="useCustomColors">Use custom colors instead of lookup table</param>
/// <returns>Subtitle image</returns>
public Bitmap GetBitmap ( List < Color > colorLookupTable , Color background , Color pattern , Color emphasis1 , Color emphasis2 , bool useCustomColors )
{
var fourColors = new List < Color > { background , pattern , emphasis1 , emphasis2 } ;
return ParseDisplayControlCommands ( true , colorLookupTable , fourColors , useCustomColors ) ;
}
private Bitmap ParseDisplayControlCommands ( bool createBitmap , List < Color > colorLookUpTable , List < Color > fourColors , bool useCustomColors )
{
ImageDisplayArea = new Rectangle ( ) ;
Bitmap bmp = null ;
var displayControlSequenceTableAddresses = new List < int > ( ) ;
int imageTopFieldDataAddress = 0 ;
int imageBottomFieldDataAddress = 0 ;
bool bitmapGenerated = false ;
double largestDelay = - 999999 ;
int displayControlSequenceTableAddress = _startDisplayControlSequenceTableAddress - _pixelDataAddressOffset ;
int lastDisplayControlSequenceTableAddress = 0 ;
displayControlSequenceTableAddresses . Add ( displayControlSequenceTableAddress ) ;
int commandIndex = 0 ;
while ( displayControlSequenceTableAddress > lastDisplayControlSequenceTableAddress & & displayControlSequenceTableAddress + 1 < _data . Length & & commandIndex < _data . Length )
{
int delayBeforeExecute = Helper . GetEndianWord ( _data , displayControlSequenceTableAddress + _pixelDataAddressOffset ) ;
commandIndex = displayControlSequenceTableAddress + 4 + _pixelDataAddressOffset ;
if ( commandIndex > = _data . Length )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
break ; // invalid index
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
int command = _data [ commandIndex ] ;
int numberOfCommands = 0 ;
while ( command ! = ( int ) DisplayControlCommand . End & & numberOfCommands < 1000 & & commandIndex < _data . Length )
{
numberOfCommands + + ;
switch ( command )
{
case ( int ) DisplayControlCommand . ForcedStartDisplay : // 0
Forced = true ;
commandIndex + + ;
break ;
case ( int ) DisplayControlCommand . StartDisplay : // 1
commandIndex + + ;
break ;
case ( int ) DisplayControlCommand . StopDisplay : // 2
2018-02-06 20:34:15 +01:00
Delay = TimeSpan . FromMilliseconds ( ( delayBeforeExecute < < 10 ) / 90.0 ) ;
2015-08-26 22:42:40 +02:00
if ( createBitmap & & Delay . TotalMilliseconds > largestDelay ) // in case of more than one images, just use the one with the largest display time
{
largestDelay = Delay . TotalMilliseconds ;
2018-02-06 20:34:15 +01:00
bmp ? . Dispose ( ) ;
2015-08-26 22:42:40 +02:00
bmp = GenerateBitmap ( ImageDisplayArea , imageTopFieldDataAddress , imageBottomFieldDataAddress , fourColors ) ;
bitmapGenerated = true ;
}
commandIndex + + ;
break ;
case ( int ) DisplayControlCommand . SetColor : // 3
if ( colorLookUpTable ! = null & & fourColors . Count = = 4 )
{
byte [ ] imageColor = { _data [ commandIndex + 1 ] , _data [ commandIndex + 2 ] } ;
if ( ! useCustomColors )
{
SetColor ( fourColors , 3 , imageColor [ 0 ] > > 4 , colorLookUpTable ) ;
SetColor ( fourColors , 2 , imageColor [ 0 ] & Helper . B00001111 , colorLookUpTable ) ;
SetColor ( fourColors , 1 , imageColor [ 1 ] > > 4 , colorLookUpTable ) ;
SetColor ( fourColors , 0 , imageColor [ 1 ] & Helper . B00001111 , colorLookUpTable ) ;
}
}
commandIndex + = 3 ;
break ;
case ( int ) DisplayControlCommand . SetContrast : // 4
if ( colorLookUpTable ! = null & & fourColors . Count = = 4 )
{
var imageContrast = new [ ] { _data [ commandIndex + 1 ] , _data [ commandIndex + 2 ] } ;
if ( imageContrast [ 0 ] + imageContrast [ 1 ] > 0 )
{
SetTransparency ( fourColors , 3 , ( imageContrast [ 0 ] & 0xF0 ) > > 4 ) ;
SetTransparency ( fourColors , 2 , imageContrast [ 0 ] & Helper . B00001111 ) ;
SetTransparency ( fourColors , 1 , ( imageContrast [ 1 ] & 0xF0 ) > > 4 ) ;
SetTransparency ( fourColors , 0 , imageContrast [ 1 ] & Helper . B00001111 ) ;
}
}
commandIndex + = 3 ;
break ;
case ( int ) DisplayControlCommand . SetDisplayArea : // 5
2017-10-31 15:29:03 +01:00
if ( _data . Length > commandIndex + 6 & & ImageDisplayArea . Width = = 0 & & ImageDisplayArea . Height = = 0 )
2015-08-26 22:42:40 +02:00
{
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 ;
break ;
case ( int ) DisplayControlCommand . SetPixelDataAddress : // 6
imageTopFieldDataAddress = Helper . GetEndianWord ( _data , commandIndex + 1 ) + _pixelDataAddressOffset ;
imageBottomFieldDataAddress = Helper . GetEndianWord ( _data , commandIndex + 3 ) + _pixelDataAddressOffset ;
commandIndex + = 5 ;
break ;
case ( int ) DisplayControlCommand . ChangeColorAndContrast : // 7
commandIndex + + ;
//int parameterAreaSize = (int)Helper.GetEndian(_data, commandIndex, 2);
if ( commandIndex + 1 < _data . Length )
{
int parameterAreaSize = _data [ commandIndex + 1 ] ; // this should be enough??? (no larger than 255 bytes)
if ( colorLookUpTable ! = null )
{
// TODO: Set fourColors
}
commandIndex + = parameterAreaSize ;
}
else
{
commandIndex + + ;
}
break ;
default :
commandIndex + + ;
break ;
}
if ( commandIndex > = _data . Length ) // in case of bad files...
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
break ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
command = _data [ commandIndex ] ;
}
lastDisplayControlSequenceTableAddress = displayControlSequenceTableAddress ;
if ( _pixelDataAddressOffset = = - 4 )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
displayControlSequenceTableAddress = Helper . GetEndianWord ( _data , commandIndex + 3 ) ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
else
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
displayControlSequenceTableAddress = Helper . GetEndianWord ( _data , displayControlSequenceTableAddress + 2 ) ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
}
if ( createBitmap & & ! bitmapGenerated ) // StopDisplay not needed (delay will be zero - should be just before start of next subtitle)
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
bmp = GenerateBitmap ( ImageDisplayArea , imageTopFieldDataAddress , imageBottomFieldDataAddress , fourColors ) ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
return bmp ;
}
private static void SetColor ( List < Color > fourColors , int fourColorIndex , int clutIndex , List < Color > colorLookUpTable )
{
if ( clutIndex > = 0 & & clutIndex < colorLookUpTable . Count & & fourColorIndex > = 0 )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
fourColors [ fourColorIndex ] = colorLookUpTable [ clutIndex ] ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
}
private static void SetTransparency ( List < Color > fourColors , int fourColorIndex , int alpha )
{
// alpha: 0x0 = transparent, 0xF = opaque (in C# 0 is fully transparent, and 255 is fully opaque so we have to multiply by 17)
if ( fourColorIndex > = 0 )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
fourColors [ fourColorIndex ] = Color . FromArgb ( alpha * 17 , fourColors [ fourColorIndex ] . R , fourColors [ fourColorIndex ] . G , fourColors [ fourColorIndex ] . B ) ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
}
private Bitmap GenerateBitmap ( Rectangle imageDisplayArea , int imageTopFieldDataAddress , int imageBottomFieldDataAddress , List < Color > fourColors )
{
if ( imageDisplayArea . Width < = 0 | | imageDisplayArea . Height < = 0 )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
return new Bitmap ( 1 , 1 ) ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
var bmp = new Bitmap ( imageDisplayArea . Width + 1 , imageDisplayArea . Height + 1 ) ;
if ( fourColors [ 0 ] ! = Color . Transparent )
{
var gr = Graphics . FromImage ( bmp ) ;
gr . FillRectangle ( new SolidBrush ( fourColors [ 0 ] ) , new Rectangle ( 0 , 0 , bmp . Width , bmp . Height ) ) ;
gr . Dispose ( ) ;
}
var fastBmp = new FastBitmap ( bmp ) ;
fastBmp . LockImage ( ) ;
GenerateBitmap ( _data , fastBmp , 0 , imageTopFieldDataAddress , fourColors , 2 ) ;
GenerateBitmap ( _data , fastBmp , 1 , imageBottomFieldDataAddress , fourColors , 2 ) ;
Bitmap cropped = CropBitmapAndUnlok ( fastBmp , fourColors [ 0 ] ) ;
bmp . Dispose ( ) ;
return cropped ;
}
private static Bitmap CropBitmapAndUnlok ( FastBitmap bmp , Color backgroundColor )
{
int y = 0 ;
int x ;
Color c = backgroundColor ;
int backgroundArgb = backgroundColor . ToArgb ( ) ;
// Crop top
while ( y < bmp . Height & & IsBackgroundColor ( c , backgroundArgb ) )
{
c = bmp . GetPixel ( 0 , y ) ;
if ( IsBackgroundColor ( c , backgroundArgb ) )
{
for ( x = 1 ; x < bmp . Width ; x + + )
{
c = bmp . GetPixelNext ( ) ;
if ( c . A ! = 0 & & c . ToArgb ( ) ! = backgroundArgb )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
break ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
}
}
if ( IsBackgroundColor ( c , backgroundArgb ) )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
y + + ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
}
int minY = y ;
if ( minY > 3 )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
minY - = 3 ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
else
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
minY = 0 ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
// Crop left
x = 0 ;
c = backgroundColor ;
while ( x < bmp . Width & & IsBackgroundColor ( c , backgroundArgb ) )
{
for ( y = minY ; y < bmp . Height ; y + + )
{
c = bmp . GetPixel ( x , y ) ;
if ( ! IsBackgroundColor ( c , backgroundArgb ) )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
break ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
}
if ( IsBackgroundColor ( c , backgroundArgb ) )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
x + + ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
}
int minX = x ;
if ( minX > 3 )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
minX - = 3 ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
else
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
minX - = 0 ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
// Crop bottom
y = bmp . Height - 1 ;
c = backgroundColor ;
while ( y > minY & & IsBackgroundColor ( c , backgroundArgb ) )
{
c = bmp . GetPixel ( 0 , y ) ;
if ( IsBackgroundColor ( c , backgroundArgb ) )
{
for ( x = 1 ; x < bmp . Width ; x + + )
{
c = bmp . GetPixelNext ( ) ;
if ( ! IsBackgroundColor ( c , backgroundArgb ) )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
break ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
}
}
if ( IsBackgroundColor ( c , backgroundArgb ) )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
y - - ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
}
int maxY = y + 7 ;
if ( maxY > = bmp . Height )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
maxY = bmp . Height - 1 ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
// Crop right
x = bmp . Width - 1 ;
c = backgroundColor ;
while ( x > minX & & IsBackgroundColor ( c , backgroundArgb ) )
{
for ( y = minY ; y < bmp . Height ; y + + )
{
c = bmp . GetPixel ( x , y ) ;
if ( ! IsBackgroundColor ( c , backgroundArgb ) )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
break ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
}
if ( IsBackgroundColor ( c , backgroundArgb ) )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
x - - ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
}
int maxX = x + 7 ;
if ( maxX > = bmp . Width )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
maxX = bmp . Width - 1 ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
bmp . UnlockImage ( ) ;
Bitmap bmpImage = bmp . GetBitmap ( ) ;
if ( bmpImage . Width > 1 & & bmpImage . Height > 1 & & maxX - minX > 0 & & maxY - minY > 0 )
{
Bitmap bmpCrop = bmpImage . Clone ( new Rectangle ( minX , minY , maxX - minX , maxY - minY ) , bmpImage . PixelFormat ) ;
return bmpCrop ;
}
return ( Bitmap ) bmpImage . Clone ( ) ;
}
private static bool IsBackgroundColor ( Color c , int backgroundArgb )
{
return c . A = = 0 | | c . ToArgb ( ) = = backgroundArgb ;
}
public static void GenerateBitmap ( byte [ ] data , FastBitmap bmp , int startY , int dataAddress , List < Color > fourColors , int addY )
{
int index = 0 ;
bool onlyHalf = false ;
int y = startY ;
int x = 0 ;
int colorZeroValue = fourColors [ 0 ] . ToArgb ( ) ;
while ( y < bmp . Height & & dataAddress + index + 2 < data . Length )
{
int runLength ;
int color ;
bool restOfLine ;
index + = DecodeRle ( dataAddress + index , data , out color , out runLength , ref onlyHalf , out restOfLine ) ;
if ( restOfLine )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
runLength = bmp . Width - x ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
Color c = fourColors [ color ] ; // set color via the four colors
for ( int i = 0 ; i < runLength ; i + + , x + + )
{
if ( x > = bmp . Width - 1 )
{
if ( y < bmp . Height & & x < bmp . Width & & c ! = fourColors [ 0 ] )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
bmp . SetPixel ( x , y , c ) ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
if ( onlyHalf )
{
onlyHalf = false ;
index + + ;
}
x = 0 ;
y + = addY ;
break ;
}
if ( y < bmp . Height & & c . ToArgb ( ) ! = colorZeroValue )
2019-01-19 14:40:37 +01:00
{
2015-08-26 22:42:40 +02:00
bmp . SetPixel ( x , y , c ) ;
2019-01-19 14:40:37 +01:00
}
2015-08-26 22:42:40 +02:00
}
}
}
private static int DecodeRle ( int index , byte [ ] data , out int color , out int runLength , ref bool onlyHalf , out bool restOfLine )
{
//Value Bits n=length, c=color
//1-3 4 nncc (half a byte)
//4-15 8 00nnnncc (one byte)
//16-63 12 0000nnnnnncc (one and a half byte)
//64-255 16 000000nnnnnnnncc (two bytes)
// When reaching EndOfLine, index is byte aligned (skip 4 bits if necessary)
restOfLine = false ;
byte b1 = data [ index ] ;
byte b2 = data [ index + 1 ] ;
if ( onlyHalf )
{
byte b3 = data [ index + 2 ] ;
b1 = ( byte ) ( ( ( b1 & Helper . B00001111 ) < < 4 ) | ( ( b2 & Helper . B11110000 ) > > 4 ) ) ;
b2 = ( byte ) ( ( ( b2 & Helper . B00001111 ) < < 4 ) | ( ( b3 & Helper . B11110000 ) > > 4 ) ) ;
}
if ( b1 > > 2 = = 0 )
{
runLength = ( b1 < < 6 ) | ( b2 > > 2 ) ;
color = b2 & Helper . B00000011 ;
if ( runLength = = 0 )
{
// rest of line + skip 4 bits if Only half
restOfLine = true ;
if ( onlyHalf )
{
onlyHalf = false ;
return 3 ;
}
}
return 2 ;
}
if ( b1 > > 4 = = 0 )
{
runLength = ( b1 < < 2 ) | ( b2 > > 6 ) ;
color = ( b2 & Helper . B00110000 ) > > 4 ;
if ( onlyHalf )
{
onlyHalf = false ;
return 2 ;
}
onlyHalf = true ;
return 1 ;
}
if ( b1 > > 6 = = 0 )
{
runLength = b1 > > 2 ;
color = b1 & Helper . B00000011 ;
return 1 ;
}
runLength = b1 > > 6 ;
color = ( b1 & Helper . B00110000 ) > > 4 ;
if ( onlyHalf )
{
onlyHalf = false ;
return 1 ;
}
onlyHalf = true ;
return 0 ;
}
//private static int DecodeRleNonOptimized(int index, byte[] data, out int color, out int runLength, ref bool onlyHalf, out bool restOfLine)
//{
// //Value Bits n=length, c=color
// //1-3 4 n n c c (half a byte)
// //4-15 8 0 0 n n n n c c (one byte)
// //16-63 12 0 0 0 0 n n n n n n c c (one and a half byte)
// //64-255 16 0 0 0 0 0 0 n n n n n n n n c c (two bytes)
// // When reaching EndOfLine, index is byte aligned (skip 4 bits if necessary)
// restOfLine = false;
// string binary2 = Helper.GetBinaryString(data, index, 3);
// if (onlyHalf)
// binary2 = binary2.Substring(4);
// if (binary2.StartsWith("000000"))
// {
// runLength = (int)Helper.GetUInt32FromBinaryString(binary2.Substring(6, 8));
// color = (int)Helper.GetUInt32FromBinaryString(binary2.Substring(14, 2));
// if (runLength == 0)
// {
// // rest of line + skip 4 bits if Only half
// restOfLine = true;
// if (onlyHalf)
// {
// onlyHalf = false;
// return 3;
// }
// }
// return 2;
// }
// if (binary2.StartsWith("0000"))
// {
// runLength = (int)Helper.GetUInt32FromBinaryString(binary2.Substring(4, 6));
// color = (int)Helper.GetUInt32FromBinaryString(binary2.Substring(10, 2));
// if (onlyHalf)
// {
// onlyHalf = false;
// return 2;
// }
// onlyHalf = true;
// return 1;
// }
// if (binary2.StartsWith("00"))
// {
// runLength = (int)Helper.GetUInt32FromBinaryString(binary2.Substring(2, 4));
// color = (int)Helper.GetUInt32FromBinaryString(binary2.Substring(6, 2));
// return 1;
// }
// runLength = (int)Helper.GetUInt32FromBinaryString(binary2.Substring(0, 2));
// color = (int)Helper.GetUInt32FromBinaryString(binary2.Substring(2, 2));
// if (onlyHalf)
// {
// onlyHalf = false;
// return 1;
// }
// onlyHalf = true;
// return 0;
//}
}
}