Skip to content

Commit be0be69

Browse files
Fix PngMetadata
1 parent 1f4605e commit be0be69

4 files changed

Lines changed: 28 additions & 135 deletions

File tree

src/ImageSharp/Formats/Png/PngEncoderCore.cs

Lines changed: 7 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,41 +1484,19 @@ private void SanitizeAndSetEncoderOptions<TPixel>(
14841484

14851485
// Use options, then check metadata, if nothing set there then we suggest
14861486
// a sensible default based upon the pixel format.
1487-
PngColorType? colorType = encoder.ColorType ?? pngMetadata.ColorType;
1488-
byte? bits = (byte?)(encoder.BitDepth ?? pngMetadata.BitDepth);
1487+
PngColorType color = encoder.ColorType ?? pngMetadata.ColorType;
1488+
byte bits = (byte)(encoder.BitDepth ?? pngMetadata.BitDepth);
14891489

1490-
if (colorType is null || bits is null)
1491-
{
1492-
PixelTypeInfo info = TPixel.GetPixelTypeInfo();
1493-
PixelComponentInfo? componentInfo = info.ComponentInfo;
1494-
1495-
colorType ??= SuggestColorType<TPixel>(in info);
1496-
1497-
if (bits is null)
1498-
{
1499-
// TODO: Update once we stop abusing PixelTypeInfo in decoders.
1500-
if (componentInfo.HasValue)
1501-
{
1502-
PixelComponentInfo c = componentInfo.Value;
1503-
bits = (byte)SuggestBitDepth<TPixel>(in c);
1504-
}
1505-
else
1506-
{
1507-
bits = (byte)PngBitDepth.Bit8;
1508-
}
1509-
}
1510-
}
1511-
1512-
// Ensure bit depth and color type are a supported combination.
1490+
// Ensure the bit depth and color type are a supported combination.
15131491
// Bit8 is the only bit depth supported by all color types.
1514-
byte[] validBitDepths = PngConstants.ColorTypes[colorType.Value];
1492+
byte[] validBitDepths = PngConstants.ColorTypes[color];
15151493
if (Array.IndexOf(validBitDepths, bits) == -1)
15161494
{
15171495
bits = (byte)PngBitDepth.Bit8;
15181496
}
15191497

1520-
this.colorType = colorType.Value;
1521-
this.bitDepth = bits.Value;
1498+
this.colorType = color;
1499+
this.bitDepth = bits;
15221500

15231501
if (!encoder.FilterMethod.HasValue)
15241502
{
@@ -1529,7 +1507,7 @@ private void SanitizeAndSetEncoderOptions<TPixel>(
15291507
use16Bit = bits == (byte)PngBitDepth.Bit16;
15301508
bytesPerPixel = CalculateBytesPerPixel(this.colorType, use16Bit);
15311509

1532-
this.interlaceMode = (encoder.InterlaceMethod ?? pngMetadata.InterlaceMethod)!.Value;
1510+
this.interlaceMode = encoder.InterlaceMethod ?? pngMetadata.InterlaceMethod;
15331511
this.chunkFilter = encoder.SkipMetadata ? PngChunkFilter.ExcludeAll : encoder.ChunkFilter ?? PngChunkFilter.None;
15341512
}
15351513

@@ -1669,47 +1647,6 @@ private static int CalculateBytesPerPixel(PngColorType? pngColorType, bool use16
16691647
_ => use16Bit ? 8 : 4,
16701648
};
16711649

1672-
/// <summary>
1673-
/// Returns a suggested <see cref="PngColorType"/> for the given <typeparamref name="TPixel"/>
1674-
/// </summary>
1675-
/// <param name="info">The pixel type info.</param>
1676-
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
1677-
private static PngColorType SuggestColorType<TPixel>(in PixelTypeInfo info)
1678-
where TPixel : unmanaged, IPixel<TPixel>
1679-
{
1680-
if (info.AlphaRepresentation == PixelAlphaRepresentation.None)
1681-
{
1682-
return info.ColorType switch
1683-
{
1684-
PixelColorType.Luminance => PngColorType.Grayscale,
1685-
_ => PngColorType.Rgb,
1686-
};
1687-
}
1688-
1689-
return info.ColorType switch
1690-
{
1691-
PixelColorType.Luminance | PixelColorType.Alpha or PixelColorType.Alpha => PngColorType.GrayscaleWithAlpha,
1692-
_ => PngColorType.RgbWithAlpha,
1693-
};
1694-
}
1695-
1696-
/// <summary>
1697-
/// Returns a suggested <see cref="PngBitDepth"/> for the given <typeparamref name="TPixel"/>
1698-
/// </summary>
1699-
/// <param name="info">The pixel type info.</param>
1700-
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
1701-
private static PngBitDepth SuggestBitDepth<TPixel>(in PixelComponentInfo info)
1702-
where TPixel : unmanaged, IPixel<TPixel>
1703-
{
1704-
int bits = info.GetMaximumComponentPrecision();
1705-
if (bits > (int)PixelComponentBitDepth.Bit8)
1706-
{
1707-
return PngBitDepth.Bit16;
1708-
}
1709-
1710-
return PngBitDepth.Bit8;
1711-
}
1712-
17131650
private unsafe struct ScratchBuffer
17141651
{
17151652
private const int Size = 26;

src/ImageSharp/Formats/Png/PngMetadata.cs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,17 @@ private PngMetadata(PngMetadata other)
4747
/// Gets or sets the number of bits per sample or per palette index (not per pixel).
4848
/// Not all values are allowed for all <see cref="ColorType"/> values.
4949
/// </summary>
50-
public PngBitDepth? BitDepth { get; set; }
50+
public PngBitDepth BitDepth { get; set; } = PngBitDepth.Bit8;
5151

5252
/// <summary>
5353
/// Gets or sets the color type.
5454
/// </summary>
55-
public PngColorType? ColorType { get; set; }
55+
public PngColorType ColorType { get; set; } = PngColorType.RgbWithAlpha;
5656

5757
/// <summary>
5858
/// Gets or sets a value indicating whether this instance should write an Adam7 interlaced image.
5959
/// </summary>
60-
public PngInterlaceMode? InterlaceMethod { get; set; } = PngInterlaceMode.None;
60+
public PngInterlaceMode InterlaceMethod { get; set; } = PngInterlaceMode.None;
6161

6262
/// <summary>
6363
/// Gets or sets the gamma value for the image.
@@ -100,21 +100,23 @@ internal static PngMetadata FromAnimatedMetadata(AnimatedImageMetadata metadata)
100100
for (int i = 0; i < colorTable.Length; i++)
101101
{
102102
ref Color c = ref colorTable[i];
103-
if (c == metadata.BackgroundColor)
103+
if (c != metadata.BackgroundColor)
104104
{
105-
// Png treats background as fully empty
106-
c = Color.Transparent;
107-
break;
105+
continue;
108106
}
107+
108+
// Png treats background as fully empty
109+
c = Color.Transparent;
110+
break;
109111
}
110112
}
111113

112114
return new()
113115
{
114-
ColorType = colorTable != null ? PngColorType.Palette : null,
116+
ColorType = colorTable != null ? PngColorType.Palette : PngColorType.RgbWithAlpha,
115117
BitDepth = colorTable != null
116118
? (PngBitDepth)Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(colorTable.Length), 1, 8)
117-
: null,
119+
: PngBitDepth.Bit8,
118120
ColorTable = colorTable,
119121
RepeatCount = metadata.RepeatCount,
120122
};
@@ -131,12 +133,14 @@ public static PngMetadata FromFormatConnectingMetadata(FormatConnectingMetadata
131133
for (int i = 0; i < colorTable.Length; i++)
132134
{
133135
ref Color c = ref colorTable[i];
134-
if (c == metadata.BackgroundColor)
136+
if (c != metadata.BackgroundColor)
135137
{
136-
// Png treats background as fully empty
137-
c = Color.Transparent;
138-
break;
138+
continue;
139139
}
140+
141+
// Png treats background as fully empty
142+
c = Color.Transparent;
143+
break;
140144
}
141145
}
142146

@@ -240,6 +244,7 @@ public FormatConnectingMetadata ToFormatConnectingMetadata()
240244
info = PixelComponentInfo.Create(3, bpp, 16, 16, 16);
241245
break;
242246

247+
case PngColorType.RgbWithAlpha:
243248
default:
244249

245250
alpha = PixelAlphaRepresentation.Unassociated;

tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -254,49 +254,6 @@ public void WorksWithAllBitDepthsAndExcludeAllFilter<TPixel>(TestImageProvider<T
254254
}
255255
}
256256

257-
[Theory]
258-
[WithBlankImages(1, 1, PixelTypes.A8, PngColorType.GrayscaleWithAlpha, PngBitDepth.Bit8)]
259-
[WithBlankImages(1, 1, PixelTypes.Argb32, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)]
260-
[WithBlankImages(1, 1, PixelTypes.Bgr565, PngColorType.Rgb, PngBitDepth.Bit8)]
261-
[WithBlankImages(1, 1, PixelTypes.Bgra4444, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)]
262-
[WithBlankImages(1, 1, PixelTypes.Byte4, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)]
263-
[WithBlankImages(1, 1, PixelTypes.HalfSingle, PngColorType.Rgb, PngBitDepth.Bit16)]
264-
[WithBlankImages(1, 1, PixelTypes.HalfVector2, PngColorType.Rgb, PngBitDepth.Bit16)]
265-
[WithBlankImages(1, 1, PixelTypes.HalfVector4, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)]
266-
[WithBlankImages(1, 1, PixelTypes.NormalizedByte2, PngColorType.Rgb, PngBitDepth.Bit8)]
267-
[WithBlankImages(1, 1, PixelTypes.NormalizedByte4, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)]
268-
[WithBlankImages(1, 1, PixelTypes.NormalizedShort4, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)]
269-
[WithBlankImages(1, 1, PixelTypes.Rg32, PngColorType.Rgb, PngBitDepth.Bit16)]
270-
[WithBlankImages(1, 1, PixelTypes.Rgba1010102, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)]
271-
[WithBlankImages(1, 1, PixelTypes.Rgba32, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)]
272-
[WithBlankImages(1, 1, PixelTypes.RgbaVector, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)]
273-
[WithBlankImages(1, 1, PixelTypes.Short2, PngColorType.Rgb, PngBitDepth.Bit16)]
274-
[WithBlankImages(1, 1, PixelTypes.Short4, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)]
275-
[WithBlankImages(1, 1, PixelTypes.Rgb24, PngColorType.Rgb, PngBitDepth.Bit8)]
276-
[WithBlankImages(1, 1, PixelTypes.Bgr24, PngColorType.Rgb, PngBitDepth.Bit8)]
277-
[WithBlankImages(1, 1, PixelTypes.Bgra32, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)]
278-
[WithBlankImages(1, 1, PixelTypes.Rgb48, PngColorType.Rgb, PngBitDepth.Bit16)]
279-
[WithBlankImages(1, 1, PixelTypes.Rgba64, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)]
280-
[WithBlankImages(1, 1, PixelTypes.Bgra5551, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)]
281-
[WithBlankImages(1, 1, PixelTypes.L8, PngColorType.Grayscale, PngBitDepth.Bit8)]
282-
[WithBlankImages(1, 1, PixelTypes.L16, PngColorType.Grayscale, PngBitDepth.Bit16)]
283-
[WithBlankImages(1, 1, PixelTypes.La16, PngColorType.GrayscaleWithAlpha, PngBitDepth.Bit8)]
284-
[WithBlankImages(1, 1, PixelTypes.La32, PngColorType.GrayscaleWithAlpha, PngBitDepth.Bit16)]
285-
public void InfersColorTypeAndBitDepth<TPixel>(TestImageProvider<TPixel> provider, PngColorType pngColorType, PngBitDepth pngBitDepth)
286-
where TPixel : unmanaged, IPixel<TPixel>
287-
{
288-
using Stream stream = new MemoryStream();
289-
PngEncoder.Encode(provider.GetImage(), stream);
290-
291-
stream.Seek(0, SeekOrigin.Begin);
292-
293-
using Image image = PngDecoder.Instance.Decode(DecoderOptions.Default, stream);
294-
295-
PngMetadata metadata = image.Metadata.GetPngMetadata();
296-
Assert.Equal(pngColorType, metadata.ColorType);
297-
Assert.Equal(pngBitDepth, metadata.BitDepth);
298-
}
299-
300257
[Theory]
301258
[WithFile(TestImages.Png.Palette8Bpp, nameof(PaletteLargeOnly), PixelTypes.Rgba32)]
302259
public void PaletteColorType_WuQuantizer<TPixel>(TestImageProvider<TPixel> provider, int paletteSize)

tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,13 @@ public class PngSmokeTests
1717
public void GeneralTest<TPixel>(TestImageProvider<TPixel> provider)
1818
where TPixel : unmanaged, IPixel<TPixel>
1919
{
20-
// does saving a file then reopening mean both files are identical???
2120
using Image<TPixel> image = provider.GetImage();
22-
using var ms = new MemoryStream();
21+
using MemoryStream ms = new();
2322

24-
// image.Save(provider.Utility.GetTestOutputFileName("bmp"));
2523
image.Save(ms, new PngEncoder());
2624
ms.Position = 0;
2725
using Image<Rgba32> img2 = PngDecoder.Instance.Decode<Rgba32>(DecoderOptions.Default, ms);
28-
ImageComparer.Tolerant().VerifySimilarity(image, img2);
29-
30-
// img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder());
26+
ImageComparer.Exact.VerifySimilarity(image, img2);
3127
}
3228

3329
[Theory]
@@ -37,12 +33,10 @@ public void Resize<TPixel>(TestImageProvider<TPixel> provider)
3733
{
3834
// does saving a file then reopening mean both files are identical???
3935
using Image<TPixel> image = provider.GetImage();
40-
using var ms = new MemoryStream();
36+
using MemoryStream ms = new();
4137

42-
// image.Save(provider.Utility.GetTestOutputFileName("png"));
4338
image.Mutate(x => x.Resize(100, 100));
4439

45-
// image.Save(provider.Utility.GetTestOutputFileName("png", "resize"));
4640
image.Save(ms, new PngEncoder());
4741
ms.Position = 0;
4842
using Image<Rgba32> img2 = PngDecoder.Instance.Decode<Rgba32>(DecoderOptions.Default, ms);

0 commit comments

Comments
 (0)