Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  frame_header.rs

  Sprache: Rust
 

// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#![allow(clippy::excessive_precision)]

use crate::{
    BLOCK_DIM, GROUP_DIM,
    bit_reader::BitReader,
    error::Error,
    headers::{encodings::*, extra_channels::ExtraChannelInfo},
    image::Rect,
    util::FloorLog2,
};

use jxl_macros::UnconditionalCoder;
use num_derive::FromPrimitive;
use std::cmp::min;

use super::Animation;

#[derive(UnconditionalCoder, Copy, Clone, PartialEq, Debug, FromPrimitive)]
pub enum FrameType {
    RegularFrame = 0,
    LFFrame = 1,
    ReferenceOnly = 2,
    SkipProgressive = 3,
}

#[derive(UnconditionalCoder, Copy, Clone, PartialEq, Debug, FromPrimitive)]
pub enum Encoding {
    VarDCT = 0,
    Modular = 1,
}

struct Flags;

impl Flags {
    pub const ENABLE_NOISE: u64 = 1;
    pub const ENABLE_PATCHES: u64 = 2;
    pub const ENABLE_SPLINES: u64 = 0x10;
    pub const USE_LF_FRAME: u64 = 0x20;
    pub const SKIP_ADAPTIVE_LF_SMOOTHING: u64 = 0x80;
}

#[derive(UnconditionalCoder, Debug, PartialEq)]
pub struct Passes {
    #[coder(u2S(123, Bits(3) + 4))]
    #[default(1)]
    pub num_passes: u32,

    #[coder(u2S(012, Bits(1) + 3))]
    #[default(0)]
    #[condition(num_passes != 1)]
    num_ds: u32,

    #[size_coder(explicit(num_passes - 1))]
    #[coder(Bits(2))]
    #[default_element(0)]
    #[condition(num_passes != 1)]
    pub shift: Vec<u32>,

    #[size_coder(explicit(num_ds))]
    #[coder(u2S(1248))]
    #[default_element(1)]
    #[condition(num_passes != 1)]
    downsample: Vec<u32>,

    #[size_coder(explicit(num_ds))]
    #[coder(u2S(012, Bits(3)))]
    #[default_element(0)]
    #[condition(num_passes != 1)]
    last_pass: Vec<u32>,
}

impl Passes {
    pub fn downsampling_bracket(&self, pass: usize) -> (usize, usize) {
        let mut max_shift = 2;
        let mut min_shift = 3;
        for i in 0..pass + 1 {
            for j in 0..self.num_ds as usize {
                if i == self.last_pass[j] as usize {
                    min_shift = self.downsample[j].floor_log2();
                }
            }
            if i + 1 == self.num_passes as usize {
                min_shift = 0;
            }
            if i != pass {
                max_shift = min_shift.saturating_sub(1);
            }
        }
        (min_shift as usize, max_shift as usize)
    }
}

#[derive(UnconditionalCoder, Copy, Clone, PartialEq, Debug, FromPrimitive)]
pub enum BlendingMode {
    Replace = 0,
    Add = 1,
    Blend = 2,
    AlphaWeightedAdd = 3,
    Mul = 4,
}

#[derive(Default)]
pub struct BlendingInfoNonserialized {
    num_extra_channels: u32,
    full_frame: bool,
}

#[derive(UnconditionalCoder, Debug, PartialEq, Clone)]
#[nonserialized(BlendingInfoNonserialized)]
pub struct BlendingInfo {
    #[coder(u2S(012, Bits(2) + 3))]
    #[default(BlendingMode::Replace)]
    pub mode: BlendingMode,

    /* Spec: "Let multi_extra be true if and only if and the number of extra channels is at least two."
    libjxl condition is num_extra_channels > 0 */

