From 6a6605191f3f948a85167e9d57d3d6ebde384a19 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Wed, 6 Aug 2025 11:33:50 -0400 Subject: [PATCH] configurable image fit in all image views Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 5 +++-- html/locale_en.json | 1 + javascript/imageViewer.js | 12 ++++++++++++ javascript/sdnext.css | 24 ++++++++++++++++++++++-- modules/model_quant.py | 2 +- modules/ui_common.py | 2 ++ modules/ui_control.py | 2 +- modules/ui_symbols.py | 3 +-- 8 files changed, 43 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1276e1be..1cd14fb7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,12 +38,13 @@ And (*as always*) many bugfixes and improvements to existing features! - updated real-time hints, thanks @CalamitousFelicitousness - rewritten **CivitAI downloader** in *models -> civitai* + - quicksettings reset button to restore all quicksettings to default values + because things do sometimes get wrong... + - configurable image fit in all image views - updated *models -> current* tab - updated *models -> list models* tab - updated *models -> metadata* tab - updated *extensions* tab - - quicksettings reset button to restore all quicksettings to default values - because things do sometimes get wrong... - redesign *settings -> user interface* - gallery bypass browser cache for thumbnails - gallery safer delete operation diff --git a/html/locale_en.json b/html/locale_en.json index b222c2ed5..e908743eb 100644 --- a/html/locale_en.json +++ b/html/locale_en.json @@ -19,6 +19,7 @@ {"id":"","label":"🖌️","localized":"","hint":"LaMa remove selected object from image"}, {"id":"","label":"🖼️","localized":"","hint":"Show preview"}, {"id":"","label":"♻","localized":"","hint":"Interrogate image"}, + {"id":"","label":"⁜","localized":"","hint":"Cycle image fit method"}, {"id":"","label":"↶","localized":"","hint":"Apply selected style to prompt"}, {"id":"","label":"↷","localized":"","hint":"Save current prompt to style"}, {"id":"","label":"","localized":"","hint":"Sort by name, ascending"}, diff --git a/javascript/imageViewer.js b/javascript/imageViewer.js index c8a2dfd65..5631c173b 100644 --- a/javascript/imageViewer.js +++ b/javascript/imageViewer.js @@ -3,6 +3,18 @@ let previewDrag = false; let modalPreviewZone; let previewInstance; +function cycleImageFit() { + const root = document.documentElement; + const current = getComputedStyle(root).getPropertyValue('--sd-image-fit').trim(); + let next = 'contain'; + if (current === 'contain') next = 'cover'; + else if (current === 'cover') next = 'fill'; + else if (current === 'fill') next = 'scale-down'; + else if (current === 'scale-down') next = 'none'; + root.style.setProperty('--sd-image-fit', next); + log('cycleImageFit', current, next); +} + function closeModal(evt, force = false) { if (force) gradioApp().getElementById('lightboxModal').style.display = 'none'; if (previewDrag) return; diff --git a/javascript/sdnext.css b/javascript/sdnext.css index d68a1be8e..f5e2588e9 100644 --- a/javascript/sdnext.css +++ b/javascript/sdnext.css @@ -14,6 +14,7 @@ --color-trace: #666666; --color-warning: #FF9900; --left-column: 530px; + --sd-image-fit: contain; } a { @@ -515,17 +516,36 @@ color: var(--primary-500) !important color: var(--body-text-color-subdued) !important } +.gradio-gallery img, .image-container img { + max-width: 100%; + object-position: top; + width: 100%; + height: 100%; + object-fit: var(--sd-image-fit) !important; +} + .interrogate { background: none !important; font-size: 1.5em !important; max-width: fit-content; position: absolute; right: 2.8em; - top: 0.2em; + top: 0.1em; z-index: 50; } -.interrogate:hover { +.image-fit { + background: none !important; + font-size: 1.5em !important; + max-width: fit-content; + position: absolute; + right: 4.0em; + top: 0.1em; + z-index: 50; +} + +.interrogate:hover, +.image-fit:hover { background: var(--button-primary-background-fill-hover) !important; } diff --git a/modules/model_quant.py b/modules/model_quant.py index f5bb9cef9..5513bb127 100644 --- a/modules/model_quant.py +++ b/modules/model_quant.py @@ -150,7 +150,7 @@ def create_sdnq_config(kwargs = None, allow: bool = True, module: str = 'Model', return_device=return_device, modules_to_not_convert=modules_to_not_convert, ) - log.debug(f'Quantization: module="{module}" type=sdnq dtype={weights_dtype} matmul={shared.opts.sdnq_use_quantized_matmul} group_size={shared.opts.sdnq_quantize_weights_group_size} quant_conv={shared.opts.sdnq_quantize_conv_layers} matmul_conv={shared.opts.sdnq_use_quantized_matmul_conv} dequantize_fp32={shared.opts.sdnq_dequantize_fp32} quantize_with_gpu={shared.opts.sdnq_quantize_with_gpu} quantization_device={quantization_device} return_device={return_device} device_map={shared.opts.device_map} offload_mode={shared.opts.diffusers_offload_mode}') + log.debug(f'Quantization: module="{module}" type=sdnq dtype={weights_dtype} matmul={shared.opts.sdnq_use_quantized_matmul} group_size={shared.opts.sdnq_quantize_weights_group_size} quant_conv={shared.opts.sdnq_quantize_conv_layers} matmul_conv={shared.opts.sdnq_use_quantized_matmul_conv} dequantize_fp32={shared.opts.sdnq_dequantize_fp32} quantize_with_gpu={shared.opts.sdnq_quantize_with_gpu} quantization_device={quantization_device} return_device={return_device} device_map={shared.opts.device_map} offload_mode={shared.opts.diffusers_offload_mode} non_blocking={shared.opts.diffusers_offload_nonblocking}') if kwargs is None: return sdnq_config else: diff --git a/modules/ui_common.py b/modules/ui_common.py index 9df1a9ccb..494c2bc8a 100644 --- a/modules/ui_common.py +++ b/modules/ui_common.py @@ -247,6 +247,8 @@ def create_output_panel(tabname, preview=True, prompt=None, height=None, transfe ) if prompt is not None: ui_sections.create_interrogate_button(tab=tabname, inputs=result_gallery, outputs=prompt, what='output') + button_image_fit = gr.Button(ui_symbols.resize, elem_id=f"{tabname}_image_fit", elem_classes=['image-fit']) + button_image_fit.click(fn=None, _js="cycleImageFit", inputs=[], outputs=[]) with gr.Column(elem_id=f"{tabname}_footer", elem_classes="gallery_footer"): dummy_component = gr.Label(visible=False) diff --git a/modules/ui_control.py b/modules/ui_control.py index 05197127b..cfcd191ed 100644 --- a/modules/ui_control.py +++ b/modules/ui_control.py @@ -215,7 +215,7 @@ def create_ui(_blocks: gr.Blocks=None): gr.HTML('Output

') with gr.Tabs(elem_classes=['control-tabs'], elem_id='control-tab-output') as output_tabs: with gr.Tab('Gallery', id='out-gallery'): - output_gallery, _output_gen_info, _output_html_info, _output_html_info_formatted, output_html_log = ui_common.create_output_panel("control", preview=True, prompt=prompt, height=gr_height) + output_gallery, _output_gen_info, _output_html_info, _output_html_info_formatted, output_html_log = ui_common.create_output_panel("control", preview=False, prompt=prompt, height=gr_height) with gr.Tab('Image', id='out-image'): output_image = gr.Image(label="Output", show_label=False, type="pil", interactive=False, tool="editor", height=gr_height, elem_id='control_output_image', elem_classes=['control-image']) with gr.Tab('Video', id='out-video'): diff --git a/modules/ui_symbols.py b/modules/ui_symbols.py index 66fbda681..975cd4e69 100644 --- a/modules/ui_symbols.py +++ b/modules/ui_symbols.py @@ -25,8 +25,7 @@ reuse = '⬅️' search = '🔍' preview = '🖼️' image = '🖌️' -mark_diag = '※' -mark_flag = '⁜' +resize = '⁜' interrogate = '♻' int_clip = '✎' int_blip = '✐'