Skip to content

Commit 9bdbc52

Browse files
Migrate ICO and CUR
1 parent a694323 commit 9bdbc52

22 files changed

+1034
-139
lines changed

src/ImageSharp/Formats/Bmp/BmpMetadata.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ BmpInfoHeaderType.WinVersion5 or
143143
public FormatConnectingMetadata ToFormatConnectingMetadata()
144144
=> new()
145145
{
146+
EncodingType = this.BitsPerPixel <= BmpBitsPerPixel.Bit8
147+
? EncodingType.Lossy
148+
: EncodingType.Lossless,
146149
PixelTypeInfo = this.GetPixelTypeInfo()
147150
};
148151

src/ImageSharp/Formats/Cur/CurDecoderCore.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,30 @@ public CurDecoderCore(DecoderOptions options)
1515
}
1616

1717
protected override void SetFrameMetadata(
18-
ImageFrameMetadata metadata,
18+
ImageMetadata imageMetadata,
19+
ImageFrameMetadata frameMetadata,
20+
int index,
1921
in IconDirEntry entry,
2022
IconFrameCompression compression,
2123
BmpBitsPerPixel bitsPerPixel,
2224
ReadOnlyMemory<Color>? colorTable)
2325
{
24-
CurFrameMetadata curFrameMetadata = metadata.GetCurMetadata();
26+
CurFrameMetadata curFrameMetadata = frameMetadata.GetCurMetadata();
2527
curFrameMetadata.FromIconDirEntry(entry);
2628
curFrameMetadata.Compression = compression;
2729
curFrameMetadata.BmpBitsPerPixel = bitsPerPixel;
2830
curFrameMetadata.ColorTable = colorTable;
31+
32+
if (index == 0)
33+
{
34+
CurMetadata curMetadata = imageMetadata.GetCurMetadata();
35+
curMetadata.Compression = compression;
36+
curMetadata.BmpBitsPerPixel = bitsPerPixel;
37+
curMetadata.ColorTable = colorTable;
38+
curMetadata.EncodingWidth = curFrameMetadata.EncodingWidth;
39+
curMetadata.EncodingHeight = curFrameMetadata.EncodingHeight;
40+
curMetadata.HotspotX = curFrameMetadata.HotspotX;
41+
curMetadata.HotspotY = curFrameMetadata.HotspotY;
42+
}
2943
}
3044
}

src/ImageSharp/Formats/Cur/CurFrameMetadata.cs

Lines changed: 129 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Cur;
1010
/// <summary>
1111
/// IcoFrameMetadata.
1212
/// </summary>
13-
public class CurFrameMetadata : IDeepCloneable<CurFrameMetadata>, IDeepCloneable
13+
public class CurFrameMetadata : IFormatFrameMetadata<CurFrameMetadata>
1414
{
1515
/// <summary>
1616
/// Initializes a new instance of the <see cref="CurFrameMetadata"/> class.
@@ -60,7 +60,7 @@ private CurFrameMetadata(CurFrameMetadata other)
6060
/// Gets or sets the number of bits per pixel.<br/>
6161
/// Used when <see cref="Compression"/> is <see cref="IconFrameCompression.Bmp"/>
6262
/// </summary>
63-
public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel32;
63+
public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = BmpBitsPerPixel.Bit32;
6464

6565
/// <summary>
6666
/// Gets or sets the color table, if any.
@@ -69,11 +69,75 @@ private CurFrameMetadata(CurFrameMetadata other)
6969
public ReadOnlyMemory<Color>? ColorTable { get; set; }
7070

7171
/// <inheritdoc/>
72-
public CurFrameMetadata DeepClone() => new(this);
72+
public static CurFrameMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata)
73+
{
74+
if (!metadata.PixelTypeInfo.HasValue)
75+
{
76+
return new CurFrameMetadata
77+
{
78+
BmpBitsPerPixel = BmpBitsPerPixel.Bit32,
79+
Compression = IconFrameCompression.Png
80+
};
81+
}
82+
83+
byte encodingWidth = metadata.EncodingWidth switch
84+
{
85+
> 255 => 0,
86+
<= 255 and >= 1 => (byte)metadata.EncodingWidth,
87+
_ => 0
88+
};
89+
90+
byte encodingHeight = metadata.EncodingHeight switch
91+
{
92+
> 255 => 0,
93+
<= 255 and >= 1 => (byte)metadata.EncodingHeight,
94+
_ => 0
95+
};
96+
97+
int bpp = metadata.PixelTypeInfo.Value.BitsPerPixel;
98+
BmpBitsPerPixel bbpp = bpp switch
99+
{
100+
1 => BmpBitsPerPixel.Bit1,
101+
2 => BmpBitsPerPixel.Bit2,
102+
<= 4 => BmpBitsPerPixel.Bit4,
103+
<= 8 => BmpBitsPerPixel.Bit8,
104+
<= 16 => BmpBitsPerPixel.Bit16,
105+
<= 24 => BmpBitsPerPixel.Bit24,
106+
_ => BmpBitsPerPixel.Bit32
107+
};
108+
109+
IconFrameCompression compression = IconFrameCompression.Bmp;
110+
if (bbpp is BmpBitsPerPixel.Bit32)
111+
{
112+
compression = IconFrameCompression.Png;
113+
}
114+
115+
return new CurFrameMetadata
116+
{
117+
BmpBitsPerPixel = bbpp,
118+
Compression = compression,
119+
EncodingWidth = encodingWidth,
120+
EncodingHeight = encodingHeight,
121+
ColorTable = compression == IconFrameCompression.Bmp ? metadata.ColorTable : null
122+
};
123+
}
124+
125+
/// <inheritdoc/>
126+
public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata()
127+
=> new()
128+
{
129+
PixelTypeInfo = this.GetPixelTypeInfo(),
130+
ColorTable = this.ColorTable,
131+
EncodingWidth = this.EncodingWidth,
132+
EncodingHeight = this.EncodingHeight
133+
};
73134