    #[coder(u2S(012, Bits(3) + 3))]
    #[default(0)]
    #[condition(nonserialized.num_extra_channels > 0 &&
        (mode == BlendingMode::Blend || mode == BlendingMode::AlphaWeightedAdd))]
    pub alpha_channel: u32,

    #[default(false)]
    #[condition(nonserialized.num_extra_channels > 0 &&
        (mode == BlendingMode::Blend || mode == BlendingMode::AlphaWeightedAdd || mode == BlendingMode::Mul))]
    pub clamp: bool,

    #[coder(u2S(0123))]
    #[default(0)]
    // This condition is called `resets_canvas` in the spec
    #[condition(!(nonserialized.full_frame && mode == BlendingMode::Replace))]
    pub source: u32,
}

pub struct RestorationFilterNonserialized {
    encoding: Encoding,
}

#[derive(UnconditionalCoder, Debug, PartialEq)]
#[nonserialized(RestorationFilterNonserialized)]
pub struct RestorationFilter {
    #[all_default]
    all_default: bool,

    #[default(true)]
    pub gab: bool,

    #[default(false)]
    #[condition(gab)]
    gab_custom: bool,

    #[default(0.115169525)]
    #[condition(gab_custom)]
    pub gab_x_weight1: f32,

    #[default(0.061248592)]
    #[condition(gab_custom)]
    pub gab_x_weight2: f32,

    #[default(0.115169525)]
    #[condition(gab_custom)]
    pub gab_y_weight1: f32,

    #[default(0.061248592)]
    #[condition(gab_custom)]
    pub gab_y_weight2: f32,

    #[default(0.115169525)]
    #[condition(gab_custom)]
    pub gab_b_weight1: f32,

    #[default(0.061248592)]
    #[condition(gab_custom)]
    pub gab_b_weight2: f32,

    #[coder(Bits(2))]
    #[default(2)]
    pub epf_iters: u32,

    #[default(false)]
    #[condition(epf_iters > 0 && nonserialized.encoding == Encoding::VarDCT)]
    epf_sharp_custom: bool,

    #[default([0.01.0 / 7.02.0 / 7.03.0 / 7.04.0 / 7.05.0 / 7.06.0 / 7.01.0])]
    #[condition(epf_sharp_custom)]
    pub epf_sharp_lut: [f32; 8],

    #[default(false)]
    #[condition(epf_iters > 0)]
    epf_weight_custom: bool,

    #[default([40.05.03.5])]
    #[condition(epf_weight_custom)]
    pub epf_channel_scale: [f32; 3],

    #[default(0.45)]
    #[condition(epf_weight_custom)]
    epf_pass1_zeroflush: f32,

    #[default(0.6)]
    #[condition(epf_weight_custom)]
    epf_pass2_zeroflush: f32,

    #[default(false)]
    #[condition(epf_iters > 0)]
    epf_sigma_custom: bool,

    #[default(0.46)]
    #[condition(epf_sigma_custom && nonserialized.encoding == Encoding::VarDCT)]
    pub epf_quant_mul: f32,

    #[default(0.9)]
    #[condition(epf_sigma_custom)]
    pub epf_pass0_sigma_scale: f32,

    #[default(6.5)]
    #[condition(epf_sigma_custom)]
    pub epf_pass2_sigma_scale: f32,

    #[default(2.0 / 3.0)]
    #[condition(epf_sigma_custom)]
    pub epf_border_sad_mul: f32,

    #[default(1.0)]
    #[condition(epf_iters > 0 && nonserialized.encoding == Encoding::Modular)]
    pub epf_sigma_for_modular: f32,

    #[default(Extensions::default())]
    extensions: Extensions,
}

pub struct PermutationNonserialized {
    pub num_entries: u32,
    pub permuted: bool,
}

pub struct FrameHeaderNonserialized {
    pub xyb_encoded: bool,
    pub num_extra_channels: u32,
    pub extra_channel_info: Vec<ExtraChannelInfo>,
    pub have_animation: bool,
    pub have_timecode: bool,
    pub img_width: u32,
    pub img_height: u32,
}

const H_SHIFT: [usize; 4] = [0110];
const V_SHIFT: [usize; 4] = [0101];

