├── .gitignore ├── README.md ├── javascript └── sd-webui-ar-plusplus.js ├── scripts └── sd-webui-ar-plusplus.py └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.swp 2 | **/.mypy_cache 3 | *.py[cod] 4 | aspect_ratios.txt 5 | resolutions.txt 6 | button_titles.js 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stable Diffusion WebUI Aspect Ratio and Resolution Buttons 2 | 3 | Extension for [stable-diffusion-webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui.git) and [stable-diffusion-webui-forge](https://github.com/lllyasviel/stable-diffusion-webui-forge) adding image aspect ratio selector buttons. 4 | 5 | --- 6 | 7 | ## Main Fork feature 8 | 9 | Screenshot 2024-03-13 111328 10 | 11 | #### Uses an enhanced method to calculate the width/height values when clicking Aspect Ratio buttons. 12 | 13 | - Gets the mean value (average) of the current **width** and **height** values in the UI. 14 | 15 | - For the selected aspect ratio, the dimensions are offset equally (positively and negatively) from the average, ensuring nearest total pixel count to user's initial resolution. 16 | 17 | - Uses the same precision rounding method as [Stability-AI/StableSwarmUI](https://github.com/Stability-AI/StableSwarmUI/blob/4ef98019fc4796d69f0d1d2dfa487d684a748cc3/src/Utils/Utilities.cs#L573) when updating image dimensions. 18 | 19 | - "Mode" button allows switching to use calculation method from [LEv145/--sd-webui-ar-plus](https://github.com/LEv145/--sd-webui-ar-plus) (Only update Width OR Height) 20 | 21 | --- 22 | 23 | 1 24 | 25 | --- 26 | 27 | #### For best results switching between aspect ratios, pick a static res value (such as 1024, etc), then press the "Lock" button to lock in an average res. 28 | 29 | - The calculation method works correctly in all cases 30 | 31 | - However, since the output values are rounded to the nearest division of 64, the mean value (average) of the input values will change when "Unlocked". 32 | 33 | --- 34 | 35 | 2 36 | 37 | --- 38 | 39 | #### The original "Calculator panel" has been replaced with an information panel: 40 | 41 | - Current average is displayed for reference. 42 | 43 | - Rounding precision can be adjusted with default value of 64px. 44 | 45 | --- 46 | 47 | ## Installation 48 | 49 | 1. Navigate to the Extensions tab in [stable-diffusion-webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui.git) or [stable-diffusion-webui-forge](https://github.com/lllyasviel/stable-diffusion-webui-forge) 50 | 51 | 2. Click "Install from URL" 52 | 53 | 3. Copy/Paste this repository into the "URL for extension's git repository" field and click "Install" 54 | ``` 55 | https://github.com/altoiddealer/--sd-webui-ar-plusplus 56 | ``` 57 | 58 | install 59 | 60 | 61 | 4. Return to the "Installed" tab and click "Apply and restart UI" 62 | 63 | Restart 64 | 65 | 66 | --- 67 | 68 |
69 | 70 | Details from [the source project (LEv145/--sd-webui-ar-plus) 71 | 72 | # Details from [the source project](https://github.com/LEv145/--sd-webui-ar-plus) (LEv145/--sd-webui-ar-plus) 73 | 74 | (For reference - much of this is obsolete) 75 | 76 | ## Fork features 77 | 78 | - New button `🔃` for calculation of height and width inverse 79 | - Normal mode: `1024x1024 and 16:9 = 1820x1024` 80 | - Reverse mode: `1024x1024 and 16:9 = 1024x576` 81 | - New button `🔍` for rounding dimensions to the nearest multiples of 4 (`1023x101` => `1024x100`) 82 | - New styles (Some styles have been moved to the original extension) 83 | - Better resolution presets (By formula: `f(x) = 512 + (1024-512)/4*x, 0 <= x <= 4`) 84 | - Better ratios presets (From [wikipedia](https://en.wikipedia.org/wiki/Aspect_ratio_(image))) 85 | - Rename `Calc` button to `📐` 86 | - Can work together with the original extension 87 | 88 | 89 | ## Updates 90 | 91 | - 20/02/2023 :warning: this update will remove your local config files (`aspect_ratios.txt` and `resolutions.txt`) and it will create new default ones. These can be then modified freely and preserved in the future. For more info read [here](https://github.com/alemelis/sd-webui-ar/issues/9). 92 | 93 | ## Install 94 | 95 | Browse to the `Extensions` tab -> go to `Install from URL` -> paste in `https://github.com/alemelis/sd-webui-ar` -> click `Install` 96 | 97 | 98 | Here's how the UI looks like after installing this extension 99 | 100 | Screenshot 2023-03-30 at 20 37 56 101 | 102 | ## Usage 103 | 104 | - Click on the aspect ratio button you want to set. In the case of an aspect ratio greater than 1, the script fixes the width and changes the height. Whereas if the aspect ratio is smaller than 1, the width changes while the height is fixed. 105 | - Reset image resolution by clicking on one of the buttons on the second row. 106 | 107 | ### Configuration 108 | 109 | Aspect ratios can be defined in the `/sd-webui-ar/aspect_ratios.txt` file. For example, 110 | 111 | ``` 112 | 1:1, 1.0 113 | 3:2, 3/2 114 | 4:3, 4/3 115 | 16:9, 16/9 116 | # 6:13, 6/13 117 | # 9:16, 9/16 118 | # 3:5, 3/5 119 | # 2:3, 2/3 120 | # 19:16, 19/16 # fox movietone 121 | # 5:4, 5/4 # medium format photo 122 | # 11:8, 11/8 # academy standard 123 | # IMAX, 1.43 124 | # 14:9, 14/9 125 | # 16:10, 16/10 126 | # 𝜑, 1.6180 # golden ratio 127 | # 5:3, 5/3 # super 16mm 128 | # 1.85, 1.85 # US widescreen cinema 129 | # DCI, 1.9 # digital imax 130 | # 2:1, 2.0 # univisium 131 | # 70mm, 2.2 132 | # 21:9, 21/9 # cinematic wide screen 133 | # δ, 2.414 # silver ratio 134 | # UPV70, 2.76 # ultra panavision 70 135 | # 32:9, 32/9 # ultra wide screen 136 | # PV, 4.0 # polyvision 137 | ``` 138 | 139 | Note the `#` marking the line as a comment, i.e. the extension is not reading that line. To use a custom value, un-comment the relative line by removing the starting `#`. 140 | A custom aspect ratio is defined as `button-label, aspect-ratio-value # comment`. It is recommended to set the `aspect-ratio-value` to a fraction, but a `float` or `int` will work as well. The `# comment` is optional. 141 | The `button-label` will be displayed inside the button. It can be anything you like. 142 | 143 | Resolutions presets are defined inside `resolutions.txt` file, 144 | 145 | ``` 146 | 1, 512, 512 # 1:1 square 147 | 2, 768, 512 # 3:2 landscape 148 | 3, 403, 716 # 9:16 portrait 149 | ``` 150 | 151 | The format to be used is `button-label, width, height, # optional comment`. As before, lines starting with `#` will be ignored. 152 | 153 | ## Calculator Panel 154 | Use the calculator to determine new width or height values based on the aspect ratio of source dimensions. 155 | - Click `Calc` to show or hide the aspect ratio calculator 156 | - Set the source dimensions: 157 | - Enter manually, or 158 | - Click ⬇️ to get source dimentions from txt2img/img2img sliders, or 159 | - Click 🖼️ to get source dimensions from input image component on the current tab 160 | - Click ⇅ to swap the width and height, if desired 161 | - Set the desired width or height, then click either `Calculate Height` or `Calculate Width` to calculate the missing value 162 | - Click `Apply` to send the values to the txt2txt/img2img sliders 163 | --- 164 | Basic usage of aspect ratio calculator 165 | 166 |
167 | -------------------------------------------------------------------------------- /javascript/sd-webui-ar-plusplus.js: -------------------------------------------------------------------------------- 1 | if ("undefined" === typeof arsp__ar_button_titles) { 2 | arsp__ar_button_titles = {}; 3 | } 4 | 5 | arsp__ar_button_titles[" \u{1F513}"] = 'Toggle to lock the "average" width/height values in the UI\nIt is recommended to "Lock" when switching between ARs in "Offset" mode.\n\n\u{1F512} = Locked\n\u{1F513} = Unlocked'; 6 | arsp__ar_button_titles[" \u{025AD}"] = 'For "Offset mode" (default):\n\u{025AF} = Portrait resolutions\n\u{025AD} = Landscape resolutions\n\nFor "One Dimension" mode:\n\u{025AD} = Modify Width\n\u{025AF} = Modify Height'; 7 | arsp__ar_button_titles[" \u{02B83}"] = 'Toggle the Mode for updating resolution.\nResolution is always rounded to precision (default 64px).\n\n\u{02B83} = "Offset" updates both Width/Height from the average current resolution\n\u{02B85} = "One Dimension" changes only Width or Height'; 8 | arsp__ar_button_titles[" \u{02139}"] = 'Show the Information panel including additional settings.' 9 | arsp__ar_button_titles[" \u{02BC5}"] = 'Hide the Information panel.' 10 | 11 | onUiUpdate(function(){ 12 | gradioApp().querySelectorAll('#arsp__txt2img_container_aspect_ratio button, #arsp__img2img_container_aspect_ratio button').forEach(function(elem){ 13 | tooltip = arsp__ar_button_titles[elem.textContent]; 14 | if(tooltip){ 15 | elem.title = tooltip; 16 | } 17 | }) 18 | }) -------------------------------------------------------------------------------- /scripts/sd-webui-ar-plusplus.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | import contextlib 3 | from pathlib import Path 4 | 5 | import gradio as gr 6 | 7 | import modules.scripts as scripts 8 | from modules.ui_components import ToolButton 9 | 10 | from fractions import Fraction 11 | from math import sqrt 12 | 13 | BASE_PATH = scripts.basedir() 14 | 15 | class ButtonState(): 16 | def __init__(self): 17 | self.is_locked = False 18 | self.switched = False 19 | self.alt_mode = False 20 | def toggle_lock(self): 21 | self.is_locked = not self.is_locked 22 | def toggle_switch(self): 23 | self.switched = not self.switched 24 | def toggle_mode(self): 25 | self.alt_mode = not self.alt_mode 26 | 27 | txt2img_state = ButtonState() 28 | img2img_state = ButtonState() 29 | 30 | # Helper functions for calculating new width/height values 31 | def round_to_precision(val, prec): 32 | return round(val / prec) * prec 33 | 34 | def res_to_model_fit(avg, w, h, prec): 35 | mp = w * h 36 | mp_target = avg * avg 37 | scale = sqrt(mp_target / mp) 38 | w = int(round_to_precision(w * scale, prec)) 39 | h = int(round_to_precision(h * scale, prec)) 40 | return w, h 41 | 42 | def calc_width(n, d, w, h, prec): 43 | ar = round((n / d), 2) # Convert AR parts to fraction 44 | if ar > 1.0: 45 | h = w / ar 46 | elif ar < 1.0: 47 | w = h * ar 48 | else: 49 | new_value = max([w, h]) 50 | w, h = new_value, new_value 51 | w = int(round_to_precision((w + prec / 2), prec)) 52 | h = int(round_to_precision((h + prec / 2), prec)) 53 | return w, h 54 | 55 | def calc_height(n, d, w, h, prec): 56 | ar = round((n / d), 2) # Convert AR parts to fraction 57 | if ar > 1.0: 58 | w = h * ar 59 | elif ar < 1.0: 60 | h = w / ar 61 | else: 62 | new_value = min([w, h]) 63 | w, h = new_value, new_value 64 | w = int(round_to_precision((w + prec / 2), prec)) 65 | h = int(round_to_precision((h + prec / 2), prec)) 66 | return w, h 67 | 68 | def dims_from_ar(avg, n, d, prec): 69 | doubleavg = avg * 2 70 | ar_sum = n+d 71 | # calculate width and height by factoring average with aspect ratio 72 | w = round((n / ar_sum) * doubleavg) 73 | h = round((d / ar_sum) * doubleavg) 74 | # Round to correct megapixel precision 75 | w, h = res_to_model_fit(avg, w, h, prec) 76 | return w, h 77 | 78 | def avg_from_dims(w, h): 79 | avg = (w + h) // 2 80 | if (w + h) % 2 != 0: 81 | avg += 1 82 | return avg 83 | 84 | ## Aspect Ratio buttons 85 | def create_ar_button_function(ar:str, is_img2img:bool): 86 | def wrapper(avg, prec, w=512, h=512): 87 | # Determine the state based on whether it's img2img or txt2img 88 | state = img2img_state if is_img2img else txt2img_state 89 | 90 | n, d = map(Fraction, ar.split(':')) # Split numerator and denominator 91 | 92 | if not state.is_locked: 93 | avg = avg_from_dims(w, h) # Get average of current width/height values 94 | 95 | if not state.alt_mode: # True = offset, False = One dimension 96 | w, h = dims_from_ar(avg, n, d, prec) # Calculate new w + h from avg, AR, and precision 97 | if state.switched: # Switch results if switch mode is active 98 | w, h = h, w 99 | else: # Calculate w or h from input, AR, and precision 100 | if state.switched: # Switch results if switch mode is active 101 | w, h = calc_width(n, d, w, h, prec) # Modify width 102 | else: 103 | w, h = calc_height(n, d, w, h, prec) # Modify height 104 | 105 | return avg, w, h 106 | 107 | return wrapper 108 | 109 | def create_ar_buttons( 110 | lst: t.Iterable[str], 111 | is_img2img: bool, 112 | ) -> t.Tuple[t.List[ToolButton], t.Dict[ToolButton, t.Callable]]: 113 | buttons = [] 114 | functions = {} 115 | 116 | for ar in lst: 117 | button = ToolButton(ar, render=False) 118 | function = create_ar_button_function(ar, is_img2img) 119 | buttons.append(button) 120 | functions[button] = function 121 | 122 | return buttons, functions 123 | 124 | ## Static Resolution buttons 125 | def create_res_button_function(w:int, h:int, is_img2img:bool): 126 | def wrapper(avg): 127 | state = img2img_state if is_img2img else txt2img_state 128 | if not state.is_locked: 129 | avg = avg_from_dims(w, h) 130 | return avg, w, h 131 | return wrapper 132 | 133 | def create_res_buttons( 134 | lst: t.Iterable[t.Tuple[t.List[int], str]], 135 | is_img2img: bool, 136 | ) -> t.Tuple[t.List[ToolButton], t.Dict[ToolButton, t.Callable]]: 137 | buttons = [] 138 | functions = {} 139 | 140 | for resolution, label in lst: 141 | button = ToolButton(label) 142 | w, h = resolution 143 | function = create_res_button_function(w, h, is_img2img) 144 | buttons.append(button) 145 | functions[button] = function 146 | 147 | return buttons, functions 148 | 149 | # Get values for Aspect Ratios from file 150 | def parse_aspect_ratios_file(filename): 151 | values, flipvals, comments = [], [], [] 152 | file = Path(BASE_PATH, filename) 153 | 154 | if not file.exists(): 155 | return values, comments, flipvals 156 | 157 | with open(file, "r", encoding="utf-8") as f: 158 | lines = f.readlines() 159 | 160 | if not lines: 161 | return values, comments, flipvals 162 | 163 | for line in lines: 164 | if line.startswith("#"): 165 | continue 166 | 167 | value = line.strip() 168 | 169 | comment = "" 170 | if "#" in value: 171 | value, comment = value.split("#") 172 | value = value.strip() 173 | values.append(value) 174 | comments.append(comment) 175 | 176 | comp1, comp2 = value.split(':') 177 | flipval = f"{comp2}:{comp1}" 178 | flipvals.append(flipval) 179 | 180 | return values, comments, flipvals 181 | 182 | # Get values for Static Resolutions from file 183 | def parse_resolutions_file(filename): 184 | labels, values, comments = [], [], [] 185 | file = Path(BASE_PATH, filename) 186 | 187 | if not file.exists(): 188 | return labels, values, comments 189 | 190 | with open(file, "r", encoding="utf-8") as f: 191 | lines = f.readlines() 192 | 193 | if not lines: 194 | return labels, values, comments 195 | 196 | for line in lines: 197 | if line.startswith("#"): 198 | continue 199 | 200 | label, width, height = line.strip().split(",") 201 | comment = "" 202 | if "#" in height: 203 | height, comment = height.split("#") 204 | 205 | resolution = (width, height) 206 | 207 | labels.append(label) 208 | values.append(resolution) 209 | comments.append(comment) 210 | 211 | return labels, values, comments 212 | 213 | def write_aspect_ratios_file(filename): 214 | aspect_ratios = [ 215 | "1:1 # Square\n", 216 | "4:3 # Television Photography\n", 217 | "3:2 # Photography\n", 218 | "8:5 # Widescreen Displays\n", 219 | "16:9 # Widescreen Television\n", 220 | "21:9 # Ultrawide Cinematography" 221 | ] 222 | with open(filename, "w", encoding="utf-8") as f: 223 | f.writelines(aspect_ratios) 224 | 225 | def write_resolutions_file(filename): 226 | resolutions = [ 227 | "512, 512, 512 # 512x512\n", 228 | "768, 768, 768 # 768x768\n", 229 | "1024, 1024, 1024 # 1024x1024\n", 230 | "1280, 1280, 1280 # 1280x1280\n", 231 | "1536, 1536, 1536 # 1536x1536\n", 232 | "2048, 2048, 2048 # 2048x2048", 233 | ] 234 | with open(filename, "w", encoding="utf-8") as f: 235 | f.writelines(resolutions) 236 | 237 | def write_js_titles_file(button_titles): 238 | filename = Path(BASE_PATH, "javascript", "button_titles.js") 239 | content = ["// Do not put custom titles here. This file is overwritten each time the WebUI is started.\n"] 240 | content.append("arsp__ar_button_titles = {\n") 241 | counter = 0 242 | while counter < len(button_titles[0]): 243 | content.append(f' " {button_titles[0][counter]}" : "{button_titles[1][counter]}",\n') 244 | counter = counter + 1 245 | content.append("}") 246 | 247 | with open(filename, "w", encoding="utf-8") as f: 248 | f.writelines(content) 249 | 250 | class AspectRatioScript(scripts.Script): 251 | def read_aspect_ratios(self): 252 | ar_file = Path(BASE_PATH, "aspect_ratios.txt") 253 | if not ar_file.exists(): 254 | write_aspect_ratios_file(ar_file) 255 | (self.aspect_ratios, self.aspect_ratio_comments, self.flipped_vals) = parse_aspect_ratios_file("aspect_ratios.txt") 256 | self.ar_buttons_labels = self.aspect_ratios 257 | 258 | def read_resolutions(self): 259 | res_file = Path(BASE_PATH, "resolutions.txt") 260 | if not res_file.exists(): 261 | write_resolutions_file(res_file) 262 | 263 | self.res_labels, res, self.res_comments = parse_resolutions_file("resolutions.txt") 264 | self.res = [list(map(int, r)) for r in res] 265 | 266 | def title(self): 267 | return "Aspect Ratio picker" 268 | 269 | def show(self, is_img2img): 270 | return scripts.AlwaysVisible 271 | 272 | def ui(self, is_img2img): 273 | self.LOCK_OPEN_ICON = "\U0001F513" # 🔓 274 | self.LOCK_CLOSED_ICON = "\U0001F512" # 🔒 275 | self.LAND_AR_ICON = "\U000025AD" # ▭ 276 | self.PORT_AR_ICON = "\U000025AF" # ▯ 277 | self.INFO_ICON = "\U00002139" # ℹ 278 | self.INFO_CLOSE_ICON = "\U00002BC5" # ⯅ 279 | self.OFFSET_ICON = "\U00002B83" # ⮃ 280 | self.ONE_DIM_ICON = "\U00002B85" # ⮅ 281 | 282 | # Determine the width and height based on the mode (img2img or txt2img) 283 | if is_img2img: 284 | w = self.i2i_w 285 | h = self.i2i_h 286 | else: 287 | w = self.t2i_w 288 | h = self.t2i_h 289 | 290 | # Average number box initialize without rendering 291 | arc_average = gr.Number(label="Current W/H Avg.", value=0, interactive=False, render=False) 292 | 293 | # Precision input box initialize without rendering 294 | arc_prec = gr.Number(label="Precision (px)", value=64, minimum=4, maximum=128, step=4, precision=0, render=False) 295 | 296 | with gr.Accordion(label="Aspect Ratio and Resolution Buttons", open=True): 297 | 298 | with gr.Column(elem_id=f'arsp__{"img" if is_img2img else "txt"}2img_container_aspect_ratio'): 299 | 300 | # Get aspect ratios from file 301 | self.read_aspect_ratios() 302 | 303 | # Top row 304 | with gr.Row(elem_id=f'arsp__{"img" if is_img2img else "txt"}2img_row_aspect_ratio'): 305 | 306 | # Lock button 307 | arc_lock = ToolButton(value=self.LOCK_OPEN_ICON, visible=True, variant="secondary", elem_id="arsp__arc_lock_button") 308 | # Lock button click event handling 309 | def toggle_lock(icon, avg, w=512, h=512): 310 | icon = self.LOCK_OPEN_ICON if (img2img_state.is_locked if is_img2img else txt2img_state.is_locked) else self.LOCK_CLOSED_ICON 311 | if is_img2img: 312 | img2img_state.toggle_lock() 313 | else: 314 | txt2img_state.toggle_lock() 315 | if not avg: 316 | avg = avg_from_dims(w, h) 317 | return icon, avg 318 | if is_img2img: 319 | lock_w = self.i2i_w 320 | lock_h = self.i2i_h 321 | else: 322 | lock_w = self.t2i_w 323 | lock_h = self.t2i_h 324 | # Lock button event listener 325 | arc_lock.click(toggle_lock, 326 | inputs = [arc_lock, arc_average, lock_w, lock_h], 327 | outputs = [arc_lock, arc_average], 328 | show_progress = 'hidden') 329 | 330 | # Initialize Aspect Ratio buttons (render=False) 331 | ar_buttons, ar_functions = create_ar_buttons(self.ar_buttons_labels, is_img2img) 332 | 333 | # Switch button 334 | arc_switch = ToolButton(value=self.LAND_AR_ICON, visible=True, variant="secondary", elem_id="arsp__arc_switch_button") 335 | # Switch button click event handling 336 | def toggle_switch(*items, **kwargs): 337 | ar_icons = items[:-1] 338 | sw_icon = items[-1] 339 | if ar_icons == tuple(self.aspect_ratios): 340 | ar_icons = tuple(self.flipped_vals) 341 | else: 342 | ar_icons = tuple(self.aspect_ratios) 343 | sw_icon = self.PORT_AR_ICON if sw_icon == self.LAND_AR_ICON else self.LAND_AR_ICON 344 | if is_img2img: 345 | img2img_state.toggle_switch() 346 | else: 347 | txt2img_state.toggle_switch() 348 | return (*ar_icons, sw_icon) 349 | # Switch button event listener 350 | arc_switch.click(toggle_switch, 351 | inputs = ar_buttons+[arc_switch], 352 | outputs = ar_buttons+[arc_switch]) 353 | 354 | # AR buttons render 355 | for button in ar_buttons: 356 | button.render() 357 | # AR buttons click event handling 358 | with contextlib.suppress(AttributeError): 359 | for button in ar_buttons: 360 | # AR buttons event listener 361 | button.click(ar_functions[button], 362 | inputs = [arc_average, arc_prec, w, h], 363 | outputs = [arc_average, w, h], 364 | show_progress = 'hidden') 365 | 366 | # Get static resolutions from file 367 | self.read_resolutions() 368 | 369 | # Bottom row 370 | with gr.Row(elem_id=f'arsp__{"img" if is_img2img else "txt"}2img_row_resolutions'): 371 | 372 | # Info button to toggle info window 373 | arc_show_info = ToolButton(value=self.INFO_ICON, visible=True, variant="secondary", elem_id="arsp__arc_show_info_button") 374 | arc_hide_info = ToolButton(value=self.INFO_CLOSE_ICON, visible=False, variant="secondary", elem_id="arsp__arc_hide_info_button") 375 | ### Click event handling for info window ### 376 | ##### is defined after everything else ##### 377 | 378 | # Mode button 379 | arc_mode = ToolButton(value=self.OFFSET_ICON, visible=True, variant="secondary", elem_id="arsp__arc_mode_button") 380 | # Mode button click event handling 381 | def toggle_mode(icon): 382 | icon = self.ONE_DIM_ICON if icon == self.OFFSET_ICON else self.OFFSET_ICON 383 | if is_img2img: 384 | img2img_state.toggle_mode() 385 | else: 386 | txt2img_state.toggle_mode() 387 | return icon 388 | # Mode button event listener 389 | arc_mode.click(toggle_mode, 390 | inputs = [arc_mode], 391 | outputs = [arc_mode]) 392 | 393 | # Static res buttons 394 | buttons, set_res_functions = create_res_buttons(zip(self.res, self.res_labels), is_img2img) 395 | 396 | # Set up click event listeners for the buttons 397 | with contextlib.suppress(AttributeError): 398 | for button in buttons: 399 | button.click(set_res_functions[button], 400 | inputs = [arc_average], 401 | outputs = [arc_average, w, h], 402 | show_progress = 'hidden') 403 | 404 | # Write button_titles.js with labels and comments read from aspect ratios and resolutions files 405 | button_titles = [self.aspect_ratios + self.res_labels] 406 | button_titles.append(self.aspect_ratio_comments + self.res_comments) 407 | write_js_titles_file(button_titles) 408 | 409 | # Information panel 410 | with gr.Column(visible=False, variant="panel", elem_id="arsp__arc_panel") as arc_panel: 411 | with gr.Row(): 412 | with gr.Column(scale=2, min_width=100): 413 | 414 | # Average number box render 415 | arc_average.render() 416 | 417 | # Precision input box render 418 | arc_prec.render() 419 | 420 | # Information blurb 421 | gr.Column(scale=1, min_width=10) 422 | with gr.Column(scale=12): 423 | arc_title_heading = gr.Markdown(value= 424 | ''' 425 | ### AR and Static Res buttons can be customized in the 'aspect_ratios.txt' and 'resolutions.txt' files 426 | **Aspect Ratio buttons (Top Row)**: 427 | (1) Averages the current width/height in the UI; (2) Offsets to the exact aspect ratio; (3) Rounds to precision. 428 | 429 | **Static Resolution buttons (Bottom Row)**: 430 | Recommended to use 1:1 values for these, to serve as a start point before clicking AR buttons. 431 | 432 | **64px Precision is recommended, the same rounding applied for image "bucketing" when model training.** 433 | ''' 434 | ) 435 | 436 | # Info panel event listeners 437 | arc_show_info.click( 438 | lambda: [ 439 | gr.update(visible=True), 440 | gr.update(visible=False), 441 | gr.update(visible=True), 442 | ], 443 | None, 444 | [ 445 | arc_panel, 446 | arc_show_info, 447 | arc_hide_info, 448 | ], 449 | ) 450 | arc_hide_info.click( 451 | lambda: [ 452 | gr.update(visible=False), 453 | gr.update(visible=True), 454 | gr.update(visible=False), 455 | ], 456 | None, 457 | [arc_panel, arc_show_info, arc_hide_info], 458 | ) 459 | 460 | ## Function to update the values in appropriate Width/Height fields 461 | # https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/7456#issuecomment-1414465888 462 | def after_component(self, component, **kwargs): 463 | if kwargs.get("elem_id") == "txt2img_width": 464 | self.t2i_w = component 465 | if kwargs.get("elem_id") == "txt2img_height": 466 | self.t2i_h = component 467 | if kwargs.get("elem_id") == "img2img_width": 468 | self.i2i_w = component 469 | if kwargs.get("elem_id") == "img2img_height": 470 | self.i2i_h = component 471 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | #arsp__txt2img_container_aspect_ratio, 2 | #arsp__img2img_container_aspect_ratio { 3 | gap: 5px !important; 4 | margin-top: 3px; 5 | margin-bottom: 3px; 6 | } 7 | 8 | #arsp__txt2img_row_aspect_ratio, 9 | #arsp__txt2img_row_resolutions, 10 | #arsp__img2img_row_aspect_ratio, 11 | #arsp__img2img_row_resolutions { 12 | gap: 8px; 13 | } 14 | 15 | #arsp__txt2img_row_aspect_ratio button, 16 | #arsp__txt2img_row_resolutions button, 17 | #arsp__img2img_row_aspect_ratio button, 18 | #arsp__img2img_row_resolutions button { 19 | gap: 10px !important; 20 | max-width: unset !important; 21 | } 22 | 23 | button#arsp__arc_lock_button, 24 | button#arsp__arc_mode_button, 25 | button#arsp__arc_switch_button, 26 | button#arsp__arc_show_info_button, 27 | button#arsp__arc_hide_info_button { 28 | max-width: 40px !important; 29 | min-width: unset !important; 30 | padding: var(--size-0-5) var(--size-2) !important; 31 | } 32 | 33 | /* TODO: Use gradio instead if posible */ 34 | #arsp__arc_ar_display_text { 35 | height: 22px; 36 | overflow: hidden !important; 37 | } 38 | 39 | /* Fix for Lobe theme */ 40 | /* https://github.com/canisminor1990/sd-webui-lobe-theme/blob/d4c4a48e8025d32b202716d4ab8d806580752256/src/styles/components/button.ts */ 41 | .hide.svelte-1ipelgc { 42 | display: none !important; 43 | } 44 | .primary.svelte-1ipelgc { 45 | background: var(--button-primary-background-fill) !important; 46 | } 47 | .secondary.svelte-1ipelgc { 48 | background: var(--button-secondary-background-fill) !important; 49 | } 50 | --------------------------------------------------------------------------------