Repository: AUTOMATIC1111/stable-diffusion-webui

Issue:
[Bug]: DecompressionBombError using X/Y/Z script
### Is there an existing issue for this?

- [X] I have searched the existing issues and checked the recent builds/commits

### What happened?

When you are get large amount of combinations in X/Y/Z script, you'll get an error like this:
```python
PIL.Image.DecompressionBombError: Image size (231258240 pixels) exceeds limit of 178956970 pixels, could be decompression bomb DOS attack.
```

This is protection from underlying Pillow library and the reasons described here:
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.open

### Possible solutions:
- Disable check by setting PIL.Image.MAX_IMAGE_PIXELS=None (cons that grid can generate huge unusable images)
- Split large grids to chunks

### Steps to reproduce the problem

1. Go to txt2img tab
2. Select X/Y/Z script
3. Enter a large number of parameters, e.g 
- Steps: 1
- Seed: 1-100000


### What should have happened?

Grid should be generated

### Commit where the problem happens

0cc0ee1bcb4c24a8c9715f66cede06601bfc00c8

### What platforms do you use to access the UI ?

_No response_

### What browsers do you use to access the UI ?

_No response_

### Command Line Arguments

```Shell
Any
```


### List of extensions

Any

### Console logs

```Python
Traceback (most recent call last):
  File "/stable-diffusion-webui/modules/call_queue.py", line 56, in f
    res = list(func(*args, **kwargs))
  File "/stable-diffusion-webui/modules/call_queue.py", line 37, in f
    res = func(*args, **kwargs)
  File "/stable-diffusion-webui/modules/txt2img.py", line 53, in txt2img
    processed = modules.scripts.scripts_txt2img.run(p, *args)
  File "/stable-diffusion-webui/modules/scripts.py", line 376, in run
    processed = script.run(p, *script_args)
  File "/stable-diffusion-webui/scripts/xyz_grid.py", line 596, in run
    processed, sub_grids = draw_xyz_grid(
  File "/stable-diffusion-webui/scripts/xyz_grid.py", line 309, in draw_xyz_grid
    z_grid = images.draw_grid_annotations(z_grid, sub_grid_size[0], sub_grid_size[1], title_texts, [[images.GridAnnotation()]])
  File "/stable-diffusion-webui/modules/images.py", line 206, in draw_grid_annotations
    cell = im.crop((width * col, height * row, width * (col+1), height * (row+1)))
  File "/stable-diffusion-webui/venv/lib/python3.10/site-packages/PIL/Image.py", line 1234, in crop
    return self._new(self._crop(self.im, box))
  File "stable-diffusion-webui/venv/lib/python3.10/site-packages/PIL/Image.py", line 1252, in _crop
    _decompression_bomb_check(absolute_values)
  File "/stable-diffusion-webui/venv/lib/python3.10/site-packages/PIL/Image.py", line 3164, in _decompression_bomb_check
    raise DecompressionBombError(msg)
PIL.Image.DecompressionBombError: Image size (231258240 pixels) exceeds limit of 178956970 pixels, could be decompression bomb DOS attack.
```


### Additional information

_No response_


Discussion:
[Connected discussion](https://github.com/AUTOMATIC1111/stable-diffusion-webui/discussions/8051)

Resolving patch:
diff --git a/modules/shared.py b/modules/shared.py
--- a/modules/shared.py
+++ b/modules/shared.py
@@ -330,6 +330,7 @@ def list_samplers():
     "export_for_4chan": OptionInfo(True, "If the saved image file size is above the limit, or its either width or height are above the limit, save a downscaled copy as JPG"),
     "img_downscale_threshold": OptionInfo(4.0, "File size limit for the above option, MB", gr.Number),
     "target_side_length": OptionInfo(4000, "Width/height limit for the above option, in pixels", gr.Number),
+    "img_max_size_mp": OptionInfo(200, "Maximum image size, in megapixels", gr.Number),
 
     "use_original_name_batch": OptionInfo(True, "Use original name for output filename during batch process in extras tab"),
     "use_upscaler_name_as_suffix": OptionInfo(False, "Use upscaler name as filename suffix in the extras tab"),
diff --git a/scripts/xyz_grid.py b/scripts/xyz_grid.py
--- a/scripts/xyz_grid.py
+++ b/scripts/xyz_grid.py
@@ -484,6 +484,11 @@ def process_axis(opt, vals):
         z_opt = self.current_axis_options[z_type]
         zs = process_axis(z_opt, z_values)
 
+        # this could be moved to common code, but unlikely to be ever triggered anywhere else
+        Image.MAX_IMAGE_PIXELS = opts.img_max_size_mp * 1.1 # allow 10% overhead for margins and legend
+        grid_mp = round(len(xs) * len(ys) * len(zs) * p.width * p.height / 1000000)
+        assert grid_mp < opts.img_max_size_mp, f'Error: Resulting grid would be too large ({grid_mp} MPixels) (max configured size is {opts.img_max_size_mp} MPixels)'
+
         def fix_axis_seeds(axis_opt, axis_list):
             if axis_opt.label in ['Seed', 'Var. seed']:
                 return [int(random.randrange(4294967294)) if val is None or val == '' or val == -1 else val for val in axis_list]


Test patch: