// 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.
/* 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(0, 1, 2, Bits(3) + 3))] #[default(0)] #[condition(nonserialized.num_extra_channels > 0 &&
(mode == BlendingMode::Blend || mode == BlendingMode::AlphaWeightedAdd))] pub alpha_channel: u32,
#[coder(u2S(0, 1, 2, 3))] #[default(0)] // This condition is called `resets_canvas` in the spec #[condition(!(nonserialized.full_frame && mode == BlendingMode::Replace))] pub source: 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,
// 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,
// 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,
/// The dimensions of this frame, as coded in the codestream, excluding padding pixels. pubfn 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. pubfn 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. pubfn size_padded(&self) -> (usize, usize) { ifself.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. pubfn 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,
)
}
pubfn 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. pubfn 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. pubfn size_lf_groups(&self) -> (usize, usize) {
( self.size_blocks().0.div_ceil(self.group_dim()), self.size_blocks().1.div_ceil(self.group_dim()),
)
}
pubfn 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 }
}
pubfn 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 }
}
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,
));
}
}
for w inself.passes.downsample.windows(2) { let [last_ds, ds] = w else { unreachable!() }; if ds >= last_ds { return Err(Error::PassesDownsampleNonDecreasing);
}
}
for w inself.passes.last_pass.windows(2) { let [last_lp, lp] = w else { unreachable!() }; if lp <= last_lp { return Err(Error::PassesLastPassNonIncreasing);
}
}
for &lp inself.passes.last_pass.iter() { if lp >= self.passes.num_passes { return Err(Error::PassesLastPassTooLarge);
}
}
#[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(_, _)));
}
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.