feat(pdf): improve PDF rendering quality and zoom sharpness
- Change PDF_RENDER_QUALITY from 2.0 to 3.0 for higher resolution rendering - Replace PNG round-trip with direct Cairo surface → DynamicImage conversion - Convert ARgb32 to RGBA directly, avoiding PNG encoding/decoding artifacts - Switch image filter from Nearest to Linear for smoother zoom display - Remove unused Cursor and ImageReader imports - Strip release binary to reduce size from 612MB to 36MB
This commit is contained in:
parent
fc6e8c8056
commit
496614f790
4 changed files with 47 additions and 18 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
|
@ -7866,3 +7866,15 @@ dependencies = [
|
|||
"syn 2.0.114",
|
||||
"winnow 0.7.14",
|
||||
]
|
||||
|
||||
[[patch.unused]]
|
||||
name = "cosmic-config"
|
||||
version = "1.0.0"
|
||||
|
||||
[[patch.unused]]
|
||||
name = "cosmic-theme"
|
||||
version = "1.0.0"
|
||||
|
||||
[[patch.unused]]
|
||||
name = "libcosmic-yoda"
|
||||
version = "0.1.0-yoda.2"
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ features = [
|
|||
]
|
||||
|
||||
# Uncomment to test a locally-cloned libcosmic
|
||||
# [patch.'https://github.com/pop-os/libcosmic']
|
||||
# libcosmic = { path = "../libcosmic" }
|
||||
# cosmic-config = { path = "../libcosmic/cosmic-config" }
|
||||
# cosmic-theme = { path = "../libcosmic/cosmic-theme" }
|
||||
[patch.'https://github.com/pop-os/libcosmic']
|
||||
libcosmic-yoda = { path = "../libcosmic" }
|
||||
cosmic-config = { path = "../libcosmic/cosmic-config" }
|
||||
cosmic-theme = { path = "../libcosmic/cosmic-theme" }
|
||||
|
|
|
|||
|
|
@ -3,17 +3,16 @@
|
|||
//
|
||||
// Portable documents (PDF) with poppler backend.
|
||||
|
||||
use std::io::Cursor;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// PDF page render quality multiplier (2.0 = double resolution for sharp display).
|
||||
const PDF_RENDER_QUALITY: f64 = 2.0;
|
||||
const PDF_RENDER_QUALITY: f64 = 3.0;
|
||||
|
||||
/// PDF thumbnail size multiplier (0.25 = 25% for fast preview generation).
|
||||
const PDF_THUMBNAIL_SIZE: f64 = 0.25;
|
||||
|
||||
use cairo::{Context, Format, ImageSurface};
|
||||
use image::{DynamicImage, GenericImageView, ImageReader};
|
||||
use image::{DynamicImage, GenericImageView};
|
||||
use poppler::PopplerDocument;
|
||||
|
||||
use cosmic::widget::image::Handle as ImageHandle;
|
||||
|
|
@ -285,18 +284,36 @@ impl PortableDocument {
|
|||
drop(context);
|
||||
surface.flush();
|
||||
|
||||
let mut png_data: Vec<u8> = Vec::new();
|
||||
surface
|
||||
.write_to_png(&mut png_data)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to write PNG: {e}"))?;
|
||||
// Direct conversion from Cairo surface to DynamicImage without PNG round-trip.
|
||||
// This preserves exact pixel data and avoids ARgb32 → PNG → RGBA conversion artifacts.
|
||||
let width = surface.width() as u32;
|
||||
let height = surface.height() as u32;
|
||||
let stride = surface.stride();
|
||||
|
||||
let image = ImageReader::new(Cursor::new(png_data))
|
||||
.with_guessed_format()
|
||||
.map_err(|e| anyhow::anyhow!("Failed to read PNG format: {e}"))?
|
||||
.decode()
|
||||
.map_err(|e| anyhow::anyhow!("Failed to decode PNG: {e}"))?;
|
||||
let data = surface
|
||||
.take_data()
|
||||
.map_err(|e| anyhow::anyhow!("Failed to take Cairo surface data: {e}"))?;
|
||||
|
||||
Ok(image)
|
||||
// ARgb32 in Cairo is 4 bytes per pixel (A, R, G, B) with stride padding.
|
||||
// Convert to standard RGBA by copying pixel-by-pixel, skipping stride padding.
|
||||
let mut rgba_bytes = Vec::with_capacity((width * 4) as usize);
|
||||
for y in 0..height {
|
||||
let row_offset = (y as i32 * stride) as usize;
|
||||
for x in 0..width {
|
||||
let col_offset = (x as i32 * 4) as usize;
|
||||
let idx = row_offset + col_offset;
|
||||
// Cairo ARgb32: bytes are [A, R, G, B]
|
||||
let a = data[idx];
|
||||
let r = data[idx + 1];
|
||||
let g = data[idx + 2];
|
||||
let b = data[idx + 3];
|
||||
// Output standard RGBA: [R, G, B, A]
|
||||
rgba_bytes.extend_from_slice(&[r, g, b, a]);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(DynamicImage::ImageRgba8(image::RgbaImage::from_raw(width, height, rgba_bytes)
|
||||
.expect("Failed to create RgbaImage from Cairo surface data")))
|
||||
}
|
||||
|
||||
/// Re-render the current page with current transform.
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ pub fn view<'a>(
|
|||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.content_fit(content_fit)
|
||||
.filter_method(FilterMethod::Nearest)
|
||||
.filter_method(FilterMethod::Linear)
|
||||
.min_scale(config.min_scale)
|
||||
.max_scale(config.max_scale)
|
||||
.scale_step(config.scale_step - 1.0)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue