Skip to content

Commit

Permalink
support jpeg
Browse files Browse the repository at this point in the history
  • Loading branch information
LaurenzV committed Aug 30, 2024
1 parent 201212c commit 3599a86
Show file tree
Hide file tree
Showing 21 changed files with 84 additions and 33 deletions.
Binary file added assets/images/luma8.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/rgb8.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/refs/image_luma8_jpg_ghostscript.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/refs/image_luma8_jpg_mupdf.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/refs/image_luma8_jpg_pdfbox.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/refs/image_luma8_jpg_pdfium.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/refs/image_luma8_jpg_pdfjs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/refs/image_luma8_jpg_poppler.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/refs/image_luma8_jpg_quartz.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/refs/image_rgb8_jpg_ghostscript.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/refs/image_rgb8_jpg_mupdf.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/refs/image_rgb8_jpg_pdfbox.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/refs/image_rgb8_jpg_pdfium.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/refs/image_rgb8_jpg_pdfjs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/refs/image_rgb8_jpg_poppler.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/refs/image_rgb8_jpg_quartz.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 37 additions & 17 deletions src/object/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,24 @@ enum ImageColorspace {
Cmyk,
}

impl TryFrom<ColorSpace> for ImageColorspace {
type Error = ();

fn try_from(value: ColorSpace) -> Result<Self, Self::Error> {
match value {
ColorSpace::RGB => Ok(ImageColorspace::Rgb),
ColorSpace::RGBA => Ok(ImageColorspace::Rgb),
ColorSpace::Luma => Ok(ImageColorspace::Luma),
ColorSpace::LumaA => Ok(ImageColorspace::Luma),
_ => Err(()),
}
}
}

