@@ -30,6 +30,7 @@ static SOS: u8 = 0xDA;
30
30
static DQT : u8 = 0xDB ;
31
31
// Application segments start and end
32
32
static APP0 : u8 = 0xE0 ;
33
+ static APP2 : u8 = 0xE2 ;
33
34
34
35
// section K.1
35
36
// table K.1
@@ -346,6 +347,8 @@ pub struct JpegEncoder<W> {
346
347
chroma_actable : Cow < ' static , [ ( u8 , u16 ) ; 256 ] > ,
347
348
348
349
pixel_density : PixelDensity ,
350
+
351
+ icc_profile : Vec < u8 > ,
349
352
}
350
353
351
354
impl < W : Write > JpegEncoder < W > {
@@ -415,6 +418,8 @@ impl<W: Write> JpegEncoder<W> {
415
418
chroma_actable : Cow :: Borrowed ( & STD_CHROMA_AC_HUFF_LUT ) ,
416
419
417
420
pixel_density : PixelDensity :: default ( ) ,
421
+
422
+ icc_profile : Vec :: new ( ) ,
418
423
}
419
424
}
420
425
@@ -494,6 +499,9 @@ impl<W: Write> JpegEncoder<W> {
494
499
build_jfif_header ( & mut buf, self . pixel_density ) ;
495
500
self . writer . write_segment ( APP0 , & buf) ?;
496
501
502
+ // Write ICC profile chunks if present
503
+ self . write_icc_profile_chunks ( ) ?;
504
+
497
505
build_frame_header (
498
506
& mut buf,
499
507
8 ,
@@ -648,6 +656,43 @@ impl<W: Write> JpegEncoder<W> {
648
656
649
657
Ok ( ( ) )
650
658
}
659
+
660
+ fn write_icc_profile_chunks ( & mut self ) -> io:: Result < ( ) > {
661
+ if self . icc_profile . is_empty ( ) {
662
+ return Ok ( ( ) ) ;
663
+ }
664
+
665
+ const MAX_CHUNK_SIZE : usize = 65533 - 14 ;
666
+ const MAX_CHUNK_COUNT : usize = 255 ;
667
+ const MAX_ICC_PROFILE_SIZE : usize = MAX_CHUNK_SIZE * MAX_CHUNK_COUNT ;
668
+
669
+ if self . icc_profile . len ( ) > MAX_ICC_PROFILE_SIZE {
670
+ return Err ( io:: Error :: new (
671
+ io:: ErrorKind :: InvalidInput ,
672
+ "ICC profile too large" ,
673
+ ) ) ;
674
+ }
675
+
676
+ let chunk_iter = self . icc_profile . chunks ( MAX_CHUNK_SIZE ) ;
677
+ let num_chunks = chunk_iter. len ( ) as u8 ;
678
+ let mut segment = Vec :: new ( ) ;
679
+
680
+ for ( i, chunk) in chunk_iter. enumerate ( ) {
681
+ let chunk_number = ( i + 1 ) as u8 ;
682
+ let length = 14 + chunk. len ( ) ;
683
+
684
+ segment. clear ( ) ;
685
+ segment. reserve ( length) ;
686
+ segment. extend_from_slice ( b"ICC_PROFILE\0 " ) ;
687
+ segment. push ( chunk_number) ;
688
+ segment. push ( num_chunks) ;
689
+ segment. extend_from_slice ( chunk) ;
690
+
691
+ self . writer . write_segment ( APP2 , & segment) ?;
692
+ }
693
+
694
+ Ok ( ( ) )
695
+ }
651
696
}
652
697
653
698
impl < W : Write > ImageEncoder for JpegEncoder < W > {
@@ -661,6 +706,11 @@ impl<W: Write> ImageEncoder for JpegEncoder<W> {
661
706
) -> ImageResult < ( ) > {
662
707
self . encode ( buf, width, height, color_type)
663
708
}
709
+
710
+ fn set_icc_profile ( & mut self , icc_profile : Vec < u8 > ) -> Result < ( ) , UnsupportedError > {
711
+ self . icc_profile = icc_profile;
712
+ Ok ( ( ) )
713
+ }
664
714
}
665
715
666
716
fn build_jfif_header ( m : & mut Vec < u8 > , density : PixelDensity ) {
0 commit comments