Skip to content

[Bug]: libjpeg fatal error: DCT coefficient out of range #332

@Mikachu2333

Description

@Mikachu2333

Is there an existing issue for this?

  • I have searched the existing issues

Description

Image

Cov pic to mozjpeg failed with tips libjpeg fatal error: DCT coefficient out of range.

Expected behavior

Cov to moz successfully

Library Version

0.11.0 (latest)

Steps To Reproduce

  1. Use the code from master branch and build locally.
  2. Unzip test_pic.zip to D:\test\egHNzjeurAcw.png
  3. run with cls; cargo r --all-features -- moz -q 90 -s asdf "D:\test\egHNzjeurAcw.png"

Anything else?

I tried AI to fix this and this runs successfully, but as the cost, the parm of --smooth was disabled...

Because I don't know anything about image processing at all, I don't have the ability to pr, please help to correct it.

Click to see FULL
  diff --git a/src/codecs/mozjpeg/encoder/mod.rs b/src/codecs/mozjpeg/encoder/mod.rs
  index 4d6738c..9562b4e 100644
  --- a/src/codecs/mozjpeg/encoder/mod.rs
  +++ b/src/codecs/mozjpeg/encoder/mod.rs
  @@ -104,6 +104,10 @@ impl EncoderTrait for MozJpegEncoder {
           let (width, height) = image.dimensions();
           let data = &image.flatten_to_u8()[0];
   
  +        // Log image info for debugging
  +        log::debug!("Encoding image: {}x{}, colorspace: {:?}, {} bytes", 
  +                   width, height, image.colorspace(), data.len());
  +
           let luma_qtable = self.options.luma_qtable.as_ref();
           let chroma_qtable = self.options.chroma_qtable.as_ref();
   
  @@ -132,7 +136,23 @@ impl EncoderTrait for MozJpegEncoder {
               }
   
               comp.set_optimize_coding(self.options.optimize_coding);
  -            comp.set_smoothing_factor(self.options.smoothing);
  +            
  +            // Apply smoothing - use user setting, or auto-apply for high quality to prevent DCT overflow
  +            let smoothing = if self.options.smoothing > 0 {
  +                self.options.smoothing
  +            } else if self.options.quality >= 95.0 {
  +                // Very high quality can cause DCT coefficient overflow
  +                log::warn!("Very high quality (>= 95) detected, auto-applying smoothing (15) to prevent DCT overflow. Use --smoothing 0 to disable.");
  +                15
  +            } else if self.options.quality >= 90.0 {
  +                // High quality may cause DCT overflow with some images
  +                log::info!("High quality (>= 90) detected, auto-applying light smoothing (10) to prevent potential DCT overflow. Use --smoothing 0 to disable.");
  +                10
  +            } else {
  +                0
  +            };
  +            comp.set_smoothing_factor(smoothing);
  +            
               comp.set_color_space(match format {
                   mozjpeg::ColorSpace::JCS_GRAYSCALE => {
                       log::warn!("Input colorspace is GRAYSCALE, using GRAYSCALE as output");
  diff --git a/src/operations/icc/mod.rs b/src/operations/icc/mod.rs
  index 180e29b..5de3bf3 100644
  --- a/src/operations/icc/mod.rs
  +++ b/src/operations/icc/mod.rs
  @@ -30,8 +30,15 @@ impl OperationsTrait for ApplyICC {
   
       fn execute_impl(&self, image: &mut Image) -> Result<(), ImageErrors> {
           let src_profile = match image.metadata().icc_chunk() {
  -            Some(icc) => Profile::new_icc_context(ThreadContext::new(), icc)
  -                .map_err(|e| ImageOperationsErrors::GenericString(e.to_string()))?,
  +            Some(icc) => {
  +                match Profile::new_icc_context(ThreadContext::new(), icc) {
  +                    Ok(profile) => profile,
  +                    Err(e) => {
  +                        log::warn!("Failed to parse ICC profile: {}, using sRGB instead", e);
  +                        Profile::new_srgb_context(ThreadContext::new())
  +                    }
  +                }
  +            }
               None => Profile::new_srgb_context(ThreadContext::new()),
           };
   
  @@ -70,12 +77,61 @@ impl OperationsTrait for ApplyICC {
               Intent::Perceptual,
               Flags::NO_CACHE,
           )
  -        .map_err(|e| ImageOperationsErrors::GenericString(e.to_string()))?;
  +        .map_err(|e| {
  +            log::error!("Failed to create ICC transform: {}", e);
  +            ImageOperationsErrors::GenericString(e.to_string())
  +        })?;
   
           for frame in image.frames_mut() {
  -            let mut buffer = frame.flatten::<u8>(colorspace);
  -            t.transform_in_place(&mut buffer);
  -            let _ = std::mem::replace(frame, Frame::from_u8(&buffer, colorspace, 0, 0));
  +            let buffer = frame.flatten::<u8>(colorspace);
  +            
  +            // Store original buffer in case transformation fails or produces invalid data
  +            let original_buffer = buffer.clone();
  +            let mut transformed_buffer = buffer;
  +            
  +            // Perform ICC transformation with error recovery
  +            let transform_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
  +                t.transform_in_place(&mut transformed_buffer);
  +            }));
  +            
  +            if transform_result.is_err() {
  +                log::error!("ICC transformation panicked, using original data without transformation");
  +                let _ = std::mem::replace(frame, Frame::from_u8(&original_buffer, colorspace, 0, 0));
  +                continue;
  +            }
  +            
  +            // Validate transformed data - check for suspicious patterns
  +            let mut is_valid = true;
  +            
  +            // Quick sanity check: ensure the transformation didn't produce all zeros or all 255s
  +            let all_same = transformed_buffer.iter().all(|&x| x == transformed_buffer[0]);
  +            if all_same && transformed_buffer.len() > 100 {
  +                log::warn!("ICC transformation produced uniform data, likely corrupted");
  +                is_valid = false;
  +            }
  +            
  +            // Check for extreme value clustering that indicates corruption
  +            if is_valid {
  +                let mut extreme_count = 0;
  +                for &val in &transformed_buffer {
  +                    if val == 0 || val == 255 {
  +                        extreme_count += 1;
  +                    }
  +                }
  +                // If more than 50% of pixels are extreme values, likely corrupted
  +                if extreme_count > transformed_buffer.len() / 2 {
  +                    log::warn!("ICC transformation produced {} extreme values out of {} pixels, likely corrupted", 
  +                              extreme_count, transformed_buffer.len());
  +                    is_valid = false;
  +                }
  +            }
  +            
  +            if is_valid {
  +                let _ = std::mem::replace(frame, Frame::from_u8(&transformed_buffer, colorspace, 0, 0));
  +            } else {
  +                log::warn!("Using original image data due to invalid ICC transformation");
  +                let _ = std::mem::replace(frame, Frame::from_u8(&original_buffer, colorspace, 0, 0));
  +            }
           }
   
           image.metadata_mut().set_icc_chunk(
  @@ -117,13 +173,22 @@ impl OperationsTrait for ApplySRGB {
   
       fn execute_impl(&self, image: &mut Image) -> Result<(), ImageErrors> {
           if image.metadata().icc_chunk().is_none() {
  -            log::warn!("No icc profile in the image, skipping");
  +            log::info!("No ICC profile in the image, skipping sRGB conversion");
               return Ok(());
           }
   
  -        ApplyICC::new(Profile::new_srgb_context(ThreadContext::new())).execute_impl(image)?;
  -
  -        Ok(())
  +        log::info!("Applying sRGB ICC profile conversion");
  +        match ApplyICC::new(Profile::new_srgb_context(ThreadContext::new())).execute_impl(image) {
  +            Ok(()) => {
  +                log::info!("Successfully applied sRGB profile");
  +                Ok(())
  +            }
  +            Err(e) => {
  +                log::error!("Failed to apply sRGB profile: {}, proceeding without ICC conversion", e);
  +                // Don't fail the whole process, just skip ICC conversion
  +                Ok(())
  +            }
  +        }
       }
   
       fn supported_types(&self) -> &'static [BitType] {

Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions