Skip to content

Commit 3599a86

Browse files
committed
support jpeg
1 parent 201212c commit 3599a86

21 files changed

+84
-33
lines changed

assets/images/luma8.jpg

2.3 KB
Loading

assets/images/rgb8.jpg

4.85 KB
Loading
860 Bytes
Loading

assets/refs/image_luma8_jpg_mupdf.png

803 Bytes
Loading
803 Bytes
Loading
782 Bytes
Loading

assets/refs/image_luma8_jpg_pdfjs.png

928 Bytes
Loading
845 Bytes
Loading
988 Bytes
Loading
3.06 KB
Loading

assets/refs/image_rgb8_jpg_mupdf.png

3 KB
Loading

assets/refs/image_rgb8_jpg_pdfbox.png

3 KB
Loading

assets/refs/image_rgb8_jpg_pdfium.png

2.98 KB
Loading

assets/refs/image_rgb8_jpg_pdfjs.png

3.02 KB
Loading
3.11 KB
Loading

assets/refs/image_rgb8_jpg_quartz.png

3.34 KB
Loading

src/object/image.rs

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,24 @@ enum ImageColorspace {
3434
Cmyk,
3535
}
3636

37+
impl TryFrom<ColorSpace> for ImageColorspace {
38+
type Error = ();
39+
40+
fn try_from(value: ColorSpace) -> Result<Self, Self::Error> {
41+
match value {
42+
ColorSpace::RGB => Ok(ImageColorspace::Rgb),
43+
ColorSpace::RGBA => Ok(ImageColorspace::Rgb),
44+
ColorSpace::Luma => Ok(ImageColorspace::Luma),
45+
ColorSpace::LumaA => Ok(ImageColorspace::Luma),
46+
_ => Err(()),
47+
}
48+
}
49+
}
50+
3751
#[derive(Debug, Hash, Eq, PartialEq)]
3852
pub struct Repr {
3953
image_data: Vec<u8>,
54+
is_dct_encoded: bool,
4055
size: SizeWrapper,
4156
mask_data: Option<Vec<u8>>,
4257
bits_per_component: BitsPerComponent,
@@ -46,22 +61,13 @@ pub struct Repr {
4661
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
4762
pub struct Image(Arc<Prehashed<Repr>>);
4863

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

5769
let color_space = decoder.get_colorspace()?;
58-
let image_color_space = match color_space {
59-
ColorSpace::RGB => ImageColorspace::Rgb,
60-
ColorSpace::RGBA => ImageColorspace::Rgb,
61-
ColorSpace::Luma => ImageColorspace::Luma,
62-
ColorSpace::LumaA => ImageColorspace::Luma,
63-
_ => return None,
64-
};
70+
let image_color_space = color_space.try_into().ok()?;
6571

6672
let size = {
6773
let info = decoder.get_info()?;
@@ -78,20 +84,31 @@ impl Image {
7884
Some(Self(Arc::new(Prehashed::new(Repr {
7985
image_data,
8086
mask_data,
87+
is_dct_encoded: false,
8188
bits_per_component,
8289
image_color_space,
8390
size: SizeWrapper(size),
8491
}))))
8592
}
8693

87-
pub fn from_jpeg(data: &[u8]) {
94+
pub fn from_jpeg(data: &[u8]) -> Option<Self> {
8895
let mut decoder = JpegDecoder::new(data);
96+
decoder.decode_headers().ok()?;
8997
let size = {
90-
let dimensions = decoder.dimensions().unwrap();
91-
Size::from_wh(dimensions.0 as f32, dimensions.1 as f32).unwrap()
98+
let dimensions = decoder.dimensions()?;
99+
Size::from_wh(dimensions.0 as f32, dimensions.1 as f32)?
92100
};
93-
let color_space = decoder.get_output_colorspace().unwrap();
94-
let headers = decoder.decode().unwrap();
101+
let color_space = decoder.get_output_colorspace()?;
102+
let image_color_space = color_space.try_into().ok()?;
103+
104+
Some(Self(Arc::new(Prehashed::new(Repr {
105+
image_data: data.to_vec(),
106+
mask_data: None,
107+
is_dct_encoded: true,
108+
bits_per_component: BitsPerComponent::Eight,
109+
image_color_space,
110+
size: SizeWrapper(size),
111+
}))))
95112
}
96113

97114
pub fn size(&self) -> Size {
@@ -123,8 +140,11 @@ impl Object for Image {
123140
soft_mask_id
124141
});
125142

126-
let image_stream =
127-
FilterStream::new_from_binary_data(&self.0.image_data, &sc.serialize_settings);
143+
let image_stream = if self.0.is_dct_encoded {
144+
FilterStream::new_from_dct_encoded(&self.0.image_data, &sc.serialize_settings)
145+
} else {
146+
FilterStream::new_from_binary_data(&self.0.image_data, &sc.serialize_settings)
147+
};
128148

129149
let mut image_x_object = chunk.image_xobject(root_ref, &image_stream.encoded_data());
130150
image_stream.write_filters(image_x_object.deref_mut().deref_mut());

src/serialize.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,13 +484,15 @@ impl FontContainer {
484484
pub enum StreamFilter {
485485
FlateDecode,
486486
AsciiHexDecode,
487+
DctDecode,
487488
}
488489

489490
impl StreamFilter {
490491
pub(crate) fn to_name(self) -> Name<'static> {
491492
match self {
492493
Self::AsciiHexDecode => Name(b"ASCIIHexDecode"),
493494
Self::FlateDecode => Name(b"FlateDecode"),
495+
Self::DctDecode => Name(b"DCTDecode"),
494496
}
495497
}
496498
}
@@ -500,6 +502,7 @@ impl StreamFilter {
500502
match self {
501503
StreamFilter::FlateDecode => deflate_encode(content),
502504
StreamFilter::AsciiHexDecode => hex_encode(content),
505+
Self::DctDecode => unreachable!(),
503506
}
504507
}
505508
}
@@ -553,6 +556,19 @@ impl<'a> FilterStream<'a> {
553556
filter_stream
554557
}
555558

559+
pub fn new_from_dct_encoded(content: &'a [u8], serialize_settings: &SerializeSettings) -> Self {
560+
let mut filter_stream = Self {
561+
content: Cow::Borrowed(content),
562+
filters: StreamFilters::Single(StreamFilter::DctDecode),
563+
};
564+
565+
if serialize_settings.ascii_compatible {
566+
filter_stream.add_filter(StreamFilter::AsciiHexDecode);
567+
}
568+
569+
filter_stream
570+
}
571+
556572
pub fn new_from_binary_data(content: &'a [u8], serialize_settings: &SerializeSettings) -> Self {
557573
let mut filter_stream = Self::empty(content);
558574
filter_stream.add_filter(StreamFilter::FlateDecode);

src/tests/manual.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@ use crate::font::Font;
66
use crate::rgb::Rgb;
77
use crate::serialize::SerializeSettings;
88
use crate::stream::Glyph;
9-
use crate::tests::{
10-
write_manual_to_store, ASSETS_PATH, COLR_TEST_GLYPHS, DEJAVU_SANS_MONO, NOTO_SANS,
11-
NOTO_SANS_ARABIC, NOTO_SANS_CJK, NOTO_SANS_DEVANAGARI,
12-
};
9+
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};
1310
use crate::util::SliceExt;
1411
use crate::Fill;
1512
use skrifa::instance::{Location, LocationRef, Size};

src/tests/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,14 @@ pub fn rect_to_path(x1: f32, y1: f32, x2: f32, y2: f32) -> Path {
8484
builder.finish().unwrap()
8585
}
8686

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

91+
pub fn load_jpg_image(name: &str) -> Image {
92+
Image::from_jpeg(&std::fs::read(ASSETS_PATH.join("images").join(name)).unwrap()).unwrap()
93+
}
94+
9195
fn write_snapshot_to_store(name: &str, content: &[u8]) {
9296
let mut path = STORE_PATH.clone().join("snapshots");
9397
let _ = std::fs::create_dir_all(&path);

src/tests/visreg.rs

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ use crate::rgb::Rgb;
33
use crate::stream::Glyph;
44
use crate::surface::Surface;
55
use crate::tests::manual::all_glyphs_to_pdf;
6-
use crate::tests::{
7-
load_image, COLR_TEST_GLYPHS, NOTO_COLOR_EMOJI, NOTO_SANS, TWITTER_COLOR_EMOJI,
8-
};
6+
use crate::tests::{load_png_image, COLR_TEST_GLYPHS, NOTO_COLOR_EMOJI, NOTO_SANS, TWITTER_COLOR_EMOJI, load_jpg_image};
97
use crate::util::SliceExt;
108
use crate::{rgb, Fill, LinearGradient, Paint, SpreadMethod, Stop};
119
use cosmic_text::{Attrs, Buffer, FontSystem, Metrics, Shaping};
@@ -140,38 +138,54 @@ fn twitter_color_emoji(document: &mut Document) {
140138
all_glyphs_to_pdf(font_data, None, document);
141139
}
142140

143-
fn image_impl(surface: &mut Surface, name: &str) {
144-
let image = load_image(name);
141+
fn png_image_impl(surface: &mut Surface, name: &str) {
142+
let image = load_png_image(name);
143+
let size = image.size();
144+
surface.draw_image(image, size);
145+
}
146+
147+
fn jpg_image_impl(surface: &mut Surface, name: &str) {
148+
let image = load_jpg_image(name);
145149
let size = image.size();
146150
surface.draw_image(image, size);
147151
}
148152

149153
#[visreg(all)]
150154
fn image_luma8_png(surface: &mut Surface) {
151-
image_impl(surface, "luma8.png");
155+
png_image_impl(surface, "luma8.png");
152156
}
153157

154158
#[visreg(all)]
155159
fn image_luma16_png(surface: &mut Surface) {
156-
image_impl(surface, "luma16.png");
160+
png_image_impl(surface, "luma16.png");
157161
}
158162

159163
#[visreg(all)]
160164
fn image_rgb8_png(surface: &mut Surface) {
161-
image_impl(surface, "rgb8.png");
165+
png_image_impl(surface, "rgb8.png");
162166
}
163167

164168
#[visreg(all)]
165169
fn image_rgb16_png(surface: &mut Surface) {
166-
image_impl(surface, "rgb16.png");
170+
png_image_impl(surface, "rgb16.png");
167171
}
168172

169173
#[visreg(all)]
170174
fn image_rgba8_png(surface: &mut Surface) {
171-
image_impl(surface, "rgba8.png");
175+
png_image_impl(surface, "rgba8.png");
172176
}
173177

174178
#[visreg(all)]
175179
fn image_rgba16_png(surface: &mut Surface) {
176-
image_impl(surface, "rgba16.png");
180+
png_image_impl(surface, "rgba16.png");
181+
}
182+
183+
#[visreg(all)]
184+
fn image_luma8_jpg(surface: &mut Surface) {
185+
jpg_image_impl(surface, "luma8.jpg");
186+
}
187+
188+
#[visreg(all)]
189+
fn image_rgb8_jpg(surface: &mut Surface) {
190+
jpg_image_impl(surface, "rgb8.jpg");
177191
}

0 commit comments

Comments
 (0)