diff --git a/CHANGELOG.md b/CHANGELOG.md index c4d97ffee..3ce6e547a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,18 +31,20 @@ - Use multi-threading for 5x load speedup - Better Lora trigger words support - Auto refresh styles on change -- **General**: +- **General** - Reworked parser when pasting previously generated images/prompts includes all `txt2img`, `img2img` and `override` params - Reworked **model compile** - - Add refiner options to XYZ Grid - - Add option to create only subimages in XYZ grid, thanks @midcoastal - Support custom upscalers in subfolders - Add additional image info when loading image in process tab - Better file locking when sharing config and/or models between multiple instances - Handle custom API endpoints when using auth - Show logged in user in log when accessing via UI and/or API - Support `--ckpt none` to skip loading a model +- **XYZ grid** + - Add refiner options to XYZ Grid + - Add option to create only subimages in XYZ grid, thanks @midcoastal + - Allow custom font, background and text color in settings - **Fixes** - Fix `params.txt` saved before actual image - Fix inpaint diff --git a/modules/images.py b/modules/images.py index 0d5d74de9..652551b08 100644 --- a/modules/images.py +++ b/modules/images.py @@ -56,7 +56,7 @@ def image_grid(imgs, batch_size=1, rows=None): params = script_callbacks.ImageGridLoopParams(imgs, cols, rows) script_callbacks.image_grid_callback(params) w, h = imgs[0].size - grid = Image.new('RGB', size=(params.cols * w, params.rows * h), color='black') + grid = Image.new('RGB', size=(params.cols * w, params.rows * h), color=shared.opts.grid_background) for i, img in enumerate(params.imgs): grid.paste(img, box=(i % params.cols * w, i // params.cols * h)) return grid @@ -141,37 +141,36 @@ def draw_grid_annotations(im, width, height, hor_texts, ver_texts, margin=0, tit def draw_texts(drawing: ImageDraw, draw_x, draw_y, lines, initial_fnt, initial_fontsize): for line in lines: - fnt = initial_fnt + font = initial_fnt fontsize = initial_fontsize - while drawing.multiline_textsize(line.text, font=fnt)[0] > line.allowed_width and fontsize > 0: - fontsize -= 1 - fnt = get_font(fontsize) - drawing.multiline_text((draw_x, draw_y + line.size[1] / 2), line.text, font=fnt, fill=color_active if line.is_active else color_inactive, anchor="mm", align="center") + while drawing.multiline_textsize(line.text, font=font)[0] > line.allowed_width and fontsize > 0: + fontsize -= 2 + font = get_font(fontsize) + drawing.multiline_text((draw_x, draw_y + line.size[1] / 2), line.text, font=font, fill=shared.opts.font_color if line.is_active else color_inactive, anchor="mm", align="center") if not line.is_active: drawing.line((draw_x - line.size[0] // 2, draw_y + line.size[1] // 2, draw_x + line.size[0] // 2, draw_y + line.size[1] // 2), fill=color_inactive, width=4) draw_y += line.size[1] + line_spacing fontsize = (width + height) // 25 line_spacing = fontsize // 2 - fnt = get_font(fontsize) - color_active = (0, 0, 0) - color_inactive = (153, 153, 153) + font = get_font(fontsize) + color_inactive = (127, 127, 127) pad_left = 0 if sum([sum([len(line.text) for line in lines]) for lines in ver_texts]) == 0 else width * 3 // 4 cols = im.width // width rows = im.height // height assert cols == len(hor_texts), f'bad number of horizontal texts: {len(hor_texts)}; must be {cols}' assert rows == len(ver_texts), f'bad number of vertical texts: {len(ver_texts)}; must be {rows}' - calc_img = Image.new("RGB", (1, 1), "white") + calc_img = Image.new("RGB", (1, 1), shared.opts.grid_background) calc_d = ImageDraw.Draw(calc_img) title_texts = [title] if title else [[GridAnnotation()]] for texts, allowed_width in zip(hor_texts + ver_texts + title_texts, [width] * len(hor_texts) + [pad_left] * len(ver_texts) + [(width+margin)*cols]): items = [] + texts texts.clear() for line in items: - wrapped = wrap(calc_d, line.text, fnt, allowed_width) + wrapped = wrap(calc_d, line.text, font, allowed_width) texts += [GridAnnotation(x, line.is_active) for x in wrapped] for line in texts: - bbox = calc_d.multiline_textbbox((0, 0), line.text, font=fnt) + bbox = calc_d.multiline_textbbox((0, 0), line.text, font=font) line.size = (bbox[2] - bbox[0], bbox[3] - bbox[1]) line.allowed_width = allowed_width hor_text_heights = [sum([line.size[1] + line_spacing for line in lines]) - line_spacing for lines in hor_texts] @@ -181,7 +180,7 @@ def draw_grid_annotations(im, width, height, hor_texts, ver_texts, margin=0, tit if title: title_text_heights = [sum([line.size[1] + line_spacing for line in lines]) - line_spacing for lines in title_texts] # pylint: disable=unsubscriptable-object title_pad = 0 if sum(title_text_heights) == 0 else max(title_text_heights) + line_spacing * 2 - result = Image.new("RGB", (im.width + pad_left + margin * (cols-1), im.height + pad_top + title_pad + margin * (rows-1)), "white") + result = Image.new("RGB", (im.width + pad_left + margin * (cols-1), im.height + pad_top + title_pad + margin * (rows-1)), shared.opts.grid_background) for row in range(rows): for col in range(cols): cell = im.crop((width * col, height * row, width * (col+1), height * (row+1))) @@ -190,15 +189,15 @@ def draw_grid_annotations(im, width, height, hor_texts, ver_texts, margin=0, tit if title: x = pad_left + ((width+margin)*cols) / 2 y = title_pad / 2 - title_text_heights[0] / 2 - draw_texts(d, x, y, title_texts[0], fnt, fontsize) + draw_texts(d, x, y, title_texts[0], font, fontsize) for col in range(cols): x = pad_left + (width + margin) * col + width / 2 y = (pad_top / 2 - hor_text_heights[col] / 2) + title_pad - draw_texts(d, x, y, hor_texts[col], fnt, fontsize) + draw_texts(d, x, y, hor_texts[col], font, fontsize) for row in range(rows): x = pad_left / 2 y = (pad_top + (height + margin) * row + height / 2 - ver_text_heights[row] / 2) + title_pad - draw_texts(d, x, y, ver_texts[row], fnt, fontsize) + draw_texts(d, x, y, ver_texts[row], font, fontsize) return result diff --git a/modules/shared.py b/modules/shared.py index 2ab35e858..2b28f5eb5 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -419,6 +419,9 @@ options_templates.update(options_section(('saving-images', "Image Options"), { "grid_save": OptionInfo(True, "Always save all generated image grids"), "grid_format": OptionInfo('jpg', 'File format for grids', gr.Dropdown, {"choices": ["jpg", "png", "webp", "tiff", "jp2"]}), "n_rows": OptionInfo(-1, "Grid row count", gr.Slider, {"minimum": -1, "maximum": 16, "step": 1}), + "grid_background": OptionInfo("#000000", "Grid background color", ui_components.FormColorPicker, {}), + "font": OptionInfo("", "Font file"), + "font_color": OptionInfo("#FFFFFF", "Font color", ui_components.FormColorPicker, {}), "save_sep_options": OptionInfo("

Intermediate Image Saving

", "", gr.HTML), "save_init_img": OptionInfo(False, "Save copy of img2img init images"), @@ -470,7 +473,6 @@ options_templates.update(options_section(('ui', "User Interface"), { "disable_weights_auto_swap": OptionInfo(True, "Do not change selected model when reading generation parameters"), "send_seed": OptionInfo(True, "Send seed when sending prompt or image to other interface"), "send_size": OptionInfo(True, "Send size when sending prompt or image to another interface"), - "font": OptionInfo("", "Font for image grids that have text"), "keyedit_precision_attention": OptionInfo(0.1, "Ctrl+up/down precision when editing (attention:1.1)", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001, "visible": False}), "keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing ", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001, "visible": False}), "keyedit_delimiters": OptionInfo(".,\/!?%^*;:{}=`~()", "Ctrl+up/down word delimiters", gr.Textbox, { "visible": False }), # pylint: disable=anomalous-backslash-in-string