@@ -2,7 +2,7 @@ use ::std;
22use std:: sync:: * ;
33use :: for_other_imageflow_crates:: preludes:: external_without_std:: * ;
44use :: ffi;
5- use :: { Context , CError , Result , JsonResponse , ErrorKind , FlowError } ;
5+ use :: { Context , CError , Result , JsonResponse , ErrorKind , FlowError , ErrorCategory } ;
66use :: ffi:: CodecInstance ;
77use :: ffi:: BitmapBgra ;
88use ffi:: DecoderColorInfo ;
@@ -89,7 +89,8 @@ impl CodecInstanceContainer {
8989}
9090
9191struct ClassicDecoder {
92- classic : CodecInstance
92+ classic : CodecInstance ,
93+ ignore_color_profile : bool ,
9394}
9495
9596impl ClassicDecoder {
@@ -107,7 +108,8 @@ impl ClassicDecoder {
107108 direction : IoDirection :: In ,
108109 io_id : io_id,
109110 io : io. get_io_ptr ( )
110- }
111+ } ,
112+ ignore_color_profile : false
111113 } ) )
112114 }
113115 }
@@ -190,7 +192,9 @@ impl Decoder for ClassicDecoder{
190192 if result. is_null ( ) {
191193 Err ( cerror ! ( c) )
192194 } else {
193- ColorTransformCache :: transform_to_srgb ( unsafe { & mut * result} , & color_info) ;
195+ if !self . ignore_color_profile {
196+ ColorTransformCache :: transform_to_srgb ( unsafe { & mut * result } , & color_info) ?;
197+ }
194198 ColorTransformCache :: dispose_color_info ( & mut color_info) ;
195199
196200
@@ -220,6 +224,10 @@ impl Decoder for ClassicDecoder{
220224 Ok ( ( ) )
221225 }
222226 }
227+ } ,
228+ s:: DecoderCommand :: DiscardColorProfile => {
229+ self . ignore_color_profile = true ;
230+ Ok ( ( ) )
223231 }
224232 }
225233 }
@@ -371,8 +379,8 @@ impl From<lcms2::Error> for FlowError{
371379 }
372380}
373381lazy_static ! {
374- static ref PROFILE_TRANSFORMS : :: chashmap:: CHashMap <u64 , Transform <u8 , u8 , ThreadContext , DisallowCache >> = :: chashmap:: CHashMap :: with_capacity( 4 ) ;
375- static ref GAMA_TRANSFORMS : :: chashmap:: CHashMap <u64 , Transform <u8 , u8 , ThreadContext , DisallowCache >> = :: chashmap:: CHashMap :: with_capacity( 4 ) ;
382+ static ref PROFILE_TRANSFORMS : :: chashmap:: CHashMap <u64 , Transform <u32 , u32 , ThreadContext , DisallowCache >> = :: chashmap:: CHashMap :: with_capacity( 4 ) ;
383+ static ref GAMA_TRANSFORMS : :: chashmap:: CHashMap <u64 , Transform <u32 , u32 , ThreadContext , DisallowCache >> = :: chashmap:: CHashMap :: with_capacity( 4 ) ;
376384
377385}
378386
@@ -388,77 +396,105 @@ impl ColorTransformCache{
388396 }
389397 }
390398
391- fn create_gama_transform ( color : & ffi:: DecoderColorInfo , pixel_format : PixelFormat ) -> Result < Transform < u8 , u8 , ThreadContext , DisallowCache > > {
399+ fn create_gama_transform ( color : & ffi:: DecoderColorInfo , pixel_format : PixelFormat ) -> Result < Transform < u32 , u32 , ThreadContext , DisallowCache > > {
392400 let srgb = Profile :: new_srgb_context ( ThreadContext :: new ( ) ) ; // Save 1ms by caching - but not sync
393401
394402 let gama = ToneCurve :: new ( 1f64 / color. gamma ) ;
395403 let p = Profile :: new_rgb_context ( ThreadContext :: new ( ) , & color. white_point , & color. primaries , & [ & gama, & gama, & gama] ) ?;
396404
397- let transform = Transform :: new_flags_context ( ThreadContext :: new ( ) , & p, pixel_format, & srgb, pixel_format, Intent :: Perceptual , flags ) ?;
405+ let transform = Transform :: new_flags_context ( ThreadContext :: new ( ) , & p, pixel_format, & srgb, pixel_format, Intent :: Perceptual , Flags :: NO_CACHE ) ?;
398406 Ok ( transform)
399407 }
400- pub fn transform_to_srgb ( frame : & mut BitmapBgra , color : & ffi:: DecoderColorInfo ) -> Result < ( ) > {
408+ fn create_profile_transform ( color : & ffi:: DecoderColorInfo , pixel_format : PixelFormat ) -> Result < Transform < u32 , u32 , ThreadContext , DisallowCache > > {
401409
402- let pixel_format = ColorTransformCache :: get_pixel_format ( frame. fmt ) ;
410+ if color. profile_buffer . is_null ( ) || color. buffer_length < 1 {
411+ unreachable ! ( ) ;
412+ }
413+ let srgb = Profile :: new_srgb_context ( ThreadContext :: new ( ) ) ; // Save 1ms by caching - but not sync
414+
415+ let bytes = unsafe { slice:: from_raw_parts ( color. profile_buffer , color. buffer_length ) } ;
403416
404- let flags: Flags < DisallowCache > = Flags :: NO_CACHE ;
417+ let p = Profile :: new_icc_context ( ThreadContext :: new ( ) , bytes) ?;
418+ //TODO: handle gray transform on rgb expanded images.
419+ //TODO: Add test coverage for grayscale png
405420
406- let transform = match color. source {
421+ let transform = Transform :: new_flags_context ( ThreadContext :: new ( ) ,
422+ & p, pixel_format, & srgb, pixel_format, Intent :: Perceptual , Flags :: NO_CACHE ) ?;
423+
424+ Ok ( transform)
425+ }
426+ fn hash ( color : & ffi:: DecoderColorInfo , pixel_format : PixelFormat ) -> Option < u64 > {
427+ match color. source {
407428 ffi:: ColorProfileSource :: Null | ffi:: ColorProfileSource :: sRGB => None ,
408429 ffi:: ColorProfileSource :: GAMA_CHRM => {
409430 let struct_bytes = unsafe {
410431 slice:: from_raw_parts ( color as * const DecoderColorInfo as * const u8 , mem:: size_of :: < DecoderColorInfo > ( ) )
411432 } ;
412- let hash = imageflow_helpers:: hashing:: hash_64 ( struct_bytes) ;
413- if !GAMA_TRANSFORMS . contains_key ( & hash) {
414- let transform = ColorTransformCache :: create_gama_transform ( color, pixel_format) ?;
415-
416- GAMA_TRANSFORMS . insert_new ( hash, transform) ;
417- }
418- Some ( GAMA_TRANSFORMS . get ( & hash) . unwrap ( ) )
419-
433+ Some ( imageflow_helpers:: hashing:: hash_64 ( struct_bytes) ^ pixel_format as u64 )
420434 } ,
421435 ffi:: ColorProfileSource :: ICCP | ffi:: ColorProfileSource :: ICCP_GRAY => {
422436 if !color. profile_buffer . is_null ( ) && color. buffer_length > 0 {
423-
424- //TODO: handle gray transform on rgb expanded images.
425- //TODO: Add test coverage for grayscale png
426-
427437 let bytes = unsafe { slice:: from_raw_parts ( color. profile_buffer , color. buffer_length ) } ;
428438
429439 // Skip first 80 bytes when hashing.
430- let hash = imageflow_helpers:: hashing:: hash_64 ( & bytes[ 80 ..] ) ;
431- if !PROFILE_TRANSFORMS . contains_key ( & hash) {
432- let srgb = Profile :: new_srgb_context ( ThreadContext :: new ( ) ) ; // Save 1ms by caching
433- let pixel_format = match frame. fmt {
434- ffi:: PixelFormat :: Bgr32 | ffi:: PixelFormat :: Bgra32 => PixelFormat :: BGRA_8 ,
435- ffi:: PixelFormat :: Bgr24 => PixelFormat :: BGR_8 ,
436- ffi:: PixelFormat :: Gray8 => PixelFormat :: GRAY_8
437- } ;
438- let p = Profile :: new_icc_context ( ThreadContext :: new ( ) , bytes) ?;
439-
440- let transform = Transform :: new_flags_context ( ThreadContext :: new ( ) ,
441- & p, pixel_format, & srgb, pixel_format, Intent :: Perceptual , flags) ?;
442- PROFILE_TRANSFORMS . insert_new ( hash, transform) ;
443- }
444- Some ( PROFILE_TRANSFORMS . get ( & hash) . unwrap ( ) )
440+ Some ( imageflow_helpers:: hashing:: hash_64 ( & bytes[ 80 ..] ) ^ pixel_format as u64 )
445441 } else {
446- unreachable ! ( ) ;
442+ unreachable ! ( "Profile source should never be set to ICCP without a profile buffer" ) ;
447443 }
448444 }
449- } ;
445+ }
446+ }
447+
448+ fn apply_transform ( frame : & mut BitmapBgra , transform : & Transform < u32 , u32 , ThreadContext , DisallowCache > ) {
449+ for row in 0 ..frame. h {
450+ let mut pixels = unsafe { slice:: from_raw_parts_mut ( frame. pixels . offset ( ( row * frame. stride ) as isize ) as * mut u32 , frame. w as usize ) } ;
451+ transform. transform_in_place ( pixels)
452+ }
453+ }
450454
455+ pub fn transform_to_srgb ( frame : & mut BitmapBgra , color : & ffi:: DecoderColorInfo ) -> Result < ( ) > {
451456
457+ if frame. fmt . bytes ( ) != 4 {
458+ return Err ( nerror ! ( ErrorKind :: Category ( ErrorCategory :: InternalError ) , "Color profile application is only supported for Bgr32 and Bgra32 canvases" ) ) ;
459+ }
460+ let pixel_format = ColorTransformCache :: get_pixel_format ( frame. fmt ) ;
452461
462+ match color. source {
463+ ffi:: ColorProfileSource :: Null | ffi:: ColorProfileSource :: sRGB => Ok ( ( ) ) ,
464+ ffi:: ColorProfileSource :: GAMA_CHRM => {
453465
454- if let Some ( p) = transform{
455- for row in 0 ..frame. h {
456- let mut bytes = unsafe { slice:: from_raw_parts_mut ( frame. pixels . offset ( ( row * frame. stride ) as isize ) , frame. w as usize * frame. fmt . bytes ( ) ) } ;
457- ( * p) . transform_in_place ( bytes)
466+ // Cache up to 4 GAMA x PixelFormat transforms
467+ if GAMA_TRANSFORMS . len ( ) > 3 {
468+ let transform = ColorTransformCache :: create_gama_transform ( color, pixel_format) ?;
469+ ColorTransformCache :: apply_transform ( frame, & transform) ;
470+ Ok ( ( ) )
471+ } else {
472+ let hash = ColorTransformCache :: hash ( color, pixel_format) . unwrap ( ) ;
473+ if !GAMA_TRANSFORMS . contains_key ( & hash) {
474+ let transform = ColorTransformCache :: create_gama_transform ( color, pixel_format) ?;
475+ GAMA_TRANSFORMS . insert_new ( hash, transform) ;
476+ }
477+ ColorTransformCache :: apply_transform ( frame, & * GAMA_TRANSFORMS . get ( & hash) . unwrap ( ) ) ;
478+ Ok ( ( ) )
479+ }
480+ } ,
481+ ffi:: ColorProfileSource :: ICCP | ffi:: ColorProfileSource :: ICCP_GRAY => {
482+ // Cache up to 9 ICC profile x PixelFormat transforms
483+ if PROFILE_TRANSFORMS . len ( ) > 8 {
484+ let transform = ColorTransformCache :: create_profile_transform ( color, pixel_format) ?;
485+ ColorTransformCache :: apply_transform ( frame, & transform) ;
486+ Ok ( ( ) )
487+ } else {
488+ let hash = ColorTransformCache :: hash ( color, pixel_format) . unwrap ( ) ;
489+ if !PROFILE_TRANSFORMS . contains_key ( & hash) {
490+ let transform = ColorTransformCache :: create_profile_transform ( color, pixel_format) ?;
491+ PROFILE_TRANSFORMS . insert_new ( hash, transform) ;
492+ }
493+ ColorTransformCache :: apply_transform ( frame, & * PROFILE_TRANSFORMS . get ( & hash) . unwrap ( ) ) ;
494+ Ok ( ( ) )
495+ }
458496 }
459497 }
460- Ok ( ( ) )
461-
462498 }
463499
464500 pub fn dispose_color_info ( color : & mut ffi:: DecoderColorInfo ) {
0 commit comments