22// Licensed under the Six Labors Split License.
33
44using System . Diagnostics . CodeAnalysis ;
5- using SixLabors . ImageSharp . Formats . Bmp ;
5+ using SixLabors . ImageSharp . Formats . Cur ;
6+ using SixLabors . ImageSharp . Formats . Ico ;
67using SixLabors . ImageSharp . PixelFormats ;
78
89namespace SixLabors . ImageSharp . Formats . Icon ;
910
10- internal abstract class IconEncoderCore : IImageEncoderInternals
11+ internal abstract class IconEncoderCore ( IconFileType iconFileType )
12+ : IImageEncoderInternals
1113{
12- protected IconDir FileHeader { get ; set ; }
14+ private IconDir fileHeader ;
1315
14- protected IconDirEntry [ ] ? Entries { get ; set ; }
16+ private IconFrameMetadata [ ] ? entries ;
1517
1618 public void Encode < TPixel > (
1719 Image < TPixel > image ,
@@ -26,44 +28,93 @@ public void Encode<TPixel>(
2628
2729 // Stream may not at 0.
2830 long basePosition = stream . Position ;
29- this . GetHeader ( image ) ;
31+ this . InitHeader ( image ) ;
3032
31- int dataOffset = IconDir . Size + ( IconDirEntry . Size * this . Entries . Length ) ;
33+ int dataOffset = IconDir . Size + ( IconDirEntry . Size * this . entries . Length ) ;
3234 _ = stream . Seek ( dataOffset , SeekOrigin . Current ) ;
3335
3436 for ( int i = 0 ; i < image . Frames . Count ; i ++ )
3537 {
3638 ImageFrame < TPixel > frame = image . Frames [ i ] ;
37- this . Entries [ i ] . ImageOffset = ( uint ) stream . Position ;
38- Image < TPixel > img = new ( Configuration . Default , frame . PixelBuffer , new ( ) ) ;
39+ int width = this . entries [ i ] . Entry . Width ;
40+ if ( width is 0 )
41+ {
42+ width = frame . Width ;
43+ }
44+
45+ int height = this . entries [ i ] . Entry . Height ;
46+ if ( height is 0 )
47+ {
48+ height = frame . Height ;
49+ }
50+
51+ this . entries [ i ] . Entry . ImageOffset = ( uint ) stream . Position ;
3952
40- // Note: this encoder are not supported PNG Data.
41- BmpEncoder encoder = new ( )
53+ Image < TPixel > img = new ( width , height ) ;
54+ for ( int y = 0 ; y < height ; y ++ )
4255 {
43- ProcessedAlphaMask = true ,
44- UseDoubleHeight = true ,
45- SkipFileHeader = true ,
46- SupportTransparency = false ,
47- BitsPerPixel = this . Entries [ i ] . BitCount is 0
48- ? BmpBitsPerPixel . Pixel8
49- : ( BmpBitsPerPixel ? ) this . Entries [ i ] . BitCount
56+ frame . PixelBuffer . DangerousGetRowSpan ( y ) [ ..width ] . CopyTo ( img . GetRootFramePixelBuffer ( ) . DangerousGetRowSpan ( y ) ) ;
57+ }
58+
59+ QuantizingImageEncoder encoder = this . entries [ i ] . Compression switch
60+ {
61+ IconFrameCompression . Bmp => new Bmp . BmpEncoder ( )
62+ {
63+ ProcessedAlphaMask = true ,
64+ UseDoubleHeight = true ,
65+ SkipFileHeader = true ,
66+ SupportTransparency = false ,
67+ BitsPerPixel = iconFileType is IconFileType . ICO
68+ ? ( Bmp . BmpBitsPerPixel ? ) this . entries [ i ] . Entry . BitCount
69+ : Bmp . BmpBitsPerPixel . Pixel24 // TODO: Here you need to switch to selecting the corresponding value according to the size of the image
70+ } ,
71+ IconFrameCompression . Png => new Png . PngEncoder ( ) ,
72+ _ => throw new NotSupportedException ( ) ,
5073 } ;
5174
5275 encoder . Encode ( img , stream ) ;
53- this . Entries [ i ] . BytesInRes = this . Entries [ i ] . ImageOffset - ( uint ) stream . Position ;
76+ this . entries [ i ] . Entry . BytesInRes = ( uint ) stream . Position - this . entries [ i ] . Entry . ImageOffset ;
5477 }
5578
5679 long endPosition = stream . Position ;
5780 _ = stream . Seek ( basePosition , SeekOrigin . Begin ) ;
58- this . FileHeader . WriteTo ( stream ) ;
59- foreach ( IconDirEntry entry in this . Entries )
81+ this . fileHeader . WriteTo ( stream ) ;
82+ foreach ( IconFrameMetadata frame in this . entries )
6083 {
61- entry . WriteTo ( stream ) ;
84+ frame . Entry . WriteTo ( stream ) ;
6285 }
6386
6487 _ = stream . Seek ( endPosition , SeekOrigin . Begin ) ;
6588 }
6689
67- [ MemberNotNull ( nameof ( Entries ) ) ]
68- protected abstract void GetHeader ( in Image image ) ;
90+ [ MemberNotNull ( nameof ( entries ) ) ]
91+ private void InitHeader ( in Image image )
92+ {
93+ this . fileHeader = new ( iconFileType , ( ushort ) image . Frames . Count ) ;
94+ this . entries = iconFileType switch
95+ {
96+ IconFileType . ICO =>
97+ image . Frames . Select ( i =>
98+ {
99+ IcoFrameMetadata metadata = i . Metadata . GetIcoMetadata ( ) ;
100+ return new IconFrameMetadata ( metadata . Compression , metadata . ToIconDirEntry ( ) ) ;
101+ } ) . ToArray ( ) ,
102+ IconFileType . CUR =>
103+ image . Frames . Select ( i =>
104+ {
105+ CurFrameMetadata metadata = i . Metadata . GetCurMetadata ( ) ;
106+ return new IconFrameMetadata ( metadata . Compression , metadata . ToIconDirEntry ( ) ) ;
107+ } ) . ToArray ( ) ,
108+ _ => throw new NotSupportedException ( ) ,
109+ } ;
110+ }
111+
112+ internal sealed class IconFrameMetadata ( IconFrameCompression compression , IconDirEntry iconDirEntry )
113+ {
114+ private IconDirEntry iconDirEntry = iconDirEntry ;
115+
116+ public IconFrameCompression Compression { get ; set ; } = compression ;
117+
118+ public ref IconDirEntry Entry => ref this . iconDirEntry ;
119+ }
69120}
0 commit comments