j2k-jpeg 0.6.1

JPEG inspect/decode and fallback encode support for j2k
Documentation
// SPDX-License-Identifier: Apache-2.0

use crate::color::upsample::h2v2_fancy_sample;
use crate::color::ycbcr::ycbcr_to_rgb;

pub(crate) fn fill_rgb_row_from_gray(gray_row: &[u8], dst: &mut [u8]) {
    for (&gray, pixel) in gray_row.iter().zip(dst.chunks_exact_mut(3)) {
        pixel[0] = gray;
        pixel[1] = gray;
        pixel[2] = gray;
    }
}

pub(crate) fn fill_rgb_row_from_rgb(r_row: &[u8], g_row: &[u8], b_row: &[u8], dst: &mut [u8]) {
    for (((&r, &g), &b), pixel) in r_row
        .iter()
        .zip(g_row.iter())
        .zip(b_row.iter())
        .zip(dst.chunks_exact_mut(3))
    {
        pixel[0] = r;
        pixel[1] = g;
        pixel[2] = b;
    }
}

pub(crate) fn fill_rgb_row_from_ycbcr(y_row: &[u8], cb_row: &[u8], cr_row: &[u8], dst: &mut [u8]) {
    for (((&y_sample, &cb_sample), &cr_sample), pixel) in y_row
        .iter()
        .zip(cb_row.iter())
        .zip(cr_row.iter())
        .zip(dst.chunks_exact_mut(3))
    {
        let (r, g, b) = ycbcr_to_rgb(y_sample, cb_sample, cr_sample);
        pixel[0] = r;
        pixel[1] = g;
        pixel[2] = b;
    }
}

pub(crate) fn fill_rgba_row_from_gray(gray_row: &[u8], dst: &mut [u8], alpha: u8) {
    for (&gray, pixel) in gray_row.iter().zip(dst.chunks_exact_mut(4)) {
        write_rgba_pixel(pixel, gray, gray, gray, alpha);
    }
}

pub(crate) fn fill_rgba_row_from_rgb(
    r_row: &[u8],
    g_row: &[u8],
    b_row: &[u8],
    dst: &mut [u8],
    alpha: u8,
) {
    for (((&r, &g), &b), pixel) in r_row
        .iter()
        .zip(g_row.iter())
        .zip(b_row.iter())
        .zip(dst.chunks_exact_mut(4))
    {
        write_rgba_pixel(pixel, r, g, b, alpha);
    }
}

pub(crate) fn fill_rgba_row_from_ycbcr(
    y_row: &[u8],
    cb_row: &[u8],
    cr_row: &[u8],
    dst: &mut [u8],
    alpha: u8,
) {
    for (((&y_sample, &cb_sample), &cr_sample), pixel) in y_row
        .iter()
        .zip(cb_row.iter())
        .zip(cr_row.iter())
        .zip(dst.chunks_exact_mut(4))
    {
        let (r, g, b) = ycbcr_to_rgb(y_sample, cb_sample, cr_sample);
        write_rgba_pixel(pixel, r, g, b, alpha);
    }
}

fn write_rgba_pixel(pixel: &mut [u8], r: u8, g: u8, b: u8, alpha: u8) {
    pixel[0] = r;
    pixel[1] = g;
    pixel[2] = b;
    pixel[3] = alpha;
}

#[allow(clippy::too_many_arguments)]
pub(crate) fn fill_rgb_row_pair_from_420(
    y_top: &[u8],
    y_bottom: Option<&[u8]>,
    prev_cb: &[u8],
    curr_cb: &[u8],
    next_cb: &[u8],
    prev_cr: &[u8],
    curr_cr: &[u8],
    next_cr: &[u8],
    dst_top: &mut [u8],
    dst_bottom: Option<&mut [u8]>,
) {
    let width = y_top.len();
    debug_assert_eq!(width * 3, dst_top.len());
    debug_assert!(y_bottom.is_none_or(|row| row.len() == width));
    debug_assert!(dst_bottom.as_ref().is_none_or(|row| row.len() == width * 3));

    for (x, pixel) in dst_top.chunks_exact_mut(3).enumerate() {
        let cb = h2v2_fancy_sample(prev_cb, curr_cb, x);
        let cr = h2v2_fancy_sample(prev_cr, curr_cr, x);
        let (r, g, b) = ycbcr_to_rgb(y_top[x], cb, cr);
        pixel[0] = r;
        pixel[1] = g;
        pixel[2] = b;
    }

    if let (Some(y_bottom), Some(dst_bottom)) = (y_bottom, dst_bottom) {
        for (x, pixel) in dst_bottom.chunks_exact_mut(3).enumerate() {
            let cb = h2v2_fancy_sample(next_cb, curr_cb, x);
            let cr = h2v2_fancy_sample(next_cr, curr_cr, x);
            let (r, g, b) = ycbcr_to_rgb(y_bottom[x], cb, cr);
            pixel[0] = r;
            pixel[1] = g;
            pixel[2] = b;
        }
    }
}

#[allow(clippy::too_many_arguments)]
pub(crate) fn fill_rgb_row_pair_from_420_cropped(
    y_top: &[u8],
    y_bottom: Option<&[u8]>,
    prev_cb: &[u8],
    curr_cb: &[u8],
    next_cb: &[u8],
    prev_cr: &[u8],
    curr_cr: &[u8],
    next_cr: &[u8],
    crop_start: usize,
    crop_width: usize,
    dst_top: &mut [u8],
    dst_bottom: Option<&mut [u8]>,
) {
    let crop_end = crop_start + crop_width;
    debug_assert!(crop_end <= y_top.len());
    debug_assert_eq!(crop_width * 3, dst_top.len());
    debug_assert!(y_bottom.is_none_or(|row| row.len() == y_top.len()));
    debug_assert!(dst_bottom
        .as_ref()
        .is_none_or(|row| row.len() == crop_width * 3));
    debug_assert_eq!(prev_cb.len(), curr_cb.len());
    debug_assert_eq!(prev_cb.len(), next_cb.len());
    debug_assert_eq!(prev_cr.len(), curr_cr.len());
    debug_assert_eq!(prev_cr.len(), next_cr.len());

    for (local_x, pixel) in dst_top.chunks_exact_mut(3).enumerate() {
        let x = crop_start + local_x;
        let cb = h2v2_fancy_sample(prev_cb, curr_cb, x);
        let cr = h2v2_fancy_sample(prev_cr, curr_cr, x);
        let (r, g, b) = ycbcr_to_rgb(y_top[x], cb, cr);
        pixel[0] = r;
        pixel[1] = g;
        pixel[2] = b;
    }

    if let (Some(y_bottom), Some(dst_bottom)) = (y_bottom, dst_bottom) {
        for (local_x, pixel) in dst_bottom.chunks_exact_mut(3).enumerate() {
            let x = crop_start + local_x;
            let cb = h2v2_fancy_sample(next_cb, curr_cb, x);
            let cr = h2v2_fancy_sample(next_cr, curr_cr, x);
            let (r, g, b) = ycbcr_to_rgb(y_bottom[x], cb, cr);
            pixel[0] = r;
            pixel[1] = g;
            pixel[2] = b;
        }
    }
}