2
2
// Licensed under the Six Labors Split License.
3
3
4
4
using System . Diagnostics . CodeAnalysis ;
5
- using SixLabors . ImageSharp . Formats . Bmp ;
5
+ using SixLabors . ImageSharp . Formats . Cur ;
6
+ using SixLabors . ImageSharp . Formats . Ico ;
6
7
using SixLabors . ImageSharp . PixelFormats ;
7
8
8
9
namespace SixLabors . ImageSharp . Formats . Icon ;
9
10
10
- internal abstract class IconEncoderCore : IImageEncoderInternals
11
+ internal abstract class IconEncoderCore ( IconFileType iconFileType )
12
+ : IImageEncoderInternals
11
13
{
12
- protected IconDir FileHeader { get ; set ; }
14
+ private IconDir fileHeader ;
13
15
14
- protected IconDirEntry [ ] ? Entries { get ; set ; }
16
+ private IconFrameMetadata [ ] ? entries ;
15
17
16
18
public void Encode < TPixel > (
17
19
Image < TPixel > image ,
@@ -26,44 +28,93 @@ public void Encode<TPixel>(
26
28
27
29
// Stream may not at 0.
28
30
long basePosition = stream . Position ;
29
- this . GetHeader ( image ) ;
31
+ this . InitHeader ( image ) ;
30
32
31
- int dataOffset = IconDir . Size + ( IconDirEntry . Size * this . Entries . Length ) ;
33
+ int dataOffset = IconDir . Size + ( IconDirEntry . Size * this . entries . Length ) ;
32
34
_ = stream . Seek ( dataOffset , SeekOrigin . Current ) ;
33
35
34
36
for ( int i = 0 ; i < image . Frames . Count ; i ++ )
35
37
{
36
38
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 ;
39
52
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 ++ )
42
55
{
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 ( ) ,
50
73
} ;
51
74
52
75
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 ;
54
77
}
55
78
56
79
long endPosition = stream . Position ;
57
80
_ = 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 )
60
83
{
61
- entry . WriteTo ( stream ) ;
84
+ frame . Entry . WriteTo ( stream ) ;
62
85
}
63
86
64
87
_ = stream . Seek ( endPosition , SeekOrigin . Begin ) ;
65
88
}
66
89
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
+ }
69
120
}
0 commit comments