/// <summary> /// 指定したAVI_CONTAINER構造体にAVIファイルの情報を格納すると共に, /// ファイルにヘッダー情報を書き込みます. /// </summary> /// <param name="file">書き込み対象のファイル</param> /// <param name="scale"></param> /// <param name="rate"></param> /// <param name="compressed"></param> public unsafe bool Open( string file, uint scale, uint rate, int width, int height, bool compressed, bool transparent, IntPtr hWnd ) { #if DEBUG Console.WriteLine( "AviWriterEx.Open(string,uint,uint,bool,IntPtr)" ); #endif m_stream = new BinaryWriter( new FileStream( file, FileMode.Create, FileAccess.Write ) ); float fps = (float)rate / (float)scale; m_main_header.dwMicroSecPerFrame = (uint)(1.0e6 / fps);// ! 1秒は10^6μ秒 m_main_header.dwReserved1 = 0; m_main_header.dwFlags = 2064; m_main_header.dwInitialFrames = 0; m_main_header.dwStreams = 0; m_main_header.dwScale = scale; m_main_header.dwRate = rate; m_main_header.dwStart = 0; m_main_header.dwLength = 0; m_rate = rate; m_scale = scale; Util.fwrite( "RIFF", m_stream ); Util.WriteDWORD( 0, m_stream ); Util.fwrite( "AVI ", m_stream ); Util.fwrite( "LIST", m_stream ); Util.WriteDWORD( 0x9cc, m_stream ); Util.fwrite( "hdrl", m_stream ); m_current_chunk = 0; m_position_in_chunk = 0L; m_std_index = new AVISTDINDEX( 0L ); m_super_index = new AVISUPERINDEX( 0 ); m_riff_position = 0x4; m_compressed = compressed; m_is_transparent = transparent; m_stream_fcc_handler = 0; m_hwnd = hWnd; m_file = file; m_opened = true; if ( m_is_first ) { int stride = 0; using ( Bitmap b = new Bitmap( width, height, m_is_transparent ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb ) ) { BitmapData bd = b.LockBits( new Rectangle( 0, 0, width, height ), ImageLockMode.ReadOnly, b.PixelFormat ); stride = bd.Stride; b.UnlockBits( bd ); } m_is_first = false; m_main_header.dwWidth = (uint)width; m_main_header.dwHeight = (uint)height; m_main_header.dwMaxBytesPerSec = (uint)(stride * height * frameRate); m_main_header.dwStreams = 1; m_main_header.dwSuggestedBufferSize = (uint)(stride * height); m_linesize = stride; m_stream_header.fccType = Util.mmioFOURCC( "vids" ); m_stream_header.fccHandler = 0; m_stream_header.dwFlags = 0; m_stream_header.dwReserved1 = 0; m_stream_header.dwInitialFrames = 0; m_stream_header.dwScale = m_scale; m_stream_header.dwRate = m_rate; m_stream_header.dwStart = 0; m_stream_header.dwSuggestedBufferSize = m_main_header.dwSuggestedBufferSize; m_stream_header.dwQuality = 0; m_stream_header.dwSampleSize = 0; Util.aviWriteMainHeader( m_main_header, m_stream ); Util.fwrite( "LIST", m_stream ); Util.WriteDWORD( 0x874, m_stream ); Util.fwrite( "strl", m_stream ); Util.aviWriteStreamHeader( m_stream_header, m_main_header, m_stream ); Util.fwrite( "strf", m_stream ); BITMAPINFOHEADER bih = new BITMAPINFOHEADER(); //(BITMAPINFOHEADER)Marshal.PtrToStructure( Marshal.AllocHGlobal( sizeof( BITMAPINFOHEADER ) ), typeof( BITMAPINFOHEADER ) ); bih.biSize = (uint)(Marshal.SizeOf( bih )); bih.biWidth = width; bih.biHeight = height; bih.biPlanes = 1; bih.biBitCount = m_is_transparent ? (short)32 : (short)24; bih.biCompression = 0;//BI_RGB bih.biSizeImage = (uint)(stride * height); bih.biXPelsPerMeter = 0; bih.biYPelsPerMeter = 0; bih.biClrUsed = 0; bih.biClrImportant = 0; if ( m_compressed ) { m_p_compvar = Marshal.AllocHGlobal( sizeof( COMPVARS ) ); m_compvar = (COMPVARS*)m_p_compvar.ToPointer(); byte[] buf = new byte[sizeof( COMPVARS )]; for ( int i = 0; i < buf.Length; i++ ) { buf[i] = 0x0; } Marshal.Copy( buf, 0, m_p_compvar, buf.Length ); m_compvar->cbSize = sizeof( COMPVARS ); int ret = VCM.ICCompressorChoose( m_hwnd, 0, IntPtr.Zero, IntPtr.Zero, m_compvar, "Select Video Compressor" ); if ( ret == 0 ) { m_opened = false; Marshal.FreeHGlobal( m_p_compvar ); m_stream.Close(); return false; } if ( m_compvar->hic != 0 ) { m_p_bitmapinfo_in = Marshal.AllocHGlobal( sizeof( BITMAPINFO ) ); m_bitmapinfo_in = (BITMAPINFO*)m_p_bitmapinfo_in.ToPointer(); buf = new byte[sizeof( BITMAPINFO )]; for ( int i = 0; i < buf.Length; i++ ) { buf[i] = 0x0; } Marshal.Copy( buf, 0, m_p_bitmapinfo_in, buf.Length ); m_bitmapinfo_in->bmiHeader = bih; uint dwSize = VCM.ICCompressGetFormatSize( m_compvar->hic, m_bitmapinfo_in ); #if DEBUG Console.WriteLine( "m_compvar->hic=" + m_compvar->hic ); Console.WriteLine( "ICCompressGetFormatSize=" + dwSize ); #endif m_p_bitmapinfo_out = Marshal.AllocHGlobal( (int)dwSize ); m_bitmapinfo_out = (BITMAPINFO*)m_p_bitmapinfo_out.ToPointer(); buf = new byte[dwSize]; for ( int i = 0; i < buf.Length; i++ ) { buf[i] = 0x0; } Marshal.Copy( buf, 0, m_p_bitmapinfo_out, buf.Length ); VCM.ICCompressGetFormat( m_compvar->hic, m_bitmapinfo_in, m_bitmapinfo_out ); m_bih_compression = m_bitmapinfo_out->bmiHeader.biCompression; #if DEBUG Console.WriteLine( "AddFrame(Bitmap)" ); Console.WriteLine( " biout.biSize=" + m_bitmapinfo_out->bmiHeader.biSize ); #endif VCM.ICSeqCompressFrameStart( m_compvar, m_bitmapinfo_in ); bih = m_bitmapinfo_out->bmiHeader; Util.WriteDWORD( bih.biSize, m_stream );// infoHeaderのサイズ m_bitmapinfo_out->Write( m_stream ); } else { m_compressed = false; Util.WriteDWORD( bih.biSize, m_stream );// infoHeaderのサイズ bih.Write( m_stream ); } } else { Util.WriteDWORD( bih.biSize, m_stream );// infoHeaderのサイズ bih.Write( m_stream ); } m_super_index_position = m_stream.BaseStream.Position; Util.fwrite( "indx", m_stream ); //fcc Util.WriteDWORD( 0x7f8, m_stream ); // cb Util.WriteWORD( (byte)0x4, m_stream ); // wLongsPerEntry Util.WriteBYTE( 0x0, m_stream ); // bIndexSubType Util.WriteBYTE( Util.AVI_INDEX_OF_INDEXES, m_stream );// bIndexType Util.WriteDWORD( 0x0, m_stream ); // nEntriesInUse Util.fwrite( "00db", m_stream ); // dwChunkId Util.WriteDWORD( 0x0, m_stream ); Util.WriteDWORD( 0x0, m_stream ); Util.WriteDWORD( 0x0, m_stream ); for ( int ii = 1; ii <= 126; ii++ ) { Util.WriteQWORD( 0x0, m_stream ); Util.WriteDWORD( 0x0, m_stream ); Util.WriteDWORD( 0x0, m_stream ); } Util.fwrite( "LIST", m_stream ); Util.WriteDWORD( 0x104, m_stream ); Util.fwrite( "odml", m_stream ); Util.fwrite( "dmlh", m_stream ); Util.WriteDWORD( 0xf8, m_stream ); Util.WriteDWORD( 0x0, m_stream );//ここ後で更新するべき for ( int ii = 1; ii <= 61; ii++ ) { Util.WriteDWORD( 0x0, m_stream ); } Util.fwrite( "JUNK", m_stream ); Util.WriteDWORD( 0x60c, m_stream ); Util.WriteDWORD( 0, m_stream );//"This"が将来登録されたらやばいので string msg = "This file was generated by [email protected];VfwBugCompatible=" + VfwBugCompatible; const int tlen = 1544; int remain = tlen - msg.Length; Util.fwrite( msg, m_stream ); for ( int i = 1; i <= remain; i++ ) { m_stream.Write( (byte)0 ); } m_junk_length = 0xff4; Util.fwrite( "LIST", m_stream ); m_movi_position = m_stream.BaseStream.Position; Util.WriteDWORD( 0, m_stream );// call bmpQWordWrite( 0, avi%fp ) !// ******************ココの数字は一番最後に書き換える必要あり2040~2043あとdwTotalFrames(48~51)も Util.fwrite( "movi", m_stream ); m_next_framedata_position = m_stream.BaseStream.Position; m_std_index.SetBaseOffset( (ulong)m_next_framedata_position ); m_super_index.nEntriesInUse++; } return true; }
/// <summary> /// aviファイルにフレームを1つ追加します. /// </summary> /// <param name="bmp"></param> public void AddFrame( Bitmap bmp ) { int i, width, height, lineSize; if ( bmp.Width != m_width || bmp.Height != m_height ) { throw new Exception( "bitmap size mismatch" ); } // BitmapDataからビットマップデータと、BITMPAINFOHEADERを取り出す BitmapData bmpDat = bmp.LockBits( new Rectangle( 0, 0, (int)bmp.Width, (int)bmp.Height ), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb ); int address = bmpDat.Scan0.ToInt32(); byte[] bitmapData = new byte[bmpDat.Stride * bmpDat.Height]; Marshal.Copy( new IntPtr( address ), bitmapData, 0, bitmapData.Length ); if ( m_is_first ) {//then m_is_first = false; this.m_main_header.dwWidth = (uint)m_width; this.m_main_header.dwHeight = (uint)m_height; this.m_main_header.dwMaxBytesPerSec = (uint)(bmpDat.Stride * bmpDat.Height * this.frameRate);// bmp%infoHeader%SizeImage * avi%frameRate this.m_main_header.dwStreams = 1; this.m_main_header.dwSuggestedBufferSize = (uint)(bmpDat.Stride * bmpDat.Height);// bmp.infoHeader%SizeImage m_linesize = bmpDat.Stride; this.m_stream_header.fccType = Util.mmioFOURCC( "vids" ); this.m_stream_header.fccHandler = 0; this.m_stream_header.dwFlags = 0; this.m_stream_header.dwReserved1 = 0; this.m_stream_header.dwInitialFrames = 0; this.m_stream_header.dwScale = m_scale; this.m_stream_header.dwRate = m_rate; this.m_stream_header.dwStart = 0; this.m_stream_header.dwSuggestedBufferSize = this.m_main_header.dwSuggestedBufferSize; this.m_stream_header.dwQuality = 0; this.m_stream_header.dwSampleSize = 0; Util.aviWriteMainHeader( m_main_header, m_stream ); Util.fwrite( "LIST", this.m_stream );// i = fwrite( 'LIST', 1, 4, avi%fp ) Util.WriteDWORD( 0x874, this.m_stream );// call bmpQWordWrite( 130, avi%fp ) Util.fwrite( "strl", this.m_stream );// i = fwrite( 'strl', 1, 4, avi%fp ) Util.aviWriteStreamHeader( m_stream_header, m_main_header, m_stream );// avi ) Util.fwrite( "strf", this.m_stream );// i = fwrite( 'strf', 1, 4, avi%fp ) Util.WriteDWORD( 0x28, this.m_stream ); //call bmpQWordWrite( 40, avi%fp ) !// infoHeaderのサイズ BITMAPINFOHEADER bih = new BITMAPINFOHEADER(); bih.biSize = (uint)(Marshal.SizeOf( bih )); bih.biWidth = bmpDat.Width; bih.biHeight = bmpDat.Height; bih.biPlanes = 1; bih.biBitCount = 24; bih.biCompression = 0;//BI_RGB bih.biSizeImage = (uint)(bmpDat.Stride * bmpDat.Height); bih.biXPelsPerMeter = 0; bih.biYPelsPerMeter = 0; bih.biClrUsed = 0; bih.biClrImportant = 0; bih.Write( m_stream ); /*fwrite( "strn", this.fp ); WriteDWORD( 6, this.fp ); fwrite( "VIDEO", this.fp ); WriteBYTE( 0, this.fp );*/ Util.fwrite( "indx", this.m_stream ); //fcc Util.WriteDWORD( 0x7f8, this.m_stream ); // cb Util.WriteWORD( (byte)0x4, this.m_stream ); // wLongsPerEntry Util.WriteBYTE( 0x0, this.m_stream ); // bIndexSubType Util.WriteBYTE( Util.AVI_INDEX_OF_INDEXES, this.m_stream );// bIndexType Util.WriteDWORD( 0x0, this.m_stream ); // nEntriesInUse Util.fwrite( "00db", this.m_stream ); // dwChunkId Util.WriteDWORD( 0x0, this.m_stream ); Util.WriteDWORD( 0x0, this.m_stream ); Util.WriteDWORD( 0x0, this.m_stream ); for ( int ii = 1; ii <= 126; ii++ ) { Util.WriteQWORD( 0x0, this.m_stream ); Util.WriteDWORD( 0x0, this.m_stream ); Util.WriteDWORD( 0x0, this.m_stream ); } Util.fwrite( "LIST", this.m_stream ); Util.WriteDWORD( 0x104, m_stream ); Util.fwrite( "odml", this.m_stream ); Util.fwrite( "dmlh", m_stream ); Util.WriteDWORD( 0xf8, m_stream ); Util.WriteDWORD( 0x0, m_stream );//ここ後で更新するべき for ( int ii = 1; ii <= 61; ii++ ) { Util.WriteDWORD( 0x0, m_stream ); } Util.fwrite( "JUNK", this.m_stream );// i = fwrite( 'JUNK', 1, 4, avi%fp ) Util.WriteDWORD( 0x60c, m_stream ); Util.WriteDWORD( 0, m_stream );//"This"が将来登録されたらやばいので Util.fwrite( "This file was generated by RawAvi@LipSync", this.m_stream ); //WriteDWORD( 1503, this.fp );// call bmpQWordWrite( 1802, avi%fp ) for ( i = 1; i <= 1503; i++ ) {//do i = 1, 1802 this.m_stream.Write( (byte)0 );// call fputc( 0, avi%fp ) }//end do m_junk_length = 0xff4; Util.fwrite( "LIST", this.m_stream );// i = fwrite( 'LIST', 1, 4, avi%fp ) m_movi_position = m_stream.BaseStream.Position; Util.WriteDWORD( 0, this.m_stream );// call bmpQWordWrite( 0, avi%fp ) !// ******************ココの数字は一番最後に書き換える必要あり2040~2043あとdwTotalFrames(48~51)も Util.fwrite( "movi", this.m_stream );// i = fwrite( 'movi', 1, 4, avi%fp ) m_next_framedata_position = m_stream.BaseStream.Position; m_std_index.SetBaseOffset( (ulong)m_next_framedata_position ); m_super_index.nEntriesInUse++; }//end if if ( m_next_framedata_position != m_stream.BaseStream.Position ) { m_stream.BaseStream.Seek( m_next_framedata_position, SeekOrigin.Begin ); } long chunk_size = m_next_framedata_position - m_riff_position; #if DEBUG // MessageBox.Show( "chunk_size=" + chunk_size ); #endif if ( (m_current_chunk == 0 && chunk_size > m_split_sreshold) || (m_current_chunk > 0 && chunk_size > SRESHOLD )) { // AVIXリストへの書き込みに移行 UpdateIndex(); m_stream.BaseStream.Seek( m_avix_position, SeekOrigin.Begin ); Util.fwrite( "RIFF", m_stream ); m_riff_position = m_stream.BaseStream.Position; #if DEBUG // fp.Flush(); // MessageBox.Show( "m_riff_position=" + m_riff_position ); #endif Util.WriteDWORD( 0, m_stream ); Util.fwrite( "AVIX", m_stream ); long current = m_stream.BaseStream.Position; if ( (current + 12) % 0x800 != 0 ) { long additional = (current + 20) % 0x800; additional = 0x800 - ((current + 20) % 0x800); m_junk_length = (int)additional + 20; Util.fwrite( "JUNK", m_stream ); Util.WriteDWORD( (uint)additional, m_stream ); for ( long ii = 0; ii < additional; ii++ ) { Util.WriteBYTE( (byte)0, m_stream ); } } else { m_junk_length = 0; } m_junk_length = 0; Util.fwrite( "LIST", m_stream ); m_movi_position = m_stream.BaseStream.Position; Util.WriteDWORD( 0, m_stream );//後で更新するべき Util.fwrite( "movi", m_stream ); m_next_framedata_position = m_stream.BaseStream.Position; m_std_index.aIndex.Clear(); m_std_index.SetBaseOffset( (ulong)m_next_framedata_position ); m_current_chunk++; m_super_index.nEntriesInUse++; } // フレームを書き込む処理 width = (int)this.m_main_header.dwWidth; height = (int)this.m_main_header.dwHeight; if ( width != bmpDat.Width ) {//then //aviAddFrame = -1 return; }//end if if ( height != bmpDat.Height ) {//then //aviAddframe = -1 return; }//end if lineSize = bmpDat.Stride;// int( (width * 24 + 31) / 32 ) * 4 m_std_index.AddIndex( (uint)((ulong)m_stream.BaseStream.Position - m_std_index.qwBaseOffset) + 8, (uint)(lineSize * height) ); Util.fwrite( "00db", this.m_stream );// i = fwrite( '00db', 1, 4, avi%fp ) Util.WriteDWORD( m_main_header.dwSuggestedBufferSize, m_stream );// call bmpQWordWrite( avi%mainHeader%dwSuggestedBufferSize, avi%fp ) m_stream.Write( bitmapData ); m_next_framedata_position = m_stream.BaseStream.Position; _avisuperindex_entry entry = m_super_index.aIndex[m_current_chunk]; entry.dwDuration++; m_super_index.aIndex[m_current_chunk] = entry;// avi%noOfFrame = avi%noOfFrame + 1 this.m_stream.Flush();// aviAddFrame = fflush( avi%fp ) bmp.UnlockBits( bmpDat ); }// end function
private static void SaveCor( Bitmap item, Point hotspot, Stream stream, ushort type, Color transp ) { IconFileHeader ifh = new IconFileHeader(); ifh.icoReserved = 0x0; ifh.icoResourceCount = 1; ifh.icoResourceType = type; ifh.Write( stream ); IconInfoHeader iif = new IconInfoHeader(); BITMAPINFOHEADER bih = new BITMAPINFOHEADER(); iif.Width = (byte)item.Width; iif.Height = (byte)item.Height; iif.ColorCount = 0; iif.Reserved1 = 0; iif.Reserved2 = (ushort)hotspot.X; iif.Reserved3 = (ushort)hotspot.Y; #if RGB24 int linesize = ((item.Width * 24 + 31) / 32) * 4; #else int linesize = ((item.Width * 32 + 31) / 32) * 4; #endif int linesize_mask = ((item.Width * 1 + 31) / 32) * 4; int size = linesize * item.Height + linesize_mask * item.Height + 40; #if DEBUG Console.WriteLine( "linesize=" + linesize ); #endif iif.icoDIBSize = (uint)size; iif.icoDIBOffset = 0x16; iif.Write( stream ); bih.biSize = 40; bih.biWidth = item.Width; #if RGB24 bih.biHeight = item.Height * 2; #else bih.biHeight = item.Height * 2; #endif bih.biPlanes = 1; #if RGB24 bih.biBitCount = 24; #else bih.biBitCount = 32; #endif bih.biCompression = 0; bih.biSizeImage = (uint)(linesize * item.Height); bih.biXPelsPerMeter = 0;// (int)(item.HorizontalResolution / 2.54e-2); bih.biYPelsPerMeter = 0;// (int)(item.VerticalResolution / 2.54e-2); bih.biClrUsed = 0; bih.biClrImportant = 0; bih.Write( stream ); for ( int y = item.Height - 1; y >= 0; y-- ) { int count = 0; for ( int x = 0; x < item.Width; x++ ) { Color c = item.GetPixel( x, y ); stream.WriteByte( (byte)c.B ); stream.WriteByte( (byte)c.G ); stream.WriteByte( (byte)c.R ); #if DEBUG if ( c.R != transp.R || c.G != transp.G || c.B != transp.B ) { Console.WriteLine( "color=" + c ); } #endif #if RGB24 count += 3; #else stream.WriteByte( (byte)c.A ); count += 4; #endif } for ( int i = count; i < linesize; i++ ) { stream.WriteByte( 0x0 ); } } for ( int y = item.Height - 1; y >= 0; y-- ) { int count = 0; byte v = 0x0; int tcount = 0; for ( int x = 0; x < item.Width; x++ ) { Color c = item.GetPixel( x, y ); byte tr = 0x0; if ( c.R == transp.R && c.G == transp.G && c.B == transp.B ){ tr = 0x1; } v = (byte)((byte)(v << 1) | (byte)(tr & 0x1)); tcount++; if ( tcount == 8 ) { stream.WriteByte( v ); count++; tcount = 0; v = 0x0; } } if ( 0 < tcount ) { v = (byte)(v << (9 - tcount)); stream.WriteByte( v ); count++; } for ( int i = count; i < linesize_mask; i++ ) { stream.WriteByte( 0x0 ); } } }