@@ -87,6 +87,11 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
87
87
/// </summary>
88
88
private IMemoryOwner < byte > currentScanline ;
89
89
90
+ /// <summary>
91
+ /// The color profile name.
92
+ /// </summary>
93
+ private const string ColorProfileName = "ICC Profile" ;
94
+
90
95
/// <summary>
91
96
/// Initializes a new instance of the <see cref="PngEncoderCore" /> class.
92
97
/// </summary>
@@ -134,6 +139,7 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
134
139
135
140
this . WriteHeaderChunk ( stream ) ;
136
141
this . WriteGammaChunk ( stream ) ;
142
+ this . WriteColorProfileChunk ( stream , metadata ) ;
137
143
this . WritePaletteChunk ( stream , quantized ) ;
138
144
this . WriteTransparencyChunk ( stream , pngMetadata ) ;
139
145
this . WritePhysicalChunk ( stream , metadata ) ;
@@ -656,7 +662,7 @@ private void WriteExifChunk(Stream stream, ImageMetadata meta)
656
662
}
657
663
658
664
/// <summary>
659
- /// Writes an iTXT chunk, containing the XMP metdata to the stream, if such profile is present in the metadata.
665
+ /// Writes an iTXT chunk, containing the XMP metadata to the stream, if such profile is present in the metadata.
660
666
/// </summary>
661
667
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
662
668
/// <param name="meta">The image metadata.</param>
@@ -673,7 +679,7 @@ private void WriteXmpChunk(Stream stream, ImageMetadata meta)
673
679
return ;
674
680
}
675
681
676
- var xmpData = meta . XmpProfile . Data ;
682
+ byte [ ] xmpData = meta . XmpProfile . Data ;
677
683
678
684
if ( xmpData . Length == 0 )
679
685
{
@@ -687,19 +693,49 @@ private void WriteXmpChunk(Stream stream, ImageMetadata meta)
687
693
PngConstants . XmpKeyword . CopyTo ( payload ) ;
688
694
int bytesWritten = PngConstants . XmpKeyword . Length ;
689
695
690
- // Write the iTxt header (all zeros in this case)
691
- payload [ bytesWritten ++ ] = 0 ;
692
- payload [ bytesWritten ++ ] = 0 ;
693
- payload [ bytesWritten ++ ] = 0 ;
694
- payload [ bytesWritten ++ ] = 0 ;
695
- payload [ bytesWritten ++ ] = 0 ;
696
+ // Write the iTxt header (all zeros in this case).
697
+ Span < byte > iTxtHeader = payload . Slice ( bytesWritten ) ;
698
+ iTxtHeader [ 4 ] = 0 ;
699
+ iTxtHeader [ 3 ] = 0 ;
700
+ iTxtHeader [ 2 ] = 0 ;
701
+ iTxtHeader [ 1 ] = 0 ;
702
+ iTxtHeader [ 0 ] = 0 ;
703
+ bytesWritten += 5 ;
696
704
697
- // And the XMP data itself
705
+ // And the XMP data itself.
698
706
xmpData . CopyTo ( payload . Slice ( bytesWritten ) ) ;
699
707
this . WriteChunk ( stream , PngChunkType . InternationalText , payload ) ;
700
708
}
701
709
}
702
710
711
+ /// <summary>
712
+ /// Writes the color profile chunk.
713
+ /// </summary>
714
+ /// <param name="stream">The stream to write to.</param>
715
+ /// <param name="metaData">The image meta data.</param>
716
+ private void WriteColorProfileChunk ( Stream stream , ImageMetadata metaData )
717
+ {
718
+ if ( metaData . IccProfile is null )
719
+ {
720
+ return ;
721
+ }
722
+
723
+ byte [ ] iccProfileBytes = metaData . IccProfile . ToByteArray ( ) ;
724
+
725
+ byte [ ] compressedData = this . GetZlibCompressedBytes ( iccProfileBytes ) ;
726
+ int payloadLength = ColorProfileName . Length + compressedData . Length + 2 ;
727
+ using ( IMemoryOwner < byte > owner = this . memoryAllocator . Allocate < byte > ( payloadLength ) )
728
+ {
729
+ Span < byte > outputBytes = owner . GetSpan ( ) ;
730
+ PngConstants . Encoding . GetBytes ( ColorProfileName ) . CopyTo ( outputBytes ) ;
731
+ int bytesWritten = ColorProfileName . Length ;
732
+ outputBytes [ bytesWritten ++ ] = 0 ; // Null separator.
733
+ outputBytes [ bytesWritten ++ ] = 0 ; // Compression.
734
+ compressedData . CopyTo ( outputBytes . Slice ( bytesWritten ) ) ;
735
+ this . WriteChunk ( stream , PngChunkType . EmbeddedColorProfile , outputBytes ) ;
736
+ }
737
+ }
738
+
703
739
/// <summary>
704
740
/// Writes a text chunk to the stream. Can be either a tTXt, iTXt or zTXt chunk,
705
741
/// depending whether the text contains any latin characters or should be compressed.
@@ -727,13 +763,12 @@ private void WriteTextChunks(Stream stream, PngMetadata meta)
727
763
}
728
764
}
729
765
730
- if ( hasUnicodeCharacters || ( ! string . IsNullOrWhiteSpace ( textData . LanguageTag ) ||
731
- ! string . IsNullOrWhiteSpace ( textData . TranslatedKeyword ) ) )
766
+ if ( hasUnicodeCharacters || ( ! string . IsNullOrWhiteSpace ( textData . LanguageTag ) || ! string . IsNullOrWhiteSpace ( textData . TranslatedKeyword ) ) )
732
767
{
733
768
// Write iTXt chunk.
734
769
byte [ ] keywordBytes = PngConstants . Encoding . GetBytes ( textData . Keyword ) ;
735
770
byte [ ] textBytes = textData . Value . Length > this . options . TextCompressionThreshold
736
- ? this . GetCompressedTextBytes ( PngConstants . TranslatedEncoding . GetBytes ( textData . Value ) )
771
+ ? this . GetZlibCompressedBytes ( PngConstants . TranslatedEncoding . GetBytes ( textData . Value ) )
737
772
: PngConstants . TranslatedEncoding . GetBytes ( textData . Value ) ;
738
773
739
774
byte [ ] translatedKeyword = PngConstants . TranslatedEncoding . GetBytes ( textData . TranslatedKeyword ) ;
@@ -772,18 +807,17 @@ private void WriteTextChunks(Stream stream, PngMetadata meta)
772
807
if ( textData . Value . Length > this . options . TextCompressionThreshold )
773
808
{
774
809
// Write zTXt chunk.
775
- byte [ ] compressedData =
776
- this . GetCompressedTextBytes ( PngConstants . Encoding . GetBytes ( textData . Value ) ) ;
810
+ byte [ ] compressedData = this . GetZlibCompressedBytes ( PngConstants . Encoding . GetBytes ( textData . Value ) ) ;
777
811
int payloadLength = textData . Keyword . Length + compressedData . Length + 2 ;
778
812
using ( IMemoryOwner < byte > owner = this . memoryAllocator . Allocate < byte > ( payloadLength ) )
779
813
{
780
814
Span < byte > outputBytes = owner . GetSpan ( ) ;
781
815
PngConstants . Encoding . GetBytes ( textData . Keyword ) . CopyTo ( outputBytes ) ;
782
816
int bytesWritten = textData . Keyword . Length ;
783
- outputBytes [ bytesWritten ++ ] = 0 ;
784
- outputBytes [ bytesWritten ++ ] = 0 ;
817
+ outputBytes [ bytesWritten ++ ] = 0 ; // Null separator.
818
+ outputBytes [ bytesWritten ++ ] = 0 ; // Compression.
785
819
compressedData . CopyTo ( outputBytes . Slice ( bytesWritten ) ) ;
786
- this . WriteChunk ( stream , PngChunkType . CompressedText , outputBytes . ToArray ( ) ) ;
820
+ this . WriteChunk ( stream , PngChunkType . CompressedText , outputBytes ) ;
787
821
}
788
822
}
789
823
else
@@ -796,9 +830,8 @@ private void WriteTextChunks(Stream stream, PngMetadata meta)
796
830
PngConstants . Encoding . GetBytes ( textData . Keyword ) . CopyTo ( outputBytes ) ;
797
831
int bytesWritten = textData . Keyword . Length ;
798
832
outputBytes [ bytesWritten ++ ] = 0 ;
799
- PngConstants . Encoding . GetBytes ( textData . Value )
800
- . CopyTo ( outputBytes . Slice ( bytesWritten ) ) ;
801
- this . WriteChunk ( stream , PngChunkType . Text , outputBytes . ToArray ( ) ) ;
833
+ PngConstants . Encoding . GetBytes ( textData . Value ) . CopyTo ( outputBytes . Slice ( bytesWritten ) ) ;
834
+ this . WriteChunk ( stream , PngChunkType . Text , outputBytes ) ;
802
835
}
803
836
}
804
837
}
@@ -808,15 +841,15 @@ private void WriteTextChunks(Stream stream, PngMetadata meta)
808
841
/// <summary>
809
842
/// Compresses a given text using Zlib compression.
810
843
/// </summary>
811
- /// <param name="textBytes ">The text bytes to compress.</param>
812
- /// <returns>The compressed text byte array.</returns>
813
- private byte [ ] GetCompressedTextBytes ( byte [ ] textBytes )
844
+ /// <param name="dataBytes ">The bytes to compress.</param>
845
+ /// <returns>The compressed byte array.</returns>
846
+ private byte [ ] GetZlibCompressedBytes ( byte [ ] dataBytes )
814
847
{
815
848
using ( var memoryStream = new MemoryStream ( ) )
816
849
{
817
850
using ( var deflateStream = new ZlibDeflateStream ( this . memoryAllocator , memoryStream , this . options . CompressionLevel ) )
818
851
{
819
- deflateStream . Write ( textBytes ) ;
852
+ deflateStream . Write ( dataBytes ) ;
820
853
}
821
854
822
855
return memoryStream . ToArray ( ) ;
0 commit comments