fn compute_jpeg_shift(jpeg_upsampling: &[u32], shift_table: &[usize]) -> u32 {
    jpeg_upsampling
        .iter()
        .map(|&ch| shift_table[ch as usize])
        .max()
        .unwrap_or(0as u32
}

#[derive(UnconditionalCoder, Debug, PartialEq)]
#[nonserialized(FrameHeaderNonserialized)]
#[aligned]
#[validate]
pub struct FrameHeader {
    #[all_default]
    all_default: bool,

    #[coder(Bits(2))]
    #[default(FrameType::RegularFrame)]
    pub frame_type: FrameType,

    #[coder(Bits(1))]
    #[default(Encoding::VarDCT)]
    pub encoding: Encoding,

    #[default(0)]
    flags: u64,

    #[default(false)]
    #[condition(!nonserialized.xyb_encoded)]
    pub do_ycbcr: bool,

    #[coder(Bits(2))]
    #[default([000])]
    #[condition(do_ycbcr && flags & Flags::USE_LF_FRAME == 0)]
    jpeg_upsampling: [u32; 3],

    #[coder(u2S(1248))]
    #[default(1)]
    #[condition(flags & Flags::USE_LF_FRAME == 0)]
    pub upsampling: u32,

    #[size_coder(explicit(nonserialized.num_extra_channels))]
    #[coder(u2S(1248))]
    #[default_element(1)]
    #[condition(flags & Flags::USE_LF_FRAME == 0)]
    pub ec_upsampling: Vec<u32>,

    #[coder(Bits(2))]
    #[default(1)]
    #[condition(encoding == Encoding::Modular)]
    group_size_shift: u32,

    #[coder(Bits(3))]
    #[default(3)]
    #[condition(encoding == Encoding::VarDCT && nonserialized.xyb_encoded)]
    pub x_qm_scale: u32,

    #[coder(Bits(3))]
    #[default(2)]
    #[condition(encoding == Encoding::VarDCT && nonserialized.xyb_encoded)]
    pub b_qm_scale: u32,

    #[condition(frame_type != FrameType::ReferenceOnly)]
    #[default(Passes::default(&field_nonserialized))]
    pub passes: Passes,

    #[coder(u2S(1234))]
    #[default(0)]
    #[condition(frame_type == FrameType::LFFrame)]
    pub lf_level: u32,

    #[default(false)]
    #[condition(frame_type != FrameType::LFFrame)]
    have_crop: bool,

    #[coder(u2S(Bits(8), Bits(11) + 256, Bits(14) + 2304, Bits(30) + 18688))]
    #[default(0)]
    #[condition(have_crop && frame_type != FrameType::ReferenceOnly)]
    pub x0: i32,

    #[coder(u2S(Bits(8), Bits(11) + 256, Bits(14) + 2304, Bits(30) + 18688))]
    #[default(0)]
    #[condition(have_crop && frame_type != FrameType::ReferenceOnly)]
    pub y0: i32,

    #[coder(u2S(Bits(8), Bits(11) + 256, Bits(14) + 2304, Bits(30) + 18688))]
    #[default(0)]
    #[condition(have_crop)]
    frame_width: u32,

    #[coder(u2S(Bits(8), Bits(11) + 256, Bits(14) + 2304, Bits(30) + 18688))]
    #[default(0)]
    #[condition(have_crop)]
    frame_height: u32,

    // The following 2 fields are not actually serialized, but just used as variables to help with
    // defining later conditions.
    #[default(x0 <= 0 && y0 <= 0 && (frame_width as i64 + x0 as i64) >= nonserialized.img_width as i64 &&
        (frame_height as i64 + y0 as i64) >= nonserialized.img_height as i64)]
    #[condition(false)]
    completely_covers: bool,

    #[default(!have_crop || completely_covers)]
    #[condition(false)]
    full_frame: bool,

    /* "normal_frame" denotes the condition !all_default
    && (frame_type == kRegularFrame || frame_type == kSkipProgressive) */

    #[default(BlendingInfo::default(&field_nonserialized))]
    #[condition(frame_type == FrameType::RegularFrame || frame_type == FrameType::SkipProgressive)]
    #[nonserialized(num_extra_channels : nonserialized.num_extra_channels, full_frame : full_frame)]
    pub blending_info: BlendingInfo,

    #[size_coder(explicit(nonserialized.num_extra_channels))]
    #[condition(frame_type == FrameType::RegularFrame || frame_type == FrameType::SkipProgressive)]
    #[default_element(BlendingInfo::default(&field_nonserialized))]
    #[nonserialized(num_extra_channels : nonserialized.num_extra_channels, full_frame: full_frame)]
    pub ec_blending_info: Vec<BlendingInfo>,

    #[coder(u2S(01, Bits(8), Bits(32)))]
    #[default(0)]
    #[condition((frame_type == FrameType::RegularFrame ||
        frame_type == FrameType::SkipProgressive) && nonserialized.have_animation)]
    pub duration: u32,

    #[coder(Bits(32))]
    #[default(0)]
    #[condition((frame_type == FrameType::RegularFrame ||
        frame_type == FrameType::SkipProgressive) && nonserialized.have_timecode)]
    timecode: u32,

    #[default(frame_type == FrameType::RegularFrame)]
    #[condition(frame_type == FrameType::RegularFrame || frame_type == FrameType::SkipProgressive)]
    pub is_last: bool,

    #[coder(Bits(2))]
    #[default(0)]
    #[condition(frame_type != FrameType::LFFrame && !is_last)]
    pub save_as_reference: u32,

    // The following 2 fields are not actually serialized, but just used as variables to help with
    // defining later conditions.
    #[default(!is_last && frame_type != FrameType::LFFrame && (duration == 0 || save_as_reference !0))]
    #[condition(false)]
    pub can_be_referenced: bool,

    #[default(can_be_referenced && blending_info.mode == BlendingMode::Replace && full_frame &&
              (frame_type == FrameType::RegularFrame || frame_type == FrameType::SkipProgressive))]
    #[condition(false)]
    save_before_ct_def_false: bool,

    #[default(frame_type == FrameType::LFFrame)]
    #[condition(frame_type == FrameType::ReferenceOnly || save_before_ct_def_false)]
    pub save_before_ct: bool,

    pub name: String,

    #[default(RestorationFilter::default(&field_nonserialized))]
    #[nonserialized(encoding : encoding)]
    pub restoration_filter: RestorationFilter,

    #[default(Extensions::default())]
    extensions: Extensions,

    // The following fields are not actually serialized, but just used as variables for
    // implementing the methods below.
    #[coder(Bits(0))]
    #[default(if frame_width == 0 { nonserialized.img_width } else { frame_width })]
    #[condition(false)]
    pub width: u32,

    #[coder(Bits(0))]
    #[default(if frame_height == 0 { nonserialized.img_height } else { frame_height })]
    #[condition(false)]
    pub height: u32,

    #[coder(Bits(0))]
    #[default(compute_jpeg_shift(&jpeg_upsampling, &H_SHIFT))]
    #[condition(false)]
    pub maxhs: u32,

    #[coder(Bits(0))]
    #[default(compute_jpeg_shift(&jpeg_upsampling, &V_SHIFT))]
    #[condition(false)]
    pub maxvs: u32,

    #[coder(Bits(0))]
    #[default(nonserialized.num_extra_channels)]
    #[condition(false)]
    pub num_extra_channels: u32,
}

