@@ -19,7 +19,9 @@ internal class PaletteTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
1919 private readonly ushort bitsPerSample1 ;
2020 private readonly TiffExtraSampleType ? extraSamplesType ;
2121
22- private readonly Vector4 [ ] paletteVectors ;
22+ private readonly Vector4 [ ] vectorPallete ;
23+ private readonly TPixel [ ] pixelPalette ;
24+
2325 private readonly float alphaScale ;
2426 private readonly bool hasAlpha ;
2527 private Color [ ] ? paletteColors ;
@@ -41,7 +43,7 @@ public PaletteTiffColor(TiffBitsPerSample bitsPerSample, ushort[] colorMap, Tiff
4143 int colorCount = 1 << this . bitsPerSample0 ;
4244
4345 // TIFF PaletteColor uses ColorMap (tag 320 / 0x0140) which is RGB-only (no alpha).
44- this . paletteVectors = GeneratePaletteVectors ( colorMap , colorCount ) ;
46+ this . vectorPallete = GenerateVectorPalette ( colorMap , colorCount ) ;
4547
4648 // ExtraSamples (tag 338 / 0x0152) describes extra per-pixel samples stored in the image data stream.
4749 // For PaletteColor, any alpha is per pixel (stored alongside the index), not per palette entry.
@@ -54,10 +56,16 @@ public PaletteTiffColor(TiffBitsPerSample bitsPerSample, ushort[] colorMap, Tiff
5456 {
5557 ulong alphaMax = ( 1UL << this . bitsPerSample1 ) - 1 ;
5658 this . alphaScale = alphaMax > 0 ? 1f / alphaMax : 1f ;
59+ this . pixelPalette = [ ] ;
60+ }
61+ else
62+ {
63+ // Pre-generate pixel palette for non-alpha case for performance.
64+ this . pixelPalette = GeneratePixelPalette ( colorMap , colorCount ) ;
5765 }
5866 }
5967
60- public Color [ ] PaletteColors => this . paletteColors ??= GenerateColorPalette ( this . paletteVectors ) ;
68+ public Color [ ] PaletteColors => this . paletteColors ??= GenerateColorPalette ( this . vectorPallete ) ;
6169
6270 /// <inheritdoc/>
6371 public override void Decode ( ReadOnlySpan < byte > data , Buffer2D < TPixel > pixels , int left , int top , int width , int height )
@@ -66,7 +74,7 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
6674
6775 if ( this . hasAlpha )
6876 {
69- Color [ ] colors = this . paletteColors ??= GenerateColorPalette ( this . paletteVectors ) ;
77+ Color [ ] colors = this . paletteColors ??= GenerateColorPalette ( this . vectorPallete ) ;
7078
7179 // NOTE: ExtraSamples may report "AssociatedAlphaData". For PaletteColor, the stored color sample is the
7280 // palette index, not per-pixel RGB components, so the premultiplication concept is not representable
@@ -81,18 +89,19 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
8189 float alpha = bitReader . ReadBits ( this . bitsPerSample1 ) * this . alphaScale ;
8290
8391 // Defensive guard against malformed streams.
84- if ( ( uint ) index >= ( uint ) this . paletteVectors . Length )
92+ if ( ( uint ) index >= ( uint ) this . vectorPallete . Length )
8593 {
8694 index = 0 ;
8795 }
8896
89- Vector4 color = this . paletteVectors [ index ] ;
97+ Vector4 color = this . vectorPallete [ index ] ;
9098 color . W = alpha ;
9199
92100 pixelRow [ x ] = TPixel . FromScaledVector4 ( color ) ;
93101
94102 // Best-effort palette update for downstream conversions.
95103 // This is intentionally "last writer wins" with no per-pixel branch.
104+ // Performance is not an issue here since the constructor performs no actual transformations.
96105 colors [ index ] = Color . FromScaledVector ( color ) ;
97106 }
98107
@@ -110,19 +119,19 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
110119 int index = bitReader . ReadBits ( this . bitsPerSample0 ) ;
111120
112121 // Defensive guard against malformed streams.
113- if ( ( uint ) index >= ( uint ) this . paletteVectors . Length )
122+ if ( ( uint ) index >= ( uint ) this . pixelPalette . Length )
114123 {
115124 index = 0 ;
116125 }
117126
118- pixelRow [ x ] = TPixel . FromScaledVector4 ( this . paletteVectors [ index ] ) ;
127+ pixelRow [ x ] = this . pixelPalette [ index ] ;
119128 }
120129
121130 bitReader . NextRow ( ) ;
122131 }
123132 }
124133
125- private static Vector4 [ ] GeneratePaletteVectors ( ushort [ ] colorMap , int colorCount )
134+ private static Vector4 [ ] GenerateVectorPalette ( ushort [ ] colorMap , int colorCount )
126135 {
127136 Vector4 [ ] palette = new Vector4 [ colorCount ] ;
128137
@@ -141,6 +150,25 @@ private static Vector4[] GeneratePaletteVectors(ushort[] colorMap, int colorCoun
141150 return palette ;
142151 }
143152
153+ private static TPixel [ ] GeneratePixelPalette ( ushort [ ] colorMap , int colorCount )
154+ {
155+ TPixel [ ] palette = new TPixel [ colorCount ] ;
156+
157+ const int rOffset = 0 ;
158+ int gOffset = colorCount ;
159+ int bOffset = colorCount * 2 ;
160+
161+ for ( int i = 0 ; i < palette . Length ; i ++ )
162+ {
163+ float r = colorMap [ rOffset + i ] * InvMax ;
164+ float g = colorMap [ gOffset + i ] * InvMax ;
165+ float b = colorMap [ bOffset + i ] * InvMax ;
166+ palette [ i ] = TPixel . FromScaledVector4 ( new Vector4 ( r , g , b , 1f ) ) ;
167+ }
168+
169+ return palette ;
170+ }
171+
144172 private static Color [ ] GenerateColorPalette ( Vector4 [ ] palette )
145173 {
146174 Color [ ] colors = new Color [ palette . Length ] ;
0 commit comments