@@ -172,21 +172,20 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken
172172 if ( image is null )
173173 {
174174 this . InitializeImage ( metadata , out image ) ;
175+
176+ // Both PLTE and tRNS chunks, if present, have been read at this point as per spec.
177+ AssignColorPalette ( this . palette , this . paletteAlpha , pngMetadata ) ;
175178 }
176179
177180 this . ReadScanlines ( chunk , image . Frames . RootFrame , pngMetadata , cancellationToken ) ;
178181
179182 break ;
180183 case PngChunkType . Palette :
181- byte [ ] pal = new byte [ chunk . Length ] ;
182- chunk . Data . GetSpan ( ) . CopyTo ( pal ) ;
183- this . palette = pal ;
184+ this . palette = chunk . Data . GetSpan ( ) . ToArray ( ) ;
184185 break ;
185186 case PngChunkType . Transparency :
186- byte [ ] alpha = new byte [ chunk . Length ] ;
187- chunk . Data . GetSpan ( ) . CopyTo ( alpha ) ;
188- this . paletteAlpha = alpha ;
189- this . AssignTransparentMarkers ( alpha , pngMetadata ) ;
187+ this . paletteAlpha = chunk . Data . GetSpan ( ) . ToArray ( ) ;
188+ this . AssignTransparentMarkers ( this . paletteAlpha , pngMetadata ) ;
190189 break ;
191190 case PngChunkType . Text :
192191 this . ReadTextChunk ( metadata , pngMetadata , chunk . Data . GetSpan ( ) ) ;
@@ -292,12 +291,15 @@ public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellat
292291
293292 this . SkipChunkDataAndCrc ( chunk ) ;
294293 break ;
294+ case PngChunkType . Palette :
295+ this . palette = chunk . Data . GetSpan ( ) . ToArray ( ) ;
296+ break ;
297+
295298 case PngChunkType . Transparency :
296- byte [ ] alpha = new byte [ chunk . Length ] ;
297- chunk . Data . GetSpan ( ) . CopyTo ( alpha ) ;
298- this . paletteAlpha = alpha ;
299- this . AssignTransparentMarkers ( alpha , pngMetadata ) ;
299+ this . paletteAlpha = chunk . Data . GetSpan ( ) . ToArray ( ) ;
300+ this . AssignTransparentMarkers ( this . paletteAlpha , pngMetadata ) ;
300301
302+ // Spec says tRNS must be after PLTE so safe to exit.
301303 if ( this . colorMetadataOnly )
302304 {
303305 goto EOF ;
@@ -370,6 +372,9 @@ public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellat
370372 PngThrowHelper . ThrowNoHeader ( ) ;
371373 }
372374
375+ // Both PLTE and tRNS chunks, if present, have been read at this point as per spec.
376+ AssignColorPalette ( this . palette , this . paletteAlpha , pngMetadata ) ;
377+
373378 return new ImageInfo ( new PixelTypeInfo ( this . CalculateBitsPerPixel ( ) ) , new ( this . header . Width , this . header . Height ) , metadata ) ;
374379 }
375380 finally
@@ -766,9 +771,7 @@ private void ProcessDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScan
766771 this . header ,
767772 scanlineSpan ,
768773 rowSpan ,
769- pngMetadata . HasTransparency ,
770- pngMetadata . TransparentL16 . GetValueOrDefault ( ) ,
771- pngMetadata . TransparentL8 . GetValueOrDefault ( ) ) ;
774+ pngMetadata . TransparentColor ) ;
772775
773776 break ;
774777
@@ -787,8 +790,7 @@ private void ProcessDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScan
787790 this . header ,
788791 scanlineSpan ,
789792 rowSpan ,
790- this . palette ,
791- this . paletteAlpha ) ;
793+ pngMetadata . ColorTable ) ;
792794
793795 break ;
794796
@@ -800,9 +802,7 @@ private void ProcessDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScan
800802 rowSpan ,
801803 this . bytesPerPixel ,
802804 this . bytesPerSample ,
803- pngMetadata . HasTransparency ,
804- pngMetadata . TransparentRgb48 . GetValueOrDefault ( ) ,
805- pngMetadata . TransparentRgb24 . GetValueOrDefault ( ) ) ;
805+ pngMetadata . TransparentColor ) ;
806806
807807 break ;
808808
@@ -860,9 +860,7 @@ private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defi
860860 rowSpan ,
861861 ( uint ) pixelOffset ,
862862 ( uint ) increment ,
863- pngMetadata . HasTransparency ,
864- pngMetadata . TransparentL16 . GetValueOrDefault ( ) ,
865- pngMetadata . TransparentL8 . GetValueOrDefault ( ) ) ;
863+ pngMetadata . TransparentColor ) ;
866864
867865 break ;
868866
@@ -885,8 +883,7 @@ private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defi
885883 rowSpan ,
886884 ( uint ) pixelOffset ,
887885 ( uint ) increment ,
888- this . palette ,
889- this . paletteAlpha ) ;
886+ pngMetadata . ColorTable ) ;
890887
891888 break ;
892889
@@ -899,9 +896,7 @@ private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defi
899896 ( uint ) increment ,
900897 this . bytesPerPixel ,
901898 this . bytesPerSample ,
902- pngMetadata . HasTransparency ,
903- pngMetadata . TransparentRgb48 . GetValueOrDefault ( ) ,
904- pngMetadata . TransparentRgb24 . GetValueOrDefault ( ) ) ;
899+ pngMetadata . TransparentColor ) ;
905900
906901 break ;
907902
@@ -924,10 +919,44 @@ private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defi
924919 }
925920 }
926921
922+ /// <summary>
923+ /// Decodes and assigns the color palette to the metadata
924+ /// </summary>
925+ /// <param name="palette">The palette buffer.</param>
926+ /// <param name="alpha">The alpha palette buffer.</param>
927+ /// <param name="pngMetadata">The png metadata.</param>
928+ private static void AssignColorPalette ( ReadOnlySpan < byte > palette , ReadOnlySpan < byte > alpha , PngMetadata pngMetadata )
929+ {
930+ if ( palette . Length == 0 )
931+ {
932+ return ;
933+ }
934+
935+ Color [ ] colorTable = new Color [ palette . Length / Unsafe . SizeOf < Rgb24 > ( ) ] ;
936+ ReadOnlySpan < Rgb24 > rgbTable = MemoryMarshal . Cast < byte , Rgb24 > ( palette ) ;
937+ for ( int i = 0 ; i < colorTable . Length ; i ++ )
938+ {
939+ colorTable [ i ] = new Color ( rgbTable [ i ] ) ;
940+ }
941+
942+ if ( alpha . Length > 0 )
943+ {
944+ // The alpha chunk may contain as many transparency entries as there are palette entries
945+ // (more than that would not make any sense) or as few as one.
946+ for ( int i = 0 ; i < alpha . Length ; i ++ )
947+ {
948+ ref Color color = ref colorTable [ i ] ;
949+ color = color . WithAlpha ( alpha [ i ] / 255F ) ;
950+ }
951+ }
952+
953+ pngMetadata . ColorTable = colorTable ;
954+ }
955+
927956 /// <summary>
928957 /// Decodes and assigns marker colors that identify transparent pixels in non indexed images.
929958 /// </summary>
930- /// <param name="alpha">The alpha tRNS array .</param>
959+ /// <param name="alpha">The alpha tRNS buffer .</param>
931960 /// <param name="pngMetadata">The png metadata.</param>
932961 private void AssignTransparentMarkers ( ReadOnlySpan < byte > alpha , PngMetadata pngMetadata )
933962 {
@@ -941,16 +970,14 @@ private void AssignTransparentMarkers(ReadOnlySpan<byte> alpha, PngMetadata pngM
941970 ushort gc = BinaryPrimitives . ReadUInt16LittleEndian ( alpha . Slice ( 2 , 2 ) ) ;
942971 ushort bc = BinaryPrimitives . ReadUInt16LittleEndian ( alpha . Slice ( 4 , 2 ) ) ;
943972
944- pngMetadata . TransparentRgb48 = new Rgb48 ( rc , gc , bc ) ;
945- pngMetadata . HasTransparency = true ;
973+ pngMetadata . TransparentColor = new ( new Rgb48 ( rc , gc , bc ) ) ;
946974 return ;
947975 }
948976
949977 byte r = ReadByteLittleEndian ( alpha , 0 ) ;
950978 byte g = ReadByteLittleEndian ( alpha , 2 ) ;
951979 byte b = ReadByteLittleEndian ( alpha , 4 ) ;
952- pngMetadata . TransparentRgb24 = new Rgb24 ( r , g , b ) ;
953- pngMetadata . HasTransparency = true ;
980+ pngMetadata . TransparentColor = new ( new Rgb24 ( r , g , b ) ) ;
954981 }
955982 }
956983 else if ( this . pngColorType == PngColorType . Grayscale )
@@ -959,20 +986,14 @@ private void AssignTransparentMarkers(ReadOnlySpan<byte> alpha, PngMetadata pngM
959986 {
960987 if ( this . header . BitDepth == 16 )
961988 {
962- pngMetadata . TransparentL16 = new L16 ( BinaryPrimitives . ReadUInt16LittleEndian ( alpha [ ..2 ] ) ) ;
989+ pngMetadata . TransparentColor = Color . FromPixel ( new L16 ( BinaryPrimitives . ReadUInt16LittleEndian ( alpha [ ..2 ] ) ) ) ;
963990 }
964991 else
965992 {
966- pngMetadata . TransparentL8 = new L8 ( ReadByteLittleEndian ( alpha , 0 ) ) ;
993+ pngMetadata . TransparentColor = Color . FromPixel ( new L8 ( ReadByteLittleEndian ( alpha , 0 ) ) ) ;
967994 }
968-
969- pngMetadata . HasTransparency = true ;
970995 }
971996 }
972- else if ( this . pngColorType == PngColorType . Palette && alpha . Length > 0 )
973- {
974- pngMetadata . HasTransparency = true ;
975- }
976997 }
977998
978999 /// <summary>
@@ -1461,7 +1482,7 @@ private bool TryReadChunk(Span<byte> buffer, out PngChunk chunk)
14611482
14621483 // If we're reading color metadata only we're only interested in the IHDR and tRNS chunks.
14631484 // We can skip all other chunk data in the stream for better performance.
1464- if ( this . colorMetadataOnly && type != PngChunkType . Header && type != PngChunkType . Transparency )
1485+ if ( this . colorMetadataOnly && type != PngChunkType . Header && type != PngChunkType . Transparency && type != PngChunkType . Palette )
14651486 {
14661487 chunk = new PngChunk ( length , type ) ;
14671488
0 commit comments