74135
/// <inheritdoc/>
75136
IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone();
76137

138+
/// <inheritdoc/>
139+
public CurFrameMetadata DeepClone() => new(this);
140+
77141
internal void FromIconDirEntry(IconDirEntry entry)
78142
{
79143
this.EncodingWidth = entry.Width;
@@ -84,7 +148,7 @@ internal void FromIconDirEntry(IconDirEntry entry)
84148

85149
internal IconDirEntry ToIconDirEntry()
86150
{
87-
byte colorCount = this.Compression == IconFrameCompression.Png || this.BmpBitsPerPixel > BmpBitsPerPixel.Pixel8
151+
byte colorCount = this.Compression == IconFrameCompression.Png || this.BmpBitsPerPixel > BmpBitsPerPixel.Bit8
88152
? (byte)0
89153
: (byte)ColorNumerics.GetColorCountForBitDepth((int)this.BmpBitsPerPixel);
90154

@@ -97,4 +161,65 @@ internal IconDirEntry ToIconDirEntry()
97161
ColorCount = colorCount
98162
};
99163
}
164+
165+
private PixelTypeInfo GetPixelTypeInfo()
166+
{
167+
int bpp = (int)this.BmpBitsPerPixel;
168+
PixelComponentInfo info;
169+
PixelColorType color;
170+
PixelAlphaRepresentation alpha = PixelAlphaRepresentation.None;
171+
172+
if (this.Compression is IconFrameCompression.Png)
173+
{
174+
bpp = 32;
175+
info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8);
176+
color = PixelColorType.RGB | PixelColorType.Alpha;
177+
alpha = PixelAlphaRepresentation.Unassociated;
178+
}
179+
else
180+
{
181+
switch (this.BmpBitsPerPixel)
182+
{
183+
case BmpBitsPerPixel.Bit1:
184+
info = PixelComponentInfo.Create(1, bpp, 1);
185+
color = PixelColorType.Binary;
186+
break;
187+
case BmpBitsPerPixel.Bit2:
188+
info = PixelComponentInfo.Create(1, bpp, 2);
189+
color = PixelColorType.Indexed;
190+
break;
191+
case BmpBitsPerPixel.Bit4:
192+
info = PixelComponentInfo.Create(1, bpp, 4);
193+
color = PixelColorType.Indexed;
194+
break;
195+
case BmpBitsPerPixel.Bit8:
196+
info = PixelComponentInfo.Create(1, bpp, 8);
197+
color = PixelColorType.Indexed;
198+
break;
199+
200+
// Could be 555 with padding but 565 is more common in newer bitmaps and offers
201+
// greater accuracy due to extra green precision.
202+
case BmpBitsPerPixel.Bit16:
203+
info = PixelComponentInfo.Create(3, bpp, 5, 6, 5);
204+
color = PixelColorType.RGB;
205+
break;
206+
case BmpBitsPerPixel.Bit24:
207+
info = PixelComponentInfo.Create(3, bpp, 8, 8, 8);
208+
color = PixelColorType.RGB;
209+
break;
210+
case BmpBitsPerPixel.Bit32 or _:
211+
info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8);
212+
color = PixelColorType.RGB | PixelColorType.Alpha;
213+
alpha = PixelAlphaRepresentation.Unassociated;
214+
break;
215+
}
216+
}
217+
218+
return new PixelTypeInfo(bpp)
219+
{
220+
AlphaRepresentation = alpha,
221+
ComponentInfo = info,
222+
ColorType = color
223+
};
224+
}
100225
}
Lines changed: 170 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,183 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
33

