From 1ec2f1df368ba931b022c105ac00dea467789ae7 Mon Sep 17 00:00:00 2001 From: kovacsv Date: Wed, 28 Jun 2023 19:02:39 +0200 Subject: [PATCH] Option to create screenshot with transparent background #401 --- sandbox/embed_selfhost_snapshot.html | 2 +- source/engine/viewer/viewer.js | 7 ++++- source/website/css/dialogs.css | 34 ++++++++++++++++------- source/website/css/themes.css | 4 ++- source/website/snapshotdialog.js | 41 ++++++++++++++++++---------- source/website/themehandler.js | 1 + 6 files changed, 62 insertions(+), 27 deletions(-) diff --git a/sandbox/embed_selfhost_snapshot.html b/sandbox/embed_selfhost_snapshot.html index 585abca..9074cbb 100644 --- a/sandbox/embed_selfhost_snapshot.html +++ b/sandbox/embed_selfhost_snapshot.html @@ -22,7 +22,7 @@ for (let embeddedViewer of embeddedViewers) { let viewer = embeddedViewer.GetViewer (); let img = document.createElement ('img'); - img.src = viewer.GetImageAsDataUrl (400, 300); + img.src = viewer.GetImageAsDataUrl (400, 300, false); document.body.appendChild (img); } } diff --git a/source/engine/viewer/viewer.js b/source/engine/viewer/viewer.js index 1f90aa3..ae5d13f 100644 --- a/source/engine/viewer/viewer.js +++ b/source/engine/viewer/viewer.js @@ -587,7 +587,7 @@ export class Viewer }; } - GetImageAsDataUrl (width, height) + GetImageAsDataUrl (width, height, isTransparent) { let originalSize = this.GetImageSize (); let renderWidth = width; @@ -596,10 +596,15 @@ export class Viewer renderWidth /= window.devicePixelRatio; renderHeight /= window.devicePixelRatio; } + let clearAlpha = this.renderer.getClearAlpha (); + if (isTransparent) { + this.renderer.setClearAlpha (0.0); + } this.ResizeRenderer (renderWidth, renderHeight); this.Render (); let url = this.renderer.domElement.toDataURL (); this.ResizeRenderer (originalSize.width, originalSize.height); + this.renderer.setClearAlpha (clearAlpha); return url; } diff --git a/source/website/css/dialogs.css b/source/website/css/dialogs.css index f8f8efa..ff8f27f 100644 --- a/source/website/css/dialogs.css +++ b/source/website/css/dialogs.css @@ -79,7 +79,7 @@ div.ov_dialog div.ov_dialog_submessage div.ov_dialog input.ov_dialog_text { padding: 5px; - border: 1px solid var(--ov_border_color); + border: 1px solid var(--ov_dialog_control_border_color); border-radius: 5px; overflow: auto; } @@ -89,7 +89,7 @@ div.ov_dialog textarea.ov_dialog_textarea margin: 10px 0px; width: 100%; height: 120px; - border: 1px solid var(--ov_border_color); + border: 1px solid var(--ov_dialog_control_border_color); box-sizing: border-box; } @@ -130,7 +130,7 @@ div.ov_dialog div.ov_dialog_file_link div.ov_dialog_file_link_text div.ov_dialog div.ov_dialog_copyable_input { padding: 3px; - border: 1px solid var(--ov_border_color); + border: 1px solid var(--ov_dialog_control_border_color); border-radius: 5px; overflow: auto; } @@ -176,6 +176,7 @@ div.ov_dialog div.ov_dialog_row_value div.ov_dialog select.ov_select { width: 100%; + border: 1px solid var(--ov_dialog_control_border_color); box-sizing: border-box; } @@ -251,20 +252,27 @@ div.ov_progress div.ov_progress_text text-align: center; } -div.ov_snapshot_dialog_left +div.ov_snapshot_dialog { - width: 190px; - float: left; + width: 480px; + float: right; +} + +div.ov_snapshot_dialog_options +{ + width: 230px; + float: right; } img.ov_snapshot_dialog_preview { background: var(--ov_border_color); - border: 1px solid var(--ov_border_color); - width: 183px; - height: 183px; + border: 1px solid var(--ov_dialog_control_border_color); + width: 230px; + height: 230px; object-fit: contain; - float: right; + float: left; + border-radius: 5px; } div.ov_snapshot_dialog_param_name @@ -282,6 +290,12 @@ input.ov_snapshot_dialog_param_value float: left; } +div.ov_snapshot_dialog_separator +{ + margin: 10px 0px; + border-bottom: 1px solid var(--ov_border_color); +} + @media (hover) { diff --git a/source/website/css/themes.css b/source/website/css/themes.css index f2ae8c9..8215978 100644 --- a/source/website/css/themes.css +++ b/source/website/css/themes.css @@ -23,6 +23,7 @@ --ov_treeview_selected_color: #eeeeee; --ov_dialog_foreground_color: #000000; --ov_dialog_background_color: #ffffff; + --ov_dialog_control_border_color: #e1e1e1; --ov_border_color: #dddddd; --ov_shadow: 0px 0px 10px #cccccc; @@ -48,7 +49,8 @@ --ov_toolbar_separator_color_dark: #888888; --ov_treeview_selected_color_dark: #38393d; --ov_dialog_foreground_color_dark: #fafafa; - --ov_dialog_background_color_dark: #333333; + --ov_dialog_background_color_dark: #3c3c40; + --ov_dialog_control_border_color_dark: #e1e1e1; --ov_border_color_dark: #444444; --ov_shadow_dark: 0px 0px 10px #222222; } diff --git a/source/website/snapshotdialog.js b/source/website/snapshotdialog.js index c1186f6..26dd97c 100644 --- a/source/website/snapshotdialog.js +++ b/source/website/snapshotdialog.js @@ -1,8 +1,8 @@ -import { AddDiv, CreateDomElement } from '../engine/viewer/domutils.js'; -import { AddNumberInput, AddRadioButton } from '../website/utils.js'; +import { AddDiv, AddDomElement, CreateDomElement } from '../engine/viewer/domutils.js'; +import { AddCheckbox, AddNumberInput, AddRadioButton } from '../website/utils.js'; import { ButtonDialog } from './dialog.js'; import { DownloadUrlAsFile } from './utils.js'; -import { CookieGetIntVal, CookieGetStringVal, CookieSetIntVal, CookieSetStringVal } from './cookiehandler.js'; +import { CookieGetBoolVal, CookieGetIntVal, CookieGetStringVal, CookieSetBoolVal, CookieSetIntVal, CookieSetStringVal } from './cookiehandler.js'; import { HandleEvent } from './eventhandler.js'; export function ShowSnapshotDialog (viewer) @@ -13,19 +13,19 @@ export function ShowSnapshotDialog (viewer) AddRadioButton (line, id, 'snapshot_size', text, isSelected, onChange); } - function GetImageUrl (viewer, size) + function GetImageUrl (viewer, size, isTransparent) { let width = parseInt (size[0], 10); let height = parseInt (size[1], 10); if (width < 1 || height < 1) { return null; } - return viewer.GetImageAsDataUrl (size[0], size[1]); + return viewer.GetImageAsDataUrl (size[0], size[1], isTransparent); } - function UpdatePreview (viewer, previewImage, size) + function UpdatePreview (viewer, previewImage, size, isTransparent) { - let url = GetImageUrl (viewer, size); + let url = GetImageUrl (viewer, size, isTransparent); previewImage.src = url; } @@ -62,6 +62,7 @@ export function ShowSnapshotDialog (viewer) } let selectedIndex = 0; + let isTransparent = CookieGetBoolVal ('ov_last_snapshot_transparent', false); let customIndex = 3; let sizes = [ { @@ -98,7 +99,7 @@ export function ShowSnapshotDialog (viewer) onClick () { dialog.Close (); HandleEvent ('snapshot_created', sizes[selectedIndex].name); - let url = GetImageUrl (viewer, GetSize (sizes, selectedIndex)); + let url = GetImageUrl (viewer, GetSize (sizes, selectedIndex), isTransparent); if (url !== null) { DownloadUrlAsFile (url, 'model.png'); } @@ -106,8 +107,13 @@ export function ShowSnapshotDialog (viewer) } ]); - let optionsDiv = AddDiv (contentDiv, 'ov_snapshot_dialog_left'); + let dialogDiv = dialog.GetContentDiv (); + dialogDiv.classList.add ('ov_snapshot_dialog'); + let previewImage = CreateDomElement ('img', 'ov_snapshot_dialog_preview'); + contentDiv.appendChild (previewImage); + + let optionsDiv = AddDiv (contentDiv, 'ov_snapshot_dialog_options'); let lastSnapshotSizeName = CookieGetStringVal ('ov_last_snapshot_size', sizes[1].name); for (let i = 0; i < sizes.length; i++) { @@ -124,25 +130,32 @@ export function ShowSnapshotDialog (viewer) AddSizeRadioButton (optionsDiv, 'snapshot_' + i.toString (), size.name, selected, () => { selectedIndex = i; CookieSetStringVal ('ov_last_snapshot_size', size.name); - UpdatePreview (viewer, previewImage, GetSize (sizes, i)); + UpdatePreview (viewer, previewImage, GetSize (sizes, i), isTransparent); UpdateCustomStatus (sizes, customIndex, selectedIndex); }); } customSize.widthInput = AddWidthHeightNumberInput (optionsDiv, 'Width', (val) => { - UpdatePreview (viewer, previewImage, GetSize (sizes, selectedIndex)); + UpdatePreview (viewer, previewImage, GetSize (sizes, selectedIndex), isTransparent); CookieSetIntVal ('ov_snapshot_custom_width', val); }); customSize.heightInput = AddWidthHeightNumberInput (optionsDiv, 'Height', (val) => { - UpdatePreview (viewer, previewImage, GetSize (sizes, selectedIndex)); + UpdatePreview (viewer, previewImage, GetSize (sizes, selectedIndex), isTransparent); CookieSetIntVal ('ov_snapshot_custom_height', val); }); customSize.widthInput.value = CookieGetIntVal ('ov_snapshot_custom_width', 1000); customSize.heightInput.value = CookieGetIntVal ('ov_snapshot_custom_height', 1000); UpdateCustomStatus (sizes, customIndex, selectedIndex); - contentDiv.appendChild (previewImage); - UpdatePreview (viewer, previewImage, GetSize (sizes, selectedIndex)); + AddDomElement (optionsDiv, 'div', 'ov_snapshot_dialog_separator', null); + + let transparentCheckbox = AddCheckbox (optionsDiv, 'snapshot_transparent_background', 'Transparent background', isTransparent, () => { + isTransparent = transparentCheckbox.checked; + UpdatePreview (viewer, previewImage, GetSize (sizes, selectedIndex), isTransparent); + CookieSetBoolVal ('ov_last_snapshot_transparent', isTransparent); + }); + + UpdatePreview (viewer, previewImage, GetSize (sizes, selectedIndex), isTransparent); dialog.Open (); return dialog; diff --git a/source/website/themehandler.js b/source/website/themehandler.js index 24a1f71..78f2183 100644 --- a/source/website/themehandler.js +++ b/source/website/themehandler.js @@ -27,6 +27,7 @@ export class ThemeHandler '--ov_treeview_selected_color': {}, '--ov_dialog_foreground_color': {}, '--ov_dialog_background_color': {}, + '--ov_dialog_control_border_color': {}, '--ov_border_color': {}, '--ov_shadow': {} };