mirror of
https://github.com/storytold/photocraft.git
synced 2026-11-10 20:32:31 +00:00
Refactor codecs for strict Clippy checks
This commit is contained in:
1 parent
ddaf299335
commit
9a348da8bb
7 files changed
+192
-157
No files matched your search
@@ -27,7 +27,7 @@
|
||||
//! (see [`writes_little_endian`]), which the layer data must match.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::io::{self, Cursor, Read, Seek, SeekFrom};
|
||||
use std::io::{self, Read, Seek, SeekFrom};
|
||||
|
||||
use half::f16;
|
||||
use tiff::decoder::{ChunkType, Decoder};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::decode::{Layout, Patched, Src, is_eof};
|
||||
use super::*;
|
||||
use super::{CodecError, Decoder, SampleType, err, f16, map_err};
|
||||
|
||||
/// The a* and b* samples (1 and 2 of every pixel) of a Lab image between the file's encoding and
|
||||
/// PhotoCraft's (8 bits: `a + 128`; 16 bits: `(a + 128) · 257`). In the file, CIELab holds them
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use super::bands::{convert_ab, decode_bands, extra_sample_indices, split_extra_samples, zeroed};
|
||||
use super::tags::{F, TAG_IMAGE_SOURCE_DATA, TAG_PHOTOSHOP, TAG_XMP, TEXT_TAGS, photoshop_tags};
|
||||
use super::*;
|
||||
use super::{
|
||||
ChannelLayout, ChunkType, CodecError, DecodeWarning, Decoder, ExifDirs, File, Ifd, Image, Limits, Metadata, Read, SampleType, Seek, SeekFrom, Tag,
|
||||
camera_raw, err, ifd, io, map_err, tiff_exif,
|
||||
};
|
||||
|
||||
/// Decodes the page Photoshop would open: the first full-resolution directory.
|
||||
pub(crate) fn decode(bytes: &[u8], limits: &Limits) -> Result<Image, CodecError> {
|
||||
@@ -235,6 +238,112 @@ pub(super) fn is_eof(e: &tiff::TiffError) -> bool {
|
||||
matches!(e, tiff::TiffError::IoError(io) if io.kind() == io::ErrorKind::UnexpectedEof)
|
||||
}
|
||||
|
||||
/// The sample type of a (SampleFormat, BitsPerSample) pair.
|
||||
fn sample_src(format: u64, bits: u64) -> Result<Src, CodecError> {
|
||||
Ok(match (format, bits) {
|
||||
(1, 1 | 2 | 4) => Src::Bits(bits as u8),
|
||||
(1, 8) => Src::U8,
|
||||
(1, 16) => Src::U16,
|
||||
(1, 32) => Src::U32,
|
||||
(1, 64) => Src::U64,
|
||||
(3, 16) => Src::F16,
|
||||
(3, 32) => Src::F32,
|
||||
(3, 64) => Src::F64,
|
||||
(2, _) => return Err(CodecError::unsupported(F, "signed integer samples")),
|
||||
_ => return Err(CodecError::unsupported(F, format!("{bits}-bit samples (sample format {format})"))),
|
||||
})
|
||||
}
|
||||
|
||||
/// The ColorMap of a palette image as RGB entries.
|
||||
fn read_palette(f: &File<'_>, dir: &Ifd, spp: u64, bits: u64, src: Src) -> Result<Vec<[u16; 3]>, CodecError> {
|
||||
if spp != 1 || !matches!(src, Src::Bits(_) | Src::U8 | Src::U16) {
|
||||
return Err(CodecError::unsupported(F, format!("palette image with {spp} samples of {bits} bits")));
|
||||
}
|
||||
let n = 1u64 << bits;
|
||||
let map = dir.get(ifd::TAG_COLOR_MAP).filter(|e| e.ty == 3 && e.count == 3 * n).ok_or_else(|| err("palette image without a valid ColorMap"))?;
|
||||
let at = |i: u64| f.uint(map, i).and_then(|v| u16::try_from(v).ok());
|
||||
let table: Option<Vec<[u16; 3]>> = (0..n).map(|i| Some([at(i)?, at(n + i)?, at(2 * n + i)?])).collect();
|
||||
table.ok_or_else(|| err("palette ColorMap cut off"))
|
||||
}
|
||||
|
||||
/// The layout of a multiband read that was asked for every sample.
|
||||
fn multiband_layout(photometric: u64, has_alpha: bool, gray_alpha: bool) -> ChannelLayout {
|
||||
match (photometric, has_alpha) {
|
||||
(2 | 8 | 9, true) => ChannelLayout::Rgba,
|
||||
(2 | 8 | 9, false) => ChannelLayout::Rgb,
|
||||
(5, true) => ChannelLayout::CmykA,
|
||||
(5, false) => ChannelLayout::Cmyk,
|
||||
_ if gray_alpha => ChannelLayout::GrayA,
|
||||
_ => ChannelLayout::Gray,
|
||||
}
|
||||
}
|
||||
|
||||
/// The chunk tables must match the geometry. Checked in 64 bits, before the `tiff` crate does
|
||||
/// the same arithmetic in narrower types.
|
||||
fn check_chunk_tables(f: &File<'_>, dir: &Ifd, (w, h): (u32, u32), spp: u64, planar: bool, tiled: bool) -> Result<(u64, u64), CodecError> {
|
||||
let need = |tag: u16, name: &str| f.tag_uint(dir, tag).ok_or_else(|| err(format!("missing or unreadable {name}")));
|
||||
let (cw, ch) = if tiled {
|
||||
let tw = need(ifd::TAG_TILE_WIDTH, "TileWidth")?;
|
||||
let tl = need(ifd::TAG_TILE_LENGTH, "TileLength")?;
|
||||
(tw, tl)
|
||||
} else {
|
||||
(u64::from(w), f.tag_uint(dir, ifd::TAG_ROWS_PER_STRIP).unwrap_or(u64::from(h)).min(u64::from(h)))
|
||||
};
|
||||
if cw == 0 || ch == 0 {
|
||||
return Err(err("zero tile or strip size"));
|
||||
}
|
||||
// The chunk tables must match the geometry. Checked here, in 64 bits, before the `tiff`
|
||||
// crate does the same arithmetic in narrower types.
|
||||
let planes_in_file = if planar { spp } else { 1 };
|
||||
let expected = u64::from(w)
|
||||
.div_ceil(cw)
|
||||
.checked_mul(u64::from(h).div_ceil(ch))
|
||||
.and_then(|n| n.checked_mul(planes_in_file))
|
||||
.filter(|&n| n <= u64::from(u32::MAX))
|
||||
.ok_or_else(|| err("too many strips or tiles"))?;
|
||||
let (offsets_tag, counts_tag) = if tiled { (324, 325) } else { (273, 279) };
|
||||
let counts = (dir.get(offsets_tag).map(|e| e.count), dir.get(counts_tag).map(|e| e.count));
|
||||
if counts != (Some(expected), Some(expected)) {
|
||||
return Err(err(format!("{expected} strips or tiles expected, the tables list {counts:?}")));
|
||||
}
|
||||
Ok((cw, ch))
|
||||
}
|
||||
|
||||
/// Byte patches the `tiff` crate reads through: the directory is seen as the last of its chain,
|
||||
/// and PhotometricInterpretation is handed over as black-is-zero (or RGB for Lab) when asked to.
|
||||
fn header_patches(
|
||||
f: &File<'_>,
|
||||
dir: &Ifd,
|
||||
photometric_entry: Option<ifd::Entry>,
|
||||
patch_photometric: bool,
|
||||
lab_only: bool,
|
||||
) -> Result<Vec<(u64, Vec<u8>)>, CodecError> {
|
||||
let mut patches: Vec<(u64, Vec<u8>)> = Vec::new();
|
||||
let encode_u = |v: u64, len: usize| -> Vec<u8> {
|
||||
let b = if f.le { v.to_le_bytes() } else { v.to_be_bytes() };
|
||||
if f.le { b.get(..len).map(<[u8]>::to_vec).unwrap_or_default() } else { b.get(8 - len..).map(<[u8]>::to_vec).unwrap_or_default() }
|
||||
};
|
||||
if f.big {
|
||||
patches.push((8, encode_u(dir.at, 8)));
|
||||
} else {
|
||||
let at = u32::try_from(dir.at).map_err(|_| err("directory offset out of range"))?;
|
||||
patches.push((4, encode_u(u64::from(at), 4)));
|
||||
}
|
||||
// The directory is seen as the last of its chain: the chain was walked already, and a
|
||||
// cycle through it must not fail this page.
|
||||
patches.push((dir.next_at, vec![0; if f.big { 8 } else { 4 }]));
|
||||
if patch_photometric {
|
||||
let e = photometric_entry.ok_or_else(|| err("missing PhotometricInterpretation"))?;
|
||||
let len = match e.ty {
|
||||
3 if e.count == 1 => 2,
|
||||
4 if e.count == 1 => 4,
|
||||
_ => return Err(err("malformed PhotometricInterpretation")),
|
||||
};
|
||||
patches.push((e.value_at, encode_u(if lab_only { 2 } else { 1 }, len)));
|
||||
}
|
||||
Ok(patches)
|
||||
}
|
||||
|
||||
/// Decodes the pixels of one directory.
|
||||
fn decode_ifd(f: &File<'_>, dir: &Ifd, limits: &Limits) -> Result<Image, CodecError> {
|
||||
// The values the `tiff` crate reads must exist: a count past the end of the file is
|
||||
@@ -263,59 +372,13 @@ fn decode_ifd(f: &File<'_>, dir: &Ifd, limits: &Limits) -> Result<Image, CodecEr
|
||||
let planar = f.tag_uint(dir, ifd::TAG_PLANAR) == Some(2);
|
||||
let extra = f.tag_uints(dir, ifd::TAG_EXTRA_SAMPLES, 64).unwrap_or_default();
|
||||
let tiled = dir.get(ifd::TAG_TILE_OFFSETS).is_some();
|
||||
let (cw, ch) = if tiled {
|
||||
let tw = need(ifd::TAG_TILE_WIDTH, "TileWidth")?;
|
||||
let tl = need(ifd::TAG_TILE_LENGTH, "TileLength")?;
|
||||
(tw, tl)
|
||||
} else {
|
||||
(u64::from(w), f.tag_uint(dir, ifd::TAG_ROWS_PER_STRIP).unwrap_or(u64::from(h)).min(u64::from(h)))
|
||||
};
|
||||
if cw == 0 || ch == 0 {
|
||||
return Err(err("zero tile or strip size"));
|
||||
}
|
||||
// The chunk tables must match the geometry. Checked here, in 64 bits, before the `tiff`
|
||||
// crate does the same arithmetic in narrower types.
|
||||
let planes_in_file = if planar { spp } else { 1 };
|
||||
let expected = u64::from(w)
|
||||
.div_ceil(cw)
|
||||
.checked_mul(u64::from(h).div_ceil(ch))
|
||||
.and_then(|n| n.checked_mul(planes_in_file))
|
||||
.filter(|&n| n <= u64::from(u32::MAX))
|
||||
.ok_or_else(|| err("too many strips or tiles"))?;
|
||||
let (offsets_tag, counts_tag) = if tiled { (324, 325) } else { (273, 279) };
|
||||
let counts = (dir.get(offsets_tag).map(|e| e.count), dir.get(counts_tag).map(|e| e.count));
|
||||
if counts != (Some(expected), Some(expected)) {
|
||||
return Err(err(format!("{expected} strips or tiles expected, the tables list {counts:?}")));
|
||||
}
|
||||
let src = match (format, bits) {
|
||||
(1, 1 | 2 | 4) => Src::Bits(bits as u8),
|
||||
(1, 8) => Src::U8,
|
||||
(1, 16) => Src::U16,
|
||||
(1, 32) => Src::U32,
|
||||
(1, 64) => Src::U64,
|
||||
(3, 16) => Src::F16,
|
||||
(3, 32) => Src::F32,
|
||||
(3, 64) => Src::F64,
|
||||
(2, _) => return Err(CodecError::unsupported(F, "signed integer samples")),
|
||||
_ => return Err(CodecError::unsupported(F, format!("{bits}-bit samples (sample format {format})"))),
|
||||
};
|
||||
|
||||
let (cw, ch) = check_chunk_tables(f, dir, (w, h), spp, planar, tiled)?;
|
||||
let src = sample_src(format, bits)?;
|
||||
// WhiteIsZero and palette images are handed to the `tiff` crate as BlackIsZero: it then
|
||||
// returns the stored samples, and the interpretation is applied here (for every depth and
|
||||
// with alpha, which the crate's own inversion does not cover).
|
||||
let invert = photometric == 0;
|
||||
let palette = if photometric == 3 {
|
||||
if spp != 1 || !matches!(src, Src::Bits(_) | Src::U8 | Src::U16) {
|
||||
return Err(CodecError::unsupported(F, format!("palette image with {spp} samples of {bits} bits")));
|
||||
}
|
||||
let n = 1u64 << bits;
|
||||
let map = dir.get(ifd::TAG_COLOR_MAP).filter(|e| e.ty == 3 && e.count == 3 * n).ok_or_else(|| err("palette image without a valid ColorMap"))?;
|
||||
let at = |i: u64| f.uint(map, i).and_then(|v| u16::try_from(v).ok());
|
||||
let table: Option<Vec<[u16; 3]>> = (0..n).map(|i| Some([at(i)?, at(n + i)?, at(2 * n + i)?])).collect();
|
||||
Some(table.ok_or_else(|| err("palette ColorMap cut off"))?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let palette = if photometric == 3 { Some(read_palette(f, dir, spp, bits, src)?) } else { None };
|
||||
// CIE L*a*b*: CIELab (8; a* and b* signed) and ICCLab (9; a* and b* offset) are handed to the
|
||||
// `tiff` crate as RGB, then labelled Lab with a* and b* in PhotoCraft's encoding
|
||||
// (`convert_ab`).
|
||||
@@ -337,29 +400,7 @@ fn decode_ifd(f: &File<'_>, dir: &Ifd, limits: &Limits) -> Result<Image, CodecEr
|
||||
// asked (photometric patched to black-is-zero below); gray already comes back as multiband.
|
||||
let unused = !extra.is_empty() && (spp as usize) > color.saturating_add(usize::from(alpha_at.is_some()));
|
||||
let all_samples = palette.is_none() && matches!(photometric, 2 | 5 | 8 | 9) && (alpha_at.is_some_and(|a| a > 0) || (!planar && unused));
|
||||
let mut patches: Vec<(u64, Vec<u8>)> = Vec::new();
|
||||
let encode_u = |v: u64, len: usize| -> Vec<u8> {
|
||||
let b = if f.le { v.to_le_bytes() } else { v.to_be_bytes() };
|
||||
if f.le { b.get(..len).map(<[u8]>::to_vec).unwrap_or_default() } else { b.get(8 - len..).map(<[u8]>::to_vec).unwrap_or_default() }
|
||||
};
|
||||
if f.big {
|
||||
patches.push((8, encode_u(dir.at, 8)));
|
||||
} else {
|
||||
let at = u32::try_from(dir.at).map_err(|_| err("directory offset out of range"))?;
|
||||
patches.push((4, encode_u(u64::from(at), 4)));
|
||||
}
|
||||
// The directory is seen as the last of its chain: the chain was walked already, and a
|
||||
// cycle through it must not fail this page.
|
||||
patches.push((dir.next_at, vec![0; if f.big { 8 } else { 4 }]));
|
||||
if invert || palette.is_some() || all_samples || lab {
|
||||
let e = photometric_entry.ok_or_else(|| err("missing PhotometricInterpretation"))?;
|
||||
let len = match e.ty {
|
||||
3 if e.count == 1 => 2,
|
||||
4 if e.count == 1 => 4,
|
||||
_ => return Err(err("malformed PhotometricInterpretation")),
|
||||
};
|
||||
patches.push((e.value_at, encode_u(if lab && !all_samples { 2 } else { 1 }, len)));
|
||||
}
|
||||
let patches = header_patches(f, dir, photometric_entry, invert || palette.is_some() || all_samples || lab, lab && !all_samples)?;
|
||||
|
||||
let tl = {
|
||||
let mut l = tiff::decoder::Limits::default();
|
||||
@@ -380,29 +421,7 @@ fn decode_ifd(f: &File<'_>, dir: &Ifd, limits: &Limits) -> Result<Image, CodecEr
|
||||
tiff::ColorType::Gray(_) => (ChannelLayout::Gray, 1),
|
||||
tiff::ColorType::Multiband { num_samples, .. } if all_samples => {
|
||||
// Lab reads as RGB here and is relabelled below.
|
||||
let layout = match photometric {
|
||||
2 | 8 | 9 => {
|
||||
if alpha_at.is_some() {
|
||||
ChannelLayout::Rgba
|
||||
} else {
|
||||
ChannelLayout::Rgb
|
||||
}
|
||||
}
|
||||
5 => {
|
||||
if alpha_at.is_some() {
|
||||
ChannelLayout::CmykA
|
||||
} else {
|
||||
ChannelLayout::Cmyk
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if gray_alpha {
|
||||
ChannelLayout::GrayA
|
||||
} else {
|
||||
ChannelLayout::Gray
|
||||
}
|
||||
}
|
||||
};
|
||||
let layout = multiband_layout(photometric, alpha_at.is_some(), gray_alpha);
|
||||
(layout, usize::from(num_samples))
|
||||
}
|
||||
tiff::ColorType::Multiband { num_samples, .. } if photometric <= 1 => {
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
use super::bands::convert_ab;
|
||||
use super::tags::{F, TAG_IMAGE_SOURCE_DATA, TAG_PHOTOSHOP, TAG_XMP, TEXT_TAGS};
|
||||
use super::*;
|
||||
use super::{
|
||||
ChannelLayout, CmykA16, CmykA32F, CodecError, Cow, EncodeOptions, ExifDirs, GrayA8, GrayA16, GrayA32F, Image, Lab8, Lab16, LabA8, LabA16,
|
||||
PhotometricInterpretation, Plan, Rational, ResolutionUnit, SampleFormat, SampleType, Tag, TiffColorType, TiffCompression, TiffEncoder, TiffKind,
|
||||
TiffKindBig, TiffKindStandard, TiffValue, Type, colortype, io, tiff_exif,
|
||||
};
|
||||
use std::io::Cursor;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Encoding
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::*;
|
||||
use super::{Format, Tag};
|
||||
|
||||
pub(super) const F: Format = Format::Tiff;
|
||||
|
||||
|
||||
@@ -19,7 +19,11 @@ mod transform;
|
||||
|
||||
use bool_encoder::BoolEncoder;
|
||||
use predict::{Plane, SubEdges, predict_block, predict_sub};
|
||||
use tables::*;
|
||||
use tables::{
|
||||
AC_QLOOKUP, B_DC_PRED, B_HE_PRED, B_PRED, B_TM_PRED, B_VE_PRED, BMODE_TREE, CAT_BASE, COEFF_BANDS, COEFF_TREE, COEFF_UPDATE_PROBS, DC_PRED, DC_QLOOKUP,
|
||||
DCT_0, DCT_1, DCT_2, DCT_3, DCT_4, DCT_CAT1, DCT_CAT2, DCT_CAT3, DCT_CAT4, DCT_CAT5, DCT_CAT6, DCT_EOB, DEFAULT_COEFF_PROBS, H_PRED, KF_BMODE_PROBS,
|
||||
KF_UV_MODE_PROB, KF_YMODE_PROB, KF_YMODE_TREE, PCAT, TM_PRED, UV_MODE_TREE, V_PRED, ZIGZAG,
|
||||
};
|
||||
use transform::{fdct, fwht, idct, iwht};
|
||||
|
||||
/// The largest dimension a VP8 frame header can hold (14 bits).
|
||||
@@ -353,6 +357,65 @@ impl<'a> Encoder<'a> {
|
||||
.sum()
|
||||
}
|
||||
|
||||
/// Chroma of one macroblock: one mode for both planes; returns it with the U and V blocks.
|
||||
fn encode_chroma(&mut self, mbx: usize, mby: usize) -> (u8, Vec<Block>, Vec<Block>) {
|
||||
let (cx0, cy0) = (mbx * 8, mby * 8);
|
||||
let mut src_u = [0i32; 64];
|
||||
let mut src_v = [0i32; 64];
|
||||
for r in 0..8 {
|
||||
for c in 0..8 {
|
||||
src_u[r * 8 + c] = self.src_c(&self.src.u, cx0 + c, cy0 + r);
|
||||
src_v[r * 8 + c] = self.src_c(&self.src.v, cx0 + c, cy0 + r);
|
||||
}
|
||||
}
|
||||
let mut uv_mode = DC_PRED;
|
||||
let mut uv_best = u64::MAX;
|
||||
let mut pred_u = [0u8; 64];
|
||||
let mut pred_v = [0u8; 64];
|
||||
let mut pu = [0u8; 64];
|
||||
let mut pv = [0u8; 64];
|
||||
for mode in [DC_PRED, V_PRED, H_PRED, TM_PRED] {
|
||||
predict_block(&self.rec_u, cx0, cy0, 8, mode, &mut pu);
|
||||
predict_block(&self.rec_v, cx0, cy0, 8, mode, &mut pv);
|
||||
let e = Self::sse(&pu, &src_u) + Self::sse(&pv, &src_v);
|
||||
if e < uv_best {
|
||||
uv_best = e;
|
||||
uv_mode = mode;
|
||||
pred_u = pu;
|
||||
pred_v = pv;
|
||||
}
|
||||
}
|
||||
let mut u_blocks = Vec::with_capacity(4);
|
||||
let mut v_blocks = Vec::with_capacity(4);
|
||||
for (plane_src, pred, blocks, is_u) in [(&src_u, &pred_u, &mut u_blocks, true), (&src_v, &pred_v, &mut v_blocks, false)] {
|
||||
for b in 0..4 {
|
||||
let mut residual = [0i32; 16];
|
||||
for r in 0..4 {
|
||||
for c in 0..4 {
|
||||
let i = ((b >> 1) * 4 + r) * 8 + (b & 1) * 4 + c;
|
||||
residual[r * 4 + c] = plane_src[i] - i32::from(pred[i]);
|
||||
}
|
||||
}
|
||||
let (quantized, dequantized) = Self::code_block(&residual, self.q.uv_dc, self.q.uv_ac, true);
|
||||
let recon = idct(&dequantized);
|
||||
let (bx, by) = (cx0 + (b & 1) * 4, cy0 + (b >> 1) * 4);
|
||||
for r in 0..4 {
|
||||
for c in 0..4 {
|
||||
let i = ((b >> 1) * 4 + r) * 8 + (b & 1) * 4 + c;
|
||||
let value = (i32::from(pred[i]) + recon[r * 4 + c]).clamp(0, 255) as u8;
|
||||
if is_u {
|
||||
self.rec_u.set(bx + c, by + r, value);
|
||||
} else {
|
||||
self.rec_v.set(bx + c, by + r, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
blocks.push(Block::from_raster(&quantized, 0));
|
||||
}
|
||||
}
|
||||
(uv_mode, u_blocks, v_blocks)
|
||||
}
|
||||
|
||||
// Sub-block indices address several arrays by geometry; ranges read better than iterators here.
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
fn encode_macroblock(&mut self, mbx: usize, mby: usize) {
|
||||
@@ -476,61 +539,7 @@ impl<'a> Encoder<'a> {
|
||||
(best_mode, Some(Block::from_raster(&y2_q, 0)), blocks)
|
||||
};
|
||||
|
||||
// Chroma: one mode for both planes.
|
||||
let (cx0, cy0) = (mbx * 8, mby * 8);
|
||||
let mut src_u = [0i32; 64];
|
||||
let mut src_v = [0i32; 64];
|
||||
for r in 0..8 {
|
||||
for c in 0..8 {
|
||||
src_u[r * 8 + c] = self.src_c(&self.src.u, cx0 + c, cy0 + r);
|
||||
src_v[r * 8 + c] = self.src_c(&self.src.v, cx0 + c, cy0 + r);
|
||||
}
|
||||
}
|
||||
let mut uv_mode = DC_PRED;
|
||||
let mut uv_best = u64::MAX;
|
||||
let mut pred_u = [0u8; 64];
|
||||
let mut pred_v = [0u8; 64];
|
||||
let mut pu = [0u8; 64];
|
||||
let mut pv = [0u8; 64];
|
||||
for mode in [DC_PRED, V_PRED, H_PRED, TM_PRED] {
|
||||
predict_block(&self.rec_u, cx0, cy0, 8, mode, &mut pu);
|
||||
predict_block(&self.rec_v, cx0, cy0, 8, mode, &mut pv);
|
||||
let e = Self::sse(&pu, &src_u) + Self::sse(&pv, &src_v);
|
||||
if e < uv_best {
|
||||
uv_best = e;
|
||||
uv_mode = mode;
|
||||
pred_u = pu;
|
||||
pred_v = pv;
|
||||
}
|
||||
}
|
||||
let mut u_blocks = Vec::with_capacity(4);
|
||||
let mut v_blocks = Vec::with_capacity(4);
|
||||
for (plane_src, pred, blocks, is_u) in [(&src_u, &pred_u, &mut u_blocks, true), (&src_v, &pred_v, &mut v_blocks, false)] {
|
||||
for b in 0..4 {
|
||||
let mut residual = [0i32; 16];
|
||||
for r in 0..4 {
|
||||
for c in 0..4 {
|
||||
let i = ((b >> 1) * 4 + r) * 8 + (b & 1) * 4 + c;
|
||||
residual[r * 4 + c] = plane_src[i] - i32::from(pred[i]);
|
||||
}
|
||||
}
|
||||
let (quantized, dequantized) = Self::code_block(&residual, self.q.uv_dc, self.q.uv_ac, true);
|
||||
let recon = idct(&dequantized);
|
||||
let (bx, by) = (cx0 + (b & 1) * 4, cy0 + (b >> 1) * 4);
|
||||
for r in 0..4 {
|
||||
for c in 0..4 {
|
||||
let i = ((b >> 1) * 4 + r) * 8 + (b & 1) * 4 + c;
|
||||
let value = (i32::from(pred[i]) + recon[r * 4 + c]).clamp(0, 255) as u8;
|
||||
if is_u {
|
||||
self.rec_u.set(bx + c, by + r, value);
|
||||
} else {
|
||||
self.rec_v.set(bx + c, by + r, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
blocks.push(Block::from_raster(&quantized, 0));
|
||||
}
|
||||
}
|
||||
let (uv_mode, u_blocks, v_blocks) = self.encode_chroma(mbx, mby);
|
||||
|
||||
let skip = y2.as_ref().is_none_or(|b| !b.has_coeffs())
|
||||
&& y_blocks.iter().all(|b| !b.has_coeffs())
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
//! sub-block modes. Predictions read the frame's *unfiltered* reconstruction, which is what the
|
||||
//! decoder predicts from too (the loop filter runs on the finished frame).
|
||||
|
||||
use super::tables::*;
|
||||
#[cfg(test)]
|
||||
use super::tables::{B_DC_PRED, DC_PRED};
|
||||
use super::tables::{B_HD_PRED, B_HE_PRED, B_HU_PRED, B_LD_PRED, B_RD_PRED, B_TM_PRED, B_VE_PRED, B_VL_PRED, B_VR_PRED, H_PRED, TM_PRED, V_PRED};
|
||||
|
||||
/// A plane of the reconstruction with the virtual border VP8 defines: the row above the frame
|
||||
/// reads as 127, the column left of it as 129, and the pixel above-left of the frame as 127.
|
||||
|
||||
Reference in new issue
Block a user