Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Faster color conversions #157

Merged
merged 4 commits into from
Jun 27, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 36 additions & 31 deletions src/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,7 @@ fn compute_image_parallel(components: &[Component],
.enumerate()
.for_each(|(row, line)| {
upsampler.upsample_and_interleave_row(&data, row, output_size.width as usize, line);
color_convert_func(line, output_size.width as usize);
color_convert_func(line);
});

Ok(image)
Expand All @@ -861,7 +861,7 @@ fn compute_image_parallel(components: &[Component],
for (row, line) in image.chunks_mut(line_size)
.enumerate() {
upsampler.upsample_and_interleave_row(&data, row, output_size.width as usize, line);
color_convert_func(line, output_size.width as usize);
color_convert_func(line);
}

Ok(image)
Expand All @@ -870,7 +870,7 @@ fn compute_image_parallel(components: &[Component],
fn choose_color_convert_func(component_count: usize,
_is_jfif: bool,
color_transform: Option<AdobeColorTransform>)
-> Result<fn(&mut [u8], usize)> {
-> Result<fn(&mut [u8])> {
match component_count {
3 => {
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
Expand All @@ -894,37 +894,36 @@ fn choose_color_convert_func(component_count: usize,
}
}

fn color_convert_line_null(_data: &mut [u8], _width: usize) {
fn color_convert_line_null(_data: &mut [u8]) {
}

fn color_convert_line_ycbcr(data: &mut [u8], width: usize) {
for i in 0 .. width {
let (r, g, b) = ycbcr_to_rgb(data[i * 3], data[i * 3 + 1], data[i * 3 + 2]);

data[i * 3] = r;
data[i * 3 + 1] = g;
data[i * 3 + 2] = b;
fn color_convert_line_ycbcr(data: &mut [u8]) {
for chunk in data.chunks_exact_mut(3) {
let (r, g, b) = ycbcr_to_rgb(chunk[0], chunk[1], chunk[2]);
chunk[0] = r;
chunk[1] = g;
chunk[2] = b;
}
}

fn color_convert_line_ycck(data: &mut [u8], width: usize) {
for i in 0 .. width {
let (r, g, b) = ycbcr_to_rgb(data[i * 4], data[i * 4 + 1], data[i * 4 + 2]);
let k = data[i * 4 + 3];
fn color_convert_line_ycck(data: &mut [u8]) {
for chunk in data.chunks_exact_mut(4) {
let (r, g, b) = ycbcr_to_rgb(chunk[0], chunk[1], chunk[2]);
let k = chunk[3];
chunk[0] = r;
chunk[1] = g;
chunk[2] = b;
chunk[3] = 255 - k;

data[i * 4] = r;
data[i * 4 + 1] = g;
data[i * 4 + 2] = b;
data[i * 4 + 3] = 255 - k;
}
}

fn color_convert_line_cmyk(data: &mut [u8], width: usize) {
for i in 0 .. width {
data[i * 4] = 255 - data[i * 4];
data[i * 4 + 1] = 255 - data[i * 4 + 1];
data[i * 4 + 2] = 255 - data[i * 4 + 2];
data[i * 4 + 3] = 255 - data[i * 4 + 3];
fn color_convert_line_cmyk(data: &mut [u8]) {
for chunk in data.chunks_exact_mut(4) {
chunk[0] = 255 - chunk[0];
chunk[1] = 255 - chunk[1];
chunk[2] = 255 - chunk[2];
chunk[3] = 255 - chunk[3];
}
}

Expand All @@ -938,13 +937,19 @@ fn ycbcr_to_rgb(y: u8, cb: u8, cr: u8) -> (u8, u8, u8) {
let g = y - 0.34414 * cb - 0.71414 * cr;
let b = y + 1.77200 * cb;

(clamp((r + 0.5) as i32, 0, 255) as u8,
clamp((g + 0.5) as i32, 0, 255) as u8,
clamp((b + 0.5) as i32, 0, 255) as u8)
// TODO: Rust has defined float-to-int conversion as saturating,
// which is exactly what we need here. However, as of this writing
// it still hasn't reached the stable channel.
// This can be simplified to `(r + 0.5) as u8` without any clamping
// as soon as our MSRV reaches the version that has saturating casts.
// The version without explicit clamping is also noticeably faster.
(clamp_to_u8((r + 0.5) as i32) as u8,
clamp_to_u8((g + 0.5) as i32) as u8,
clamp_to_u8((b + 0.5) as i32) as u8)
}

fn clamp<T: PartialOrd>(value: T, min: T, max: T) -> T {
if value < min { return min; }
if value > max { return max; }
fn clamp_to_u8(value: i32) -> i32 {
let value = std::cmp::max(value, 0);
let value = std::cmp::min(value, 255);
value
}