impl FrameHeader {
    pub fn log_group_dim(&self) -> usize {
        (GROUP_DIM.ilog2() - 1 + self.group_size_shift) as usize
    }
    pub fn group_dim(&self) -> usize {
        1 << self.log_group_dim()
    }
    pub fn lf_group_dim(&self) -> usize {
        self.group_dim() * BLOCK_DIM
    }

    pub fn num_groups(&self) -> usize {
        self.size_groups().0 * self.size_groups().1
    }

    pub fn num_lf_groups(&self) -> usize {
        self.size_lf_groups().0 * self.size_lf_groups().1
    }

    pub fn num_toc_entries(&self) -> usize {
        let num_groups = self.num_groups();
        let num_dc_groups = self.num_lf_groups();

        if num_groups == 1 && self.passes.num_passes == 1 {
            1
        } else {
            2 + num_dc_groups + num_groups * self.passes.num_passes as usize
        }
    }

    pub fn duration(&self, animation: &Animation) -> f64 {
        (self.duration as f64) * 1000.0 * (animation.tps_denominator as f64)
            / (animation.tps_numerator as f64)
    }

    pub fn has_patches(&self) -> bool {
        self.flags & Flags::ENABLE_PATCHES != 0
    }

    pub fn has_noise(&self) -> bool {
        self.flags & Flags::ENABLE_NOISE != 0
    }