4+
using SixLabors.ImageSharp.Formats.Bmp;
5+
using SixLabors.ImageSharp.Formats.Icon;
6+
using SixLabors.ImageSharp.PixelFormats;
7+
48
namespace SixLabors.ImageSharp.Formats.Cur;
59

610
/// <summary>
7-
/// Provides Ico specific metadata information for the image.
11+
/// Provides Cur specific metadata information for the image.
812
/// </summary>
9-
public class CurMetadata : IDeepCloneable<CurMetadata>, IDeepCloneable
13+
public class CurMetadata : IFormatMetadata<CurMetadata>
1014
{
15+
/// <summary>
16+
/// Initializes a new instance of the <see cref="CurMetadata"/> class.
17+
/// </summary>
18+
public CurMetadata()
19+
{
20+
}
21+
22+
private CurMetadata(CurMetadata other)
23+
{
24+
this.Compression = other.Compression;
25+
this.HotspotX = other.HotspotX;
26+
this.HotspotY = other.HotspotY;
27+
this.EncodingWidth = other.EncodingWidth;
28+
this.EncodingHeight = other.EncodingHeight;
29+
this.BmpBitsPerPixel = other.BmpBitsPerPixel;
30+
31+
if (other.ColorTable?.Length > 0)
32+
{
33+
this.ColorTable = other.ColorTable.Value.ToArray();
34+
}
35+
}
36+
37+
/// <summary>
38+
/// Gets or sets the frame compressions format. Derived from the root frame.
39+
/// </summary>
40+
public IconFrameCompression Compression { get; set; }
41+
42+
/// <summary>
43+
/// Gets or sets the horizontal coordinates of the hotspot in number of pixels from the left. Derived from the root frame.
44+
/// </summary>
45+
public ushort HotspotX { get; set; }
46+
47+
/// <summary>
48+
/// Gets or sets the vertical coordinates of the hotspot in number of pixels from the top. Derived from the root frame.
49+
/// </summary>
50+
public ushort HotspotY { get; set; }
51+
52+
/// <summary>
53+
/// Gets or sets the encoding width. <br />
54+
/// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels or greater. Derived from the root frame.
55+
/// </summary>
56+
public byte EncodingWidth { get; set; }
57+
58+
/// <summary>
59+
/// Gets or sets the encoding height. <br />
60+
/// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels or greater. Derived from the root frame.
61+
/// </summary>
62+
public byte EncodingHeight { get; set; }
63+
64+
/// <summary>
65+
/// Gets or sets the number of bits per pixel.<br/>
66+
/// Used when <see cref="Compression"/> is <see cref="IconFrameCompression.Bmp"/>
67+
/// </summary>
68+
public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = BmpBitsPerPixel.Bit32;
69+
70+
/// <summary>
71+
/// Gets or sets the color table, if any. Derived from the root frame.<br/>
72+
/// The underlying pixel format is represented by <see cref="Bgr24"/>.
73+
/// </summary>
74+
public ReadOnlyMemory<Color>? ColorTable { get; set; }
75+
1176
/// <inheritdoc/>
12-
public CurMetadata DeepClone() => new();
77+
public static CurMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata)
78+
{
79+
int bpp = metadata.PixelTypeInfo.BitsPerPixel;
80+
BmpBitsPerPixel bbpp = bpp switch
81+
{
82+
1 => BmpBitsPerPixel.Bit1,
83+
2 => BmpBitsPerPixel.Bit2,
84+
<= 4 => BmpBitsPerPixel.Bit4,
85+
<= 8 => BmpBitsPerPixel.Bit8,
86+
<= 16 => BmpBitsPerPixel.Bit16,
87+
<= 24 => BmpBitsPerPixel.Bit24,
88+
_ => BmpBitsPerPixel.Bit32
89+
};
90+
91+
IconFrameCompression compression = IconFrameCompression.Bmp;
92+
if (bbpp is BmpBitsPerPixel.Bit32)
93+
{
94+
compression = IconFrameCompression.Png;
95+
}
96+
97+
return new CurMetadata
98+
{
99+
BmpBitsPerPixel = bbpp,
100+
Compression = compression,
101+
ColorTable = compression == IconFrameCompression.Bmp ? metadata.ColorTable : null
102+
};
103+
}
104+
105+
/// <inheritdoc/>
106+
public PixelTypeInfo GetPixelTypeInfo()
107+
{
108+
int bpp = (int)this.BmpBitsPerPixel;
109+
PixelComponentInfo info;
110+
PixelColorType color;
111+
PixelAlphaRepresentation alpha = PixelAlphaRepresentation.None;
112+
113+
if (this.Compression is IconFrameCompression.Png)
114+
{
115+
bpp = 32;
116+
info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8);
117+
color = PixelColorType.RGB | PixelColorType.Alpha;
118+
alpha = PixelAlphaRepresentation.Unassociated;
119+
}
120+
else
121+
{
122+
switch (this.BmpBitsPerPixel)
123+
{
124+
case BmpBitsPerPixel.Bit1:
125+
info = PixelComponentInfo.Create(1, bpp, 1);
126+
color = PixelColorType.Binary;
127+
break;
128+
case BmpBitsPerPixel.Bit2:
129+
info = PixelComponentInfo.Create(1, bpp, 2);
130+
color = PixelColorType.Indexed;
131+
break;
132+
case BmpBitsPerPixel.Bit4:
133+
info = PixelComponentInfo.Create(1, bpp, 4);
134+
color = PixelColorType.Indexed;
135+
break;
136+
case BmpBitsPerPixel.Bit8:
137+
info = PixelComponentInfo.Create(1, bpp, 8);
138+
color = PixelColorType.Indexed;
139+
break;
140+
141+
// Could be 555 with padding but 565 is more common in newer bitmaps and offers
142+
// greater accuracy due to extra green precision.
143+
case BmpBitsPerPixel.Bit16:
144+
info = PixelComponentInfo.Create(3, bpp, 5, 6, 5);
145+
color = PixelColorType.RGB;
146+
break;
147+
case BmpBitsPerPixel.Bit24:
148+
info = PixelComponentInfo.Create(3, bpp, 8, 8, 8);
149+
color = PixelColorType.RGB;
150+
break;
151+
case BmpBitsPerPixel.Bit32 or _:
152+
info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8);
153+
color = PixelColorType.RGB | PixelColorType.Alpha;
154+
alpha = PixelAlphaRepresentation.Unassociated;
155+
break;
156+
}
157+
}
158+
159+
return new PixelTypeInfo(bpp)
160+
{
161+
AlphaRepresentation = alpha,
162+
ComponentInfo = info,
163+
ColorType = color
164+
};
165+
}
166+
167+
/// <inheritdoc/>
168+
public FormatConnectingMetadata ToFormatConnectingMetadata()
169+
=> new()
170+
{
171+
EncodingType = this.Compression == IconFrameCompression.Bmp && this.BmpBitsPerPixel <= BmpBitsPerPixel.Bit8
172+
? EncodingType.Lossy
173+
: EncodingType.Lossless,
174+
PixelTypeInfo = this.GetPixelTypeInfo(),
175+
ColorTable = this.ColorTable
176+
};
13177

14178
/// <inheritdoc/>
15179
IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone();
180+
181+
/// <inheritdoc/>
182+
public CurMetadata DeepClone() => new(this);
16183
}

0 commit comments

Comments
 (0)