#[derive(Debug, Hash, Eq, PartialEq)]
pub struct Repr {
image_data: Vec<u8>,
is_dct_encoded: bool,
size: SizeWrapper,
mask_data: Option<Vec<u8>>,
bits_per_component: BitsPerComponent,
Expand All @@ -46,22 +61,13 @@ pub struct Repr {
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub struct Image(Arc<Prehashed<Repr>>);

// TODO: Improve this so:
// 1) Users are not forced to pass a dynamic image
// 2) Use the DCT decoder for JPEG images.
impl Image {
pub fn from_png(data: &[u8]) -> Option<Self> {
let mut decoder = PngDecoder::new(data);
decoder.decode_headers().ok()?;

let color_space = decoder.get_colorspace()?;
let image_color_space = match color_space {
ColorSpace::RGB => ImageColorspace::Rgb,
ColorSpace::RGBA => ImageColorspace::Rgb,
ColorSpace::Luma => ImageColorspace::Luma,
ColorSpace::LumaA => ImageColorspace::Luma,
_ => return None,
};
let image_color_space = color_space.try_into().ok()?;

let size = {
let info = decoder.get_info()?;
Expand All @@ -78,20 +84,31 @@ impl Image {
Some(Self(Arc::new(Prehashed::new(Repr {
image_data,
mask_data,
is_dct_encoded: false,
bits_per_component,
image_color_space,
size: SizeWrapper(size),
}))))
}

pub fn from_jpeg(data: &[u8]) {
pub fn from_jpeg(data: &[u8]) -> Option<Self> {
let mut decoder = JpegDecoder::new(data);
decoder.decode_headers().ok()?;
let size = {
let dimensions = decoder.dimensions().unwrap();
Size::from_wh(dimensions.0 as f32, dimensions.1 as f32).unwrap()
let dimensions = decoder.dimensions()?;
Size::from_wh(dimensions.0 as f32, dimensions.1 as f32)?
};
let color_space = decoder.get_output_colorspace().unwrap();
let headers = decoder.decode().unwrap();
let color_space = decoder.get_output_colorspace()?;
let image_color_space = color_space.try_into().ok()?;

Some(Self(Arc::new(Prehashed::new(Repr {
image_data: data.to_vec(),
mask_data: None,
is_dct_encoded: true,
bits_per_component: BitsPerComponent::Eight,
image_color_space,
size: SizeWrapper(size),
}))))
}

pub fn size(&self) -> Size {
Expand Down Expand Up @@ -123,8 +140,11 @@ impl Object for Image {
soft_mask_id
});

let image_stream =
FilterStream::new_from_binary_data(&self.0.image_data, &sc.serialize_settings);
let image_stream = if self.0.is_dct_encoded {
FilterStream::new_from_dct_encoded(&self.0.image_data, &sc.serialize_settings)
} else {
FilterStream::new_from_binary_data(&self.0.image_data, &sc.serialize_settings)
};

let mut image_x_object = chunk.image_xobject(root_ref, &image_stream.encoded_data());
image_stream.write_filters(image_x_object.deref_mut().deref_mut());
Expand Down
16 changes: 16 additions & 0 deletions src/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,13 +484,15 @@ impl FontContainer {
pub enum StreamFilter {
FlateDecode,
AsciiHexDecode,
DctDecode,
}

impl StreamFilter {
pub(crate) fn to_name(self) -> Name<'static> {
match self {
Self::AsciiHexDecode => Name(b"ASCIIHexDecode"),
Self::FlateDecode => Name(b"FlateDecode"),
Self::DctDecode => Name(b"DCTDecode"),
}
}
}
Expand All @@ -500,6 +502,7 @@ impl StreamFilter {
match self {
StreamFilter::FlateDecode => deflate_encode(content),
StreamFilter::AsciiHexDecode => hex_encode(content),
Self::DctDecode => unreachable!(),
}
}
}
Expand Down Expand Up @@ -553,6 +556,19 @@ impl<'a> FilterStream<'a> {
filter_stream
}

pub fn new_from_dct_encoded(content: &'a [u8], serialize_settings: &SerializeSettings) -> Self {
let mut filter_stream = Self {
content: Cow::Borrowed(content),
filters: StreamFilters::Single(StreamFilter::DctDecode),
};

if serialize_settings.ascii_compatible {
filter_stream.add_filter(StreamFilter::AsciiHexDecode);
}

filter_stream
}

pub fn new_from_binary_data(content: &'a [u8], serialize_settings: &SerializeSettings) -> Self {
let mut filter_stream = Self::empty(content);
filter_stream.add_filter(StreamFilter::FlateDecode);
Expand Down
5 changes: 1 addition & 4 deletions src/tests/manual.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ use crate::font::Font;
use crate::rgb::Rgb;
use crate::serialize::SerializeSettings;
use crate::stream::Glyph;
use crate::tests::{
write_manual_to_store, ASSETS_PATH, COLR_TEST_GLYPHS, DEJAVU_SANS_MONO, NOTO_SANS,
NOTO_SANS_ARABIC, NOTO_SANS_CJK, NOTO_SANS_DEVANAGARI,
};
use crate::tests::{write_manual_to_store, ASSETS_PATH, COLR_TEST_GLYPHS, DEJAVU_SANS_MONO, NOTO_SANS, NOTO_SANS_ARABIC, NOTO_SANS_CJK, NOTO_SANS_DEVANAGARI, load_jpg_image};
use crate::util::SliceExt;
use crate::Fill;
use skrifa::instance::{Location, LocationRef, Size};
Expand Down
6 changes: 5 additions & 1 deletion src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,14 @@ pub fn rect_to_path(x1: f32, y1: f32, x2: f32, y2: f32) -> Path {
builder.finish().unwrap()
}

pub fn load_image(name: &str) -> Image {
pub fn load_png_image(name: &str) -> Image {
Image::from_png(&std::fs::read(ASSETS_PATH.join("images").join(name)).unwrap()).unwrap()
}

pub fn load_jpg_image(name: &str) -> Image {
Image::from_jpeg(&std::fs::read(ASSETS_PATH.join("images").join(name)).unwrap()).unwrap()
}

fn write_snapshot_to_store(name: &str, content: &[u8]) {
let mut path = STORE_PATH.clone().join("snapshots");
let _ = std::fs::create_dir_all(&path);
Expand Down
36 changes: 25 additions & 11 deletions src/tests/visreg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ use crate::rgb::Rgb;
use crate::stream::Glyph;
use crate::surface::Surface;
use crate::tests::manual::all_glyphs_to_pdf;
use crate::tests::{
load_image, COLR_TEST_GLYPHS, NOTO_COLOR_EMOJI, NOTO_SANS, TWITTER_COLOR_EMOJI,
};
use crate::tests::{load_png_image, COLR_TEST_GLYPHS, NOTO_COLOR_EMOJI, NOTO_SANS, TWITTER_COLOR_EMOJI, load_jpg_image};
use crate::util::SliceExt;
use crate::{rgb, Fill, LinearGradient, Paint, SpreadMethod, Stop};
use cosmic_text::{Attrs, Buffer, FontSystem, Metrics, Shaping};
Expand Down Expand Up @@ -140,38 +138,54 @@ fn twitter_color_emoji(document: &mut Document) {
all_glyphs_to_pdf(font_data, None, document);
}

fn image_impl(surface: &mut Surface, name: &str) {
let image = load_image(name);
fn png_image_impl(surface: &mut Surface, name: &str) {
let image = load_png_image(name);
let size = image.size();
surface.draw_image(image, size);
}

fn jpg_image_impl(surface: &mut Surface, name: &str) {
let image = load_jpg_image(name);
let size = image.size();
surface.draw_image(image, size);
}

#[visreg(all)]
fn image_luma8_png(surface: &mut Surface) {
image_impl(surface, "luma8.png");
png_image_impl(surface, "luma8.png");
}

#[visreg(all)]
fn image_luma16_png(surface: &mut Surface) {
image_impl(surface, "luma16.png");
png_image_impl(surface, "luma16.png");
}

#[visreg(all)]
fn image_rgb8_png(surface: &mut Surface) {
image_impl(surface, "rgb8.png");
png_image_impl(surface, "rgb8.png");
}

#[visreg(all)]
fn image_rgb16_png(surface: &mut Surface) {
image_impl(surface, "rgb16.png");
png_image_impl(surface, "rgb16.png");
}

#[visreg(all)]
fn image_rgba8_png(surface: &mut Surface) {
image_impl(surface, "rgba8.png");
png_image_impl(surface, "rgba8.png");
}

#[visreg(all)]
fn image_rgba16_png(surface: &mut Surface) {
image_impl(surface, "rgba16.png");
png_image_impl(surface, "rgba16.png");
}

#[visreg(all)]
fn image_luma8_jpg(surface: &mut Surface) {
jpg_image_impl(surface, "luma8.jpg");
}

#[visreg(all)]
fn image_rgb8_jpg(surface: &mut Surface) {
jpg_image_impl(surface, "rgb8.jpg");
}

0 comments on commit 3599a86

Please sign in to comment.