Merge pull request #1513 from ramox81/codex/image-transparency

Raster images: draw opaque when transparency is off; TRANSPARENCY command
This commit is contained in:
Hakan Seven committed 2026-09-28 13:48:51 +03:00
commit 6c481c92fe
30 files changed
+242 -6

No files matched your search

+3
View File
@@ -3803,6 +3803,9 @@ insert =
.xref-old-path-prefix = بادئة المسار القديم
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3790,6 +3790,9 @@ insert =
.xref-old-path-prefix = стар префикс на път
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3788,6 +3788,9 @@ insert =
.xref-old-path-prefix = stará předpona cesty
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3789,6 +3789,9 @@ insert =
.xref-old-path-prefix = alte Pfadpräfix
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3801,6 +3801,9 @@ insert =
.xref-old-path-prefix = old path prefix
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3802,6 +3802,9 @@ insert =
.xref-old-path-prefix = old path prefix
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3791,6 +3791,9 @@ insert =
.xref-old-path-prefix = prefijo de ruta antiguo
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3790,6 +3790,9 @@ insert =
.xref-old-path-prefix = vanha polun etuliite
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3791,6 +3791,9 @@ insert =
.xref-old-path-prefix = ancien préfixe de chemin
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3779,6 +3779,9 @@ insert =
.xref-old-path-prefix = पुराना पथ उपसर्ग
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3790,6 +3790,9 @@ insert =
.xref-old-path-prefix = régi útvonal-előtag
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3790,6 +3790,9 @@ insert =
.xref-old-path-prefix = vecchio prefisso percorso
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3779,6 +3779,9 @@ insert =
.xref-old-path-prefix = 古いパスプレフィックス
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3790,6 +3790,9 @@ insert =
.xref-old-path-prefix = 이전 경로 접두어
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3789,6 +3789,9 @@ insert =
.xref-old-path-prefix = oud padprefix
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3790,6 +3790,9 @@ insert =
.xref-old-path-prefix = stary prefiks ścieżki
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3792,6 +3792,9 @@ insert =
.xref-old-path-prefix = prefixo de caminho antigo
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3792,6 +3792,9 @@ insert =
.xref-old-path-prefix = старый префикс пути
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3758,6 +3758,9 @@ insert =
.xref-old-path-prefix = eski yol öneki
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Görüntüleri seçin:
.transparency-mode-on = Saydamlık modunu girin [ON/OFF] <ON>:
.transparency-mode-off = Saydamlık modunu girin [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3774,6 +3774,9 @@ insert =
.xref-old-path-prefix = 旧路径前缀
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+3
View File
@@ -3786,6 +3786,9 @@ insert =
.xref-old-path-prefix = 舊路徑前綴
.xref-attach-dwg = Attach DWG
.xref-attach-image = Attach Image
.select-images = Select image(s):
.transparency-mode-on = Enter transparency mode [ON/OFF] <ON>:
.transparency-mode-off = Enter transparency mode [ON/OFF] <OFF>:
.xref-attach-menu = Attach...
.xref-change-path-type = Change Path Type
.xref-find-and-replace-menu = Find and Replace...
+49
View File
@@ -929,6 +929,55 @@ impl OpenCADStudio {
}
}
}
// TRANSPARENCY — raster images show transparent pixels (ON) or
// draw every pixel in its colour (OFF).
"TRANSPARENCY" => {
let c = crate::modules::insert::image_transparency::TransparencyCommand::new();
self.command_line.push_info(&c.prompt());
self.tabs[i].active_cmd = Some(Box::new(c));
}
"TRANSPARENCY MODE" | "TRANSPARENCY ON" | "TRANSPARENCY OFF" => {
use codec::entities::ImageDisplayFlags;
let images: Vec<(codec::Handle, bool)> = self.tabs[i]
.scene
.selected_entities()
.iter()
.filter(|(handle, _)| !self.tabs[i].scene.is_layer_locked(*handle))
.filter_map(|(handle, _)| match self.tabs[i].scene.document.get_entity(*handle) {
Some(codec::EntityType::RasterImage(image)) => {
Some((*handle, image.flags.contains(ImageDisplayFlags::TRANSPARENCY_ON)))
}
_ => None,
})
.collect();
let Some(&(_, first_on)) = images.first() else {
return Some(Task::none());
};
let handles: Vec<codec::Handle> = images.iter().map(|(handle, _)| *handle).collect();
if cmd == "TRANSPARENCY MODE" {
let c = crate::modules::insert::image_transparency::TransparencyCommand::mode(
handles, first_on,
);
self.command_line.push_info(&c.prompt());
self.tabs[i].active_cmd = Some(Box::new(c));
} else {
let on = cmd == "TRANSPARENCY ON";
self.push_undo_snapshot(i, "TRANSPARENCY");
for handle in &handles {
if let Some(codec::EntityType::RasterImage(image)) =
self.tabs[i].scene.document.get_entity_mut(*handle)
{
image.flags.set(ImageDisplayFlags::TRANSPARENCY_ON, on);
}
}
let changes: Vec<_> = handles
.iter()
.map(|handle| (*handle, crate::scene::ChangeKind::Modified))
.collect();
self.tabs[i].scene.bump_entities(&changes);
self.tabs[i].dirty = true;
}
}
// UNDERLAY — edit properties of selected PDF/DWF/DGN underlay entities.
// Usage:
// UNDERLAY FADE <0-80>
+1
View File
@@ -714,6 +714,7 @@ inventory::submit!(crate::command::CommandRegistration {
"IM",
"IMAGE",
"IMAGEATTACH",
"TRANSPARENCY",
"IMAGEEMBED",
"IMPORTOBJ",
"ISOLATEOBJECTS",
+11 -2
View File
@@ -242,8 +242,10 @@ fn emit_image(
{
return Err("Cannot plot bitmap: invalid coordinates, clip boundary, or opacity.".into());
}
// The low bit (free: the pixel buffer is aligned) keeps the opaque copy
// of a picture apart from its transparent one.
let key = (
std::sync::Arc::as_ptr(&image.pixels) as usize,
std::sync::Arc::as_ptr(&image.pixels) as usize | usize::from(!image.use_alpha),
image.width,
image.height,
);
@@ -255,7 +257,14 @@ fn emit_image(
doc.resources.xobjects.map.insert(
id.clone(),
printpdf::XObject::Image(printpdf::RawImage {
pixels: printpdf::RawImageData::U8(image.pixels.as_ref().clone()),
pixels: printpdf::RawImageData::U8(if image.use_alpha {
image.pixels.as_ref().clone()
} else {
// Transparency off: every pixel opaque in its colour.
let mut opaque = image.pixels.as_ref().clone();
opaque.chunks_exact_mut(4).for_each(|px| px[3] = 255);
opaque
}),
width: image.width as usize,
height: image.height as usize,
data_format: printpdf::RawImageFormat::RGBA8,
+3
View File
@@ -3303,6 +3303,9 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"File References" => Some(("insert", "xref-file-references")),
"Attach DWG" => Some(("insert", "xref-attach-dwg")),
"Attach Image" => Some(("insert", "xref-attach-image")),
"Select image(s):" => Some(("insert", "select-images")),
"Enter transparency mode [ON/OFF] <ON>:" => Some(("insert", "transparency-mode-on")),
"Enter transparency mode [ON/OFF] <OFF>:" => Some(("insert", "transparency-mode-off")),
"Attach..." => Some(("insert", "xref-attach-menu")),
"Change Path Type" => Some(("insert", "xref-change-path-type")),
"Find and Replace..." => Some(("insert", "xref-find-and-replace-menu")),
+93
View File
@@ -0,0 +1,93 @@
// TRANSPARENCY — whether raster images show their transparent pixels as
// transparent or draw every pixel in its stored colour.
//
// Select image(s):
// Enter transparency mode [ON/OFF] <OFF>: (the default is the first
// image's current mode)
use codec::Handle;
use glam::DVec3;
use crate::command::{CadCommand, CmdOption, CmdResult};
pub struct TransparencyCommand {
handles: Vec<Handle>,
/// `None` while images are being selected; then the default mode.
default_on: Option<bool>,
}
impl TransparencyCommand {
pub fn new() -> Self {
Self { handles: Vec::new(), default_on: None }
}
/// The mode prompt for images already chosen.
pub fn mode(handles: Vec<Handle>, default_on: bool) -> Self {
Self { handles, default_on: Some(default_on) }
}
fn apply(&mut self, on: bool) -> CmdResult {
let command = if on { "TRANSPARENCY ON" } else { "TRANSPARENCY OFF" };
CmdResult::Relaunch(command.to_string(), std::mem::take(&mut self.handles))
}
}
impl CadCommand for TransparencyCommand {
fn name(&self) -> &'static str {
"TRANSPARENCY"
}
fn prompt(&self) -> String {
match self.default_on {
None => crate::t!("Select image(s):").into_owned(),
Some(true) => crate::t!("Enter transparency mode [ON/OFF] <ON>:").into_owned(),
Some(false) => crate::t!("Enter transparency mode [ON/OFF] <OFF>:").into_owned(),
}
}
fn options(&self) -> Vec<CmdOption> {
match self.default_on {
None => Vec::new(),
Some(_) => vec![CmdOption::new("ON", "ON"), CmdOption::new("OFF", "OFF")],
}
}
fn is_selection_gathering(&self) -> bool {
self.default_on.is_none()
}
fn wants_text_input(&self) -> bool {
self.default_on.is_some()
}
fn on_selection_complete(&mut self, handles: Vec<Handle>) -> CmdResult {
self.handles = handles;
CmdResult::NeedPoint
}
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
let default_on = self.default_on?;
Some(match text.trim().to_uppercase().as_str() {
"" => self.apply(default_on),
"ON" => self.apply(true),
"OFF" => self.apply(false),
_ => CmdResult::ReportError("Invalid option keyword.".to_string()),
})
}
fn on_point(&mut self, _pt: DVec3) -> CmdResult {
CmdResult::NeedPoint
}
fn on_enter(&mut self) -> CmdResult {
match self.default_on {
None if self.handles.is_empty() => CmdResult::Cancel,
// The app keeps only the images and asks for the mode.
None => CmdResult::Relaunch(
"TRANSPARENCY MODE".to_string(),
std::mem::take(&mut self.handles),
),
Some(on) => self.apply(on),
}
}
}
+1
View File
@@ -9,6 +9,7 @@ mod content_browser;
pub(crate) mod create_block;
mod design_center;
mod edit_block;
pub(crate) mod image_transparency;
pub(crate) mod insert_block;
mod landxml;
pub(crate) mod picker;
+8
View File
@@ -94,6 +94,9 @@ pub struct ImageModel {
/// Magnified pixels stay square (raster images); otherwise they blend
/// (PDF pages, OLE pictures).
pub pixelated: bool,
/// Pixel alpha applies. A raster image whose transparency is off draws
/// every pixel opaque in its stored colour.
pub use_alpha: bool,
}
impl ImageModel {
@@ -165,6 +168,9 @@ impl ImageModel {
draw_depth: 0.0,
verts,
pixelated: true,
use_alpha: img
.flags
.contains(codec::entities::ImageDisplayFlags::TRANSPARENCY_ON),
})
}
}
@@ -274,6 +280,7 @@ impl ImageModel {
draw_depth: 0.0,
verts,
pixelated: false,
use_alpha: true,
})
}
}
@@ -355,6 +362,7 @@ impl ImageModel {
draw_depth: 0.0,
verts,
pixelated: false,
use_alpha: true,
})
}
}
+5 -2
View File
@@ -79,7 +79,9 @@ struct ImageParams {
/// Signed draw-order depth (-1,1); applied as a clip-z bias in the shader
/// so the raster orders against other entity types. 0.0 = neutral.
draw_depth: f32,
_pad: [f32; 2],
/// 1.0 when pixel alpha applies, 0.0 when pixels draw opaque.
use_alpha: f32,
_pad: f32,
} // 16 bytes
// ── Per-image GPU handle ──────────────────────────────────────────────────
@@ -196,7 +198,8 @@ impl ImageGpu {
let params = ImageParams {
opacity: model.opacity.clamp(0.0, 1.0),
draw_depth: 0.0,
_pad: [0.0; 2],
use_alpha: if model.use_alpha { 1.0 } else { 0.0 },
_pad: 0.0,
};
let params_buf = Arc::new(super::gpu_upload::upload_buffer(
device,
+8 -2
View File
@@ -28,7 +28,7 @@ struct Uniforms {
struct ImageParams {
opacity: f32,
draw_depth: f32, // signed (-1,1) draw-order bias; 0 = neutral
_pad1: f32,
use_alpha: f32, // 1 = pixel alpha applies, 0 = pixels draw opaque
_pad2: f32,
};
@group(1) @binding(2) var<uniform> img_params: ImageParams;
@@ -63,5 +63,11 @@ fn vs_main(in: VertIn) -> VertOut {
@fragment
fn fs_main(in: VertOut) -> @location(0) vec4<f32> {
let col = textureSample(img_texture, img_sampler, in.uv);
return vec4<f32>(col.rgb, col.a * img_params.opacity);
let alpha = select(1.0, col.a, img_params.use_alpha > 0.5);
// A fully transparent pixel leaves no depth behind, so what lies under
// the image (drawn before or after it) shows through.
if (alpha * img_params.opacity <= 0.0) {
discard;
}
return vec4<f32>(col.rgb, alpha * img_params.opacity);
}