    pub fn has_splines(&self) -> bool {
        self.flags & Flags::ENABLE_SPLINES != 0
    }
    pub fn has_lf_frame(&self) -> bool {
        self.flags & Flags::USE_LF_FRAME != 0
    }
    pub fn should_do_adaptive_lf_smoothing(&self) -> bool {
        self.flags & Flags::SKIP_ADAPTIVE_LF_SMOOTHING == 0
            && !self.has_lf_frame()
            && self.encoding == Encoding::VarDCT
    }
    pub fn raw_hshift(&self, c: usize) -> usize {
        H_SHIFT[self.jpeg_upsampling[c] as usize]
    }
    pub fn hshift(&self, c: usize) -> usize {
        (self.maxhs as usize) - self.raw_hshift(c)
    }
    pub fn raw_vshift(&self, c: usize) -> usize {
        V_SHIFT[self.jpeg_upsampling[c] as usize]
    }
    pub fn vshift(&self, c: usize) -> usize {
        (self.maxvs as usize) - self.raw_vshift(c)
    }
    pub fn is444(&self) -> bool {
        self.hshift(0) == 0 && self.vshift(0) == 0 &&  // Cb
        self.hshift(2) == 0 && self.vshift(2) == 0 &&  // Cr
        self.hshift(1) == 0 && self.vshift(1) == 0 // Y
    }
    pub fn is420(&self) -> bool {
        self.hshift(0) == 1 && self.vshift(0) == 1 &&  // Cb
        self.hshift(2) == 1 && self.vshift(2) == 1 &&  // Cr
        self.hshift(1) == 0 && self.vshift(1) == 0 // Y
    }
    pub fn is422(&self) -> bool {
        self.hshift(0) == 1 && self.vshift(0) == 0 &&  // Cb
        self.hshift(2) == 1 && self.vshift(2) == 0 &&  // Cr
        self.hshift(1) == 0 && self.vshift(1) == 0 // Y
    }
    pub fn is440(&self) -> bool {
        self.hshift(0) == 0 && self.vshift(0) == 1 &&  // Cb
        self.hshift(2) == 0 && self.vshift(2) == 1 &&  // Cr
        self.hshift(1) == 0 && self.vshift(1) == 0 // Y
    }

    pub fn is_visible(&self) -> bool {
        (self.is_last || self.duration > 0)
            && (self.frame_type == FrameType::RegularFrame
                || self.frame_type == FrameType::SkipProgressive)
    }

    pub fn needs_blending(&self) -> bool {
        if !(self.frame_type == FrameType::RegularFrame
            || self.frame_type == FrameType::SkipProgressive)
        {
            return false;
        }
        let replace_all = self.blending_info.mode == BlendingMode::Replace
            && self
                .ec_blending_info
                .iter()
                .all(|x| x.mode == BlendingMode::Replace);
        self.have_crop || !replace_all
    }

    /// The dimensions of this frame, as coded in the codestream, excluding padding pixels.
    pub fn size(&self) -> (usize, usize) {
        let (width, height) = self.size_upsampled();
        (
            width.div_ceil(self.upsampling as usize),
            height.div_ceil(self.upsampling as usize),
        )
    }

    /// The dimensions of this frame, as coded in the codestream, in 8x8 blocks.
    pub fn size_blocks(&self) -> (usize, usize) {
        (
            self.size().0.div_ceil(BLOCK_DIM << self.maxhs) << self.maxhs,
            self.size().1.div_ceil(BLOCK_DIM << self.maxvs) << self.maxvs,
        )
    }

    /// The dimensions of this frame, as coded in the codestream but including padding pixels.
    pub fn size_padded(&self) -> (usize, usize) {
        if self.encoding == Encoding::Modular {
            self.size()
        } else {
            (
                self.size_blocks().0 * BLOCK_DIM,
                self.size_blocks().1 * BLOCK_DIM,
            )
        }
    }

    /// The dimensions of this frame, after upsampling.
    pub fn size_upsampled(&self) -> (usize, usize) {
        (
            self.width.div_ceil(1 << (3 * self.lf_level)) as usize,
            self.height.div_ceil(1 << (3 * self.lf_level)) as usize,
        )
    }

    pub fn size_padded_upsampled(&self) -> (usize, usize) {
        let (xsize, ysize) = self.size_padded();
        (
            xsize * self.upsampling as usize,
            ysize * self.upsampling as usize,
        )
    }

    /// The dimensions of this frame, in groups.
    pub fn size_groups(&self) -> (usize, usize) {
        (
            self.size().0.div_ceil(self.group_dim()),
            self.size().1.div_ceil(self.group_dim()),
        )
    }

    /// The dimensions of this frame, in LF groups.
    pub fn size_lf_groups(&self) -> (usize, usize) {
        (
            self.size_blocks().0.div_ceil(self.group_dim()),
            self.size_blocks().1.div_ceil(self.group_dim()),
        )
    }

    pub fn block_group_rect(&self, group: usize) -> Rect {
        let group_dims = self.size_groups();
        let block_dims = self.size_blocks();
        let group_dim_in_blocks = self.group_dim() >> 3;
        let gx = group % group_dims.0;
        let gy = group / group_dims.0;
        let origin = (gx * group_dim_in_blocks, gy * group_dim_in_blocks);
        let size = (
            min(
                block_dims.0.checked_sub(origin.0).unwrap(),
                group_dim_in_blocks,
            ),
            min(
                block_dims.1.checked_sub(origin.1).unwrap(),
                group_dim_in_blocks,
            ),
        );
        Rect { origin, size }
    }

    pub fn lf_group_rect(&self, group: usize) -> Rect {
        let lf_dims = self.size_lf_groups();
        let block_dims = self.size_blocks();
        let gx = group % lf_dims.0;
        let gy = group / lf_dims.0;
        let origin = (gx * self.group_dim(), gy * self.group_dim());
        let size = (
            min(
                block_dims.0.checked_sub(origin.0).unwrap(),
                self.group_dim(),
            ),
            min(
                block_dims.1.checked_sub(origin.1).unwrap(),
                self.group_dim(),
            ),
        );
        Rect { origin, size }
    }

    pub fn postprocess(&mut self, nonserialized: &FrameHeaderNonserialized) {
        if self.upsampling > 1 {
            for i in 0..nonserialized.extra_channel_info.len() {
                let dim_shift = nonserialized.extra_channel_info[i].dim_shift();
                self.ec_upsampling[i] <<= dim_shift;
            }
        }
        if self.encoding != Encoding::VarDCT || !nonserialized.xyb_encoded {
            self.x_qm_scale = 2;
        }
    }

