@@ -135,7 +135,12 @@ impl<W: Write> PnmEncoder<W> {
135135 }
136136 }
137137
138- /// Encode an image whose samples are represented as `u8`.
138+ /// Encode an image whose samples are represented as a sequence of `u8` or `u16` data.
139+ ///
140+ /// If `image` is a slice of `u8`, the samples will be interpreted based on the chosen `color` option.
141+ /// Color types of 16-bit precision means that the bytes are reinterpreted as 16-bit samples,
142+ /// otherwise they are treated as 8-bit samples.
143+ /// If `image` is a slice of `u16`, the samples will be interpreted as 16-bit samples directly.
139144 ///
140145 /// Some `pnm` subtypes are incompatible with some color options, a chosen header most
141146 /// certainly with any deviation from the original decoded image.
@@ -150,13 +155,58 @@ impl<W: Write> PnmEncoder<W> {
150155 S : Into < FlatSamples < ' s > > ,
151156 {
152157 let image = image. into ( ) ;
158+
159+ // adapt samples so that they are aligned even in 16-bit samples,
160+ // required due to the narrowing of the image buffer to &[u8]
161+ // on dynamic image writing
162+ let image = match ( image, color) {
163+ (
164+ FlatSamples :: U8 ( samples) ,
165+ ExtendedColorType :: L16
166+ | ExtendedColorType :: La16
167+ | ExtendedColorType :: Rgb16
168+ | ExtendedColorType :: Rgba16 ,
169+ ) => {
170+ match bytemuck:: try_cast_slice ( samples) {
171+ // proceed with aligned 16-bit samples
172+ Ok ( samples) => FlatSamples :: U16 ( samples) ,
173+ Err ( _e) => {
174+ // reallocation is required
175+ let new_samples: Vec < u16 > = samples
176+ . chunks ( 2 )
177+ . map ( |chunk| u16:: from_ne_bytes ( [ chunk[ 0 ] , chunk[ 1 ] ] ) )
178+ . collect ( ) ;
179+
180+ let image = FlatSamples :: U16 ( & new_samples) ;
181+
182+ // make a separate encoding path,
183+ // because the image buffer lifetime has changed
184+ return self . encode_impl ( image, width, height, color) ;
185+ }
186+ }
187+ }
188+ // should not be necessary for any other case
189+ _ => image,
190+ } ;
191+
192+ self . encode_impl ( image, width, height, color)
193+ }
194+
195+ /// Encode an image whose samples are already interpreted correctly.
196+ fn encode_impl < ' s > (
197+ & mut self ,
198+ samples : FlatSamples < ' s > ,
199+ width : u32 ,
200+ height : u32 ,
201+ color : ExtendedColorType ,
202+ ) -> ImageResult < ( ) > {
153203 match self . header {
154- HeaderStrategy :: Dynamic => self . write_dynamic_header ( image , width, height, color) ,
204+ HeaderStrategy :: Dynamic => self . write_dynamic_header ( samples , width, height, color) ,
155205 HeaderStrategy :: Subtype ( subtype) => {
156- self . write_subtyped_header ( subtype, image , width, height, color)
206+ self . write_subtyped_header ( subtype, samples , width, height, color)
157207 }
158208 HeaderStrategy :: Chosen ( ref header) => {
159- Self :: write_with_header ( & mut self . writer , header, image , width, height, color)
209+ Self :: write_with_header ( & mut self . writer , header, samples , width, height, color)
160210 }
161211 }
162212 }
@@ -481,28 +531,6 @@ impl<'a> CheckedHeaderColor<'a> {
481531 }
482532 } ;
483533
484- // reinterpret image samples if color type is 16 bits per channel,
485- // required due to the narrowing of the image buffer to &[u8]
486- // on dynamic image writing
487- let image = match ( image, self . color ) {
488- (
489- FlatSamples :: U8 ( samples) ,
490- ExtendedColorType :: L16
491- | ExtendedColorType :: La16
492- | ExtendedColorType :: Rgb16
493- | ExtendedColorType :: Rgba16 ,
494- ) => FlatSamples :: U16 ( bytemuck:: try_cast_slice ( samples) . map_err ( |_e| {
495- ImageError :: Unsupported ( UnsupportedError :: from_format_and_kind (
496- ImageFormat :: Pnm . into ( ) ,
497- UnsupportedErrorKind :: GenericFeature (
498- "16-bit sampling from the given image buffer" . to_owned ( ) ,
499- ) ,
500- ) )
501- } ) ?) ,
502- // should not be necessary for any other case
503- _ => image,
504- } ;
505-
506534 // Avoid the performance heavy check if possible, e.g. if the header has been chosen by us.
507535 if header_maxval < max_sample && !image. all_smaller ( header_maxval) {
508536 // Sample value greater than allowed for chosen header.
0 commit comments