forked from 1b-agent/opencadstudio-upstream
DWF/DGN underlays: draw content as vector wires
A DWF sheet or DGN model is drawn as world-space wires (one line wire per colour and width, one fill wire per colour) instead of a raster rescaled to the view, so it stays sharp while zooming instead of showing pixels until the raster is made again. Colours go through the colour table mapping, contrast, fade and monochrome; clip boundaries clip the wires. The wires are drawn and plotted but neither picked nor snapped; the underlay's own snap geometry still serves object snaps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
be023142de
commit
507a6ab43c
8 files changed
+310
-100
No files matched your search
@@ -99,6 +99,59 @@ pub struct ImageModel {
|
||||
pub use_alpha: bool,
|
||||
}
|
||||
|
||||
/// The colour adjustments an underlay's content is drawn with over
|
||||
/// the background: contrast, monochrome, the background adjustment and (DGN)
|
||||
/// the fade.
|
||||
pub(crate) fn underlay_adjust(
|
||||
u: &codec::entities::Underlay,
|
||||
def: &codec::entities::UnderlayDefinition,
|
||||
background: [f32; 4],
|
||||
) -> super::pdf_raster::PageAdjust {
|
||||
use codec::entities::{UnderlayDisplayFlags, UnderlayType};
|
||||
use super::pdf_raster::PageAdjust;
|
||||
let page = crate::entities::underlay::page_of(def);
|
||||
// Dark means an HSL lightness under one half: pure blue counts as
|
||||
// light, (0, 128, 0) as dark.
|
||||
let bg_max = background[0].max(background[1]).max(background[2]);
|
||||
let bg_min = background[0].min(background[1]).min(background[2]);
|
||||
let bg_lum = (bg_max + bg_min) / 2.0;
|
||||
let dgn = def.underlay_type == UnderlayType::Dgn;
|
||||
// A DGN model is faded into the background before its contrast is
|
||||
// applied (the reference's order), so it is drawn opaque.
|
||||
let bg_rgb = [0, 1, 2].map(|i| (background[i].clamp(0.0, 1.0) * 255.0).round() as u8);
|
||||
let fade_before_contrast = (dgn && u.fade > 0).then_some((bg_rgb, u.fade.min(100)));
|
||||
PageAdjust {
|
||||
contrast: u.contrast.min(100),
|
||||
// Measured: PDF 0.5; DWF 0.243, or 0.73 once its colours are
|
||||
// turned over for a dark background; a DGN model's from its
|
||||
// own colours, over any background.
|
||||
contrast_pivot: match def.underlay_type {
|
||||
UnderlayType::Pdf => 500,
|
||||
UnderlayType::Dwf
|
||||
if u.flags.contains(UnderlayDisplayFlags::ADJUST_FOR_BACKGROUND) && bg_lum < 0.5 =>
|
||||
{
|
||||
730
|
||||
}
|
||||
UnderlayType::Dwf => 243,
|
||||
UnderlayType::Dgn => super::underlay_vector::dgn_contrast_pivot(
|
||||
&def.file_path,
|
||||
page,
|
||||
&super::pdf_layers::hidden_layers(u),
|
||||
super::underlay_vector::Backdrop::of([background[0], background[1], background[2]]),
|
||||
fade_before_contrast,
|
||||
)
|
||||
.unwrap_or(404),
|
||||
},
|
||||
monochrome: u.flags.contains(UnderlayDisplayFlags::MONOCHROME),
|
||||
// A DGN model's colours follow the background through its
|
||||
// colour table, whatever this setting.
|
||||
adjust_for_background: u.flags.contains(UnderlayDisplayFlags::ADJUST_FOR_BACKGROUND)
|
||||
&& !dgn,
|
||||
dark_background: bg_lum < 0.5,
|
||||
fade_before_contrast,
|
||||
}
|
||||
}
|
||||
|
||||
impl ImageModel {
|
||||
/// Build an ImageModel from a DXF RasterImage entity.
|
||||
/// Returns `None` if the image file cannot be opened or decoded.
|
||||
@@ -197,7 +250,7 @@ impl ImageModel {
|
||||
world_per_pixel: Option<f64>,
|
||||
) -> Option<Self> {
|
||||
use codec::entities::{UnderlayDisplayFlags, UnderlayType};
|
||||
use super::pdf_raster::{self, PageAdjust};
|
||||
use super::pdf_raster;
|
||||
if !u.flags.contains(UnderlayDisplayFlags::ON) {
|
||||
return None;
|
||||
}
|
||||
@@ -229,74 +282,11 @@ impl ImageModel {
|
||||
};
|
||||
(source, raster)
|
||||
}
|
||||
kind => {
|
||||
let hidden = super::pdf_layers::hidden_layers(u);
|
||||
let backdrop = super::underlay_vector::Backdrop::of([background[0], background[1], background[2]]);
|
||||
// The adjusted-pixel memo keys on the source: one per set of
|
||||
// hidden layers and backdrop.
|
||||
let source = format!(
|
||||
"{}#{}~{}{}",
|
||||
def.file_path,
|
||||
hidden.join("|"),
|
||||
backdrop.max_channel,
|
||||
backdrop.light
|
||||
);
|
||||
let raster = super::underlay_vector::display_raster(
|
||||
kind,
|
||||
&def.file_path,
|
||||
page,
|
||||
&hidden,
|
||||
screen_side.unwrap_or(super::underlay_vector::RASTER_SIDE),
|
||||
backdrop,
|
||||
)?;
|
||||
(source, raster)
|
||||
}
|
||||
// DWF sheets and DGN models are drawn as vector wires
|
||||
// (`underlay_vector::display_wires`).
|
||||
_ => return None,
|
||||
};
|
||||
// Dark means an HSL lightness under one half: pure blue counts as
|
||||
// light, (0, 128, 0) as dark.
|
||||
let bg_max = background[0].max(background[1]).max(background[2]);
|
||||
let bg_min = background[0].min(background[1]).min(background[2]);
|
||||
let bg_lum = (bg_max + bg_min) / 2.0;
|
||||
let dgn = def.underlay_type == UnderlayType::Dgn;
|
||||
// A DGN model is faded into the background before its contrast is
|
||||
// applied (the reference's order), so it is drawn opaque.
|
||||
let bg_rgb = [0, 1, 2].map(|i| (background[i].clamp(0.0, 1.0) * 255.0).round() as u8);
|
||||
let fade_before_contrast = (dgn && u.fade > 0).then_some((bg_rgb, u.fade.min(100)));
|
||||
let pixels = pdf_raster::adjusted_pixels(
|
||||
&source,
|
||||
page,
|
||||
&raster,
|
||||
PageAdjust {
|
||||
contrast: u.contrast.min(100),
|
||||
// Measured: PDF 0.5; DWF 0.243, or 0.73 once its colours are
|
||||
// turned over for a dark background; a DGN model's from its
|
||||
// own colours, over any background.
|
||||
contrast_pivot: match def.underlay_type {
|
||||
UnderlayType::Pdf => 500,
|
||||
UnderlayType::Dwf
|
||||
if u.flags.contains(UnderlayDisplayFlags::ADJUST_FOR_BACKGROUND) && bg_lum < 0.5 =>
|
||||
{
|
||||
730
|
||||
}
|
||||
UnderlayType::Dwf => 243,
|
||||
UnderlayType::Dgn => super::underlay_vector::dgn_contrast_pivot(
|
||||
&def.file_path,
|
||||
page,
|
||||
&super::pdf_layers::hidden_layers(u),
|
||||
super::underlay_vector::Backdrop::of([background[0], background[1], background[2]]),
|
||||
fade_before_contrast,
|
||||
)
|
||||
.unwrap_or(404),
|
||||
},
|
||||
monochrome: u.flags.contains(UnderlayDisplayFlags::MONOCHROME),
|
||||
// A DGN model's colours follow the background through its
|
||||
// colour table (the raster above), whatever this setting.
|
||||
adjust_for_background: u.flags.contains(UnderlayDisplayFlags::ADJUST_FOR_BACKGROUND)
|
||||
&& !dgn,
|
||||
dark_background: bg_lum < 0.5,
|
||||
fade_before_contrast,
|
||||
},
|
||||
);
|
||||
let pixels = pdf_raster::adjusted_pixels(&source, page, &raster, underlay_adjust(u, def, background));
|
||||
|
||||
// The page rectangle in drawing units (1 unit per PDF inch, or per
|
||||
// DWF/DGN sheet unit), entity scale applied, from its lower-left
|
||||
@@ -347,7 +337,7 @@ impl ImageModel {
|
||||
pixels,
|
||||
width: raster.width,
|
||||
height: raster.height,
|
||||
opacity: if dgn { 1.0 } else { 1.0 - u.fade.min(100) as f32 / 100.0 },
|
||||
opacity: 1.0 - u.fade.min(100) as f32 / 100.0,
|
||||
corners,
|
||||
corners_low,
|
||||
draw_depth: 0.0,
|
||||
|
||||
Reference in new issue
Block a user