    fn check(&self, nonserialized: &FrameHeaderNonserialized) -> Result<(), Error> {
        if self.upsampling > 1
            && let Some((dim_shift, effective_upsampling)) = nonserialized
                .extra_channel_info
                .iter()
                .zip(&self.ec_upsampling)
                .find_map(|(info, ec_upsampling)| {
                    let effective_upsampling = ec_upsampling << info.dim_shift();
                    ((effective_upsampling < self.upsampling) || (effective_upsampling > 8))
                        .then_some((info.dim_shift(), effective_upsampling))
                })
        {
            return Err(Error::InvalidEcUpsampling(
                self.upsampling,
                dim_shift,
                effective_upsampling,
            ));
        }

        if self.has_patches()
            && self.upsampling != 1
            && let Some(&ec_upsampling) = self
                .ec_upsampling
                .iter()
                .find(|&&ec_upsampling| ec_upsampling != self.upsampling)
        {
            return Err(Error::PatchesUnsupportedMixedUpsampling(
                self.upsampling,
                ec_upsampling,
            ));
        }

        let num_extra_channels = nonserialized.extra_channel_info.len();
        let uses_alpha =
            |mode| matches!(mode, BlendingMode::Blend | BlendingMode::AlphaWeightedAdd);

        debug_assert_eq!(self.ec_blending_info.len(), num_extra_channels);

        if num_extra_channels > 0
            && uses_alpha(self.blending_info.mode)
            && self.blending_info.alpha_channel as usize >= num_extra_channels
        {
            return Err(Error::InvalidBlendingAlphaChannel(
                self.blending_info.alpha_channel as usize,
                num_extra_channels,
            ));
        }

        for info in &self.ec_blending_info {
            if num_extra_channels > 0
                && uses_alpha(info.mode)
                && info.alpha_channel as usize >= num_extra_channels
            {
                return Err(Error::InvalidBlendingAlphaChannel(
                    info.alpha_channel as usize,
                    num_extra_channels,
                ));
            }
        }

        if self.has_lf_frame() && self.lf_level >= 4 {
            return Err(Error::InvalidLfLevel(self.lf_level));
        }

        if self.passes.num_ds >= self.passes.num_passes {
            return Err(Error::NumPassesTooLarge(
                self.passes.num_ds,
                self.passes.num_passes,
            ));
        }

        for w in self.passes.downsample.windows(2) {
            let [last_ds, ds] = w else { unreachable!() };
            if ds >= last_ds {
                return Err(Error::PassesDownsampleNonDecreasing);
            }
        }

        for w in self.passes.last_pass.windows(2) {
            let [last_lp, lp] = w else { unreachable!() };
            if lp <= last_lp {
                return Err(Error::PassesLastPassNonIncreasing);
            }
        }

        for &lp in self.passes.last_pass.iter() {
            if lp >= self.passes.num_passes {
                return Err(Error::PassesLastPassTooLarge);
            }
        }

        if !self.save_before_ct && !self.full_frame && self.frame_type == FrameType::ReferenceOnly {
            return Err(Error::NonPatchReferenceWithCrop);
        }
        if !self.is444()
            && ((self.flags & Flags::SKIP_ADAPTIVE_LF_SMOOTHING) == 0)
            && self.encoding == Encoding::VarDCT
        {
            return Err(Error::Non444ChromaSubsampling);
        }
        Ok(())
    }
}

#[cfg(test)]
mod test_frame_header {
    use super::super::bit_depth::BitDepth;
    use super::super::extra_channels::{ExtraChannel, ExtraChannelInfo};
    use super::super::permutation::Permutation;
    use super::super::toc::Toc;
    use super::*;
    use crate::util::test::read_headers_and_toc;
    use test_log::test;

    #[test]
    fn test_basic() {
        let (_, frame_header, toc) =
            read_headers_and_toc(include_bytes!("../../resources/test/basic.jxl")).unwrap();
        assert_eq!(frame_header.frame_type, FrameType::RegularFrame);
        assert_eq!(frame_header.encoding, Encoding::VarDCT);
        assert_eq!(frame_header.flags, 0);
        assert_eq!(frame_header.upsampling, 1);
        assert_eq!(frame_header.x_qm_scale, 2);
        assert_eq!(frame_header.b_qm_scale, 2);
        assert!(!frame_header.have_crop);
        assert!(!frame_header.save_before_ct);
        assert_eq!(frame_header.name, String::from(""));
        assert_eq!(frame_header.restoration_filter.epf_iters, 1);
        assert_eq!(
            toc,
            Toc {
                permuted: false,
                permutation: Permutation::default(),
                entries: [53].to_vec(),
            }
        )
    }

    #[test]
    fn test_extra_channel() {
        let frame_header =
            read_headers_and_toc(include_bytes!("../../resources/test/extra_channels.jxl"))
                .unwrap()
                .1;
        assert_eq!(frame_header.frame_type, FrameType::RegularFrame);
        assert_eq!(frame_header.encoding, Encoding::Modular);
        assert_eq!(frame_header.flags, 0);
        assert_eq!(frame_header.upsampling, 1);
        assert_eq!(frame_header.ec_upsampling, vec![1]);
        // libjxl x_qm_scale = 2, but condition is false (should be 3 according to the draft)
        // Doesn't actually matter since this is modular mode and the value doesn't get used.
        assert_eq!(frame_header.x_qm_scale, 3);
        assert_eq!(frame_header.b_qm_scale, 2);
        assert!(!frame_header.have_crop);
        assert!(!frame_header.save_before_ct);
        assert_eq!(frame_header.name, String::from(""));
        assert_eq!(frame_header.restoration_filter.epf_iters, 0);
        assert!(!frame_header.restoration_filter.gab);
    }

    #[test]
    fn test_invalid_blending_alpha_channel() {
        let (file_header, mut frame_header, _) =
            read_headers_and_toc(include_bytes!("../../resources/test/extra_channels.jxl"))
                .unwrap();
        let nonserialized = file_header.frame_header_nonserialized();
        frame_header.blending_info.mode = BlendingMode::Blend;
        frame_header.blending_info.alpha_channel = nonserialized.extra_channel_info.len() as u32;

        let err = frame_header.check(&nonserialized).unwrap_err();
        assert!(matches!(err, Error::InvalidBlendingAlphaChannel(_, _)));
    }

    #[test]
    fn test_invalid_ec_upsampling_after_dim_shift() {
        let (file_header, mut frame_header, _) =
            read_headers_and_toc(include_bytes!("../../resources/test/extra_channels.jxl"))
                .unwrap();
        let mut nonserialized = file_header.frame_header_nonserialized();

        nonserialized.extra_channel_info[0] = ExtraChannelInfo::new(
            false,
            ExtraChannel::Alpha,
            BitDepth::integer_samples(8),
            3,
            String::new(),
            false,
            None,
            None,
        );

        frame_header.upsampling = 8;
        frame_header.ec_upsampling[0] = 4;

        let err = frame_header.check(&nonserialized).unwrap_err();
        assert!(matches!(err, Error::InvalidEcUpsampling(8332)));
    }

    #[test]
    fn test_has_permutation() {
        let (_, frame_header, toc) =
            read_headers_and_toc(include_bytes!("../../resources/test/has_permutation.jxl"))
                .unwrap();
        assert_eq!(frame_header.frame_type, FrameType::RegularFrame);
        assert_eq!(frame_header.encoding, Encoding::VarDCT);
        assert_eq!(frame_header.flags, 0);
        assert_eq!(frame_header.upsampling, 1);
        assert_eq!(frame_header.x_qm_scale, 3);
        assert_eq!(frame_header.b_qm_scale, 2);
        assert!(!frame_header.have_crop);
        assert!(!frame_header.save_before_ct);
        assert_eq!(frame_header.name, String::from(""));
        assert_eq!(frame_header.restoration_filter.epf_iters, 1);
        assert_eq!(
            toc,
            Toc {
                permuted: true,
                permutation: Permutation(std::borrow::Cow::Owned(vec![
                    0u32, 1424823456789431011121314151617,
                    4418192021222324254526272829303132334634,
                    3536373839404147,
                ])),
                entries: vec![
                    15599255555555555555559999999,
                    999999999555555556975555560,
                ],
            },
        )
    }

    #[test]
    fn test_frame_name() {
        let (_, frame_header, _) =
            read_headers_and_toc(include_bytes!("../../resources/test/named_frame_test.jxl"))
                .unwrap();
        assert_eq!(frame_header.frame_type, FrameType::RegularFrame);
        assert_eq!(frame_header.name, "TestFrameName");
        assert_eq!(frame_header.name.len(), 13);
    }
}

Messung V0.5 in Prozent
C=90 H=98 G=94

¤ Dauer der Verarbeitung: 0.10 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002