Repository: gradio-app/gradio

Issue:
Canvas drawing is cut off when the shape is explicitly set to be a square
- [x] I have searched to see if a similar issue already exists.


**Is your feature request related to a problem? Please describe.**  

Right now if we want to make a 512\*512 drawing canvas, the only method is (1) ask users to create a pure white 512\*512 image using third-party software like PhotoShop, then (2) ask users to import that blank image into gr.Image(source='upload', tool='sketch'), then (3) use the resulting mask as the user drawing for any applications. (Besides, the initial width of scribble can not be set by code.)

This is over-complicated. We should have an one-line function to make a simple drawing canvas.

**Describe the solution you'd like**  

We may consider something like

gr.Image(source='blank', tool='sketch', size=(512, 512))

UI crashes in `gr.Image` with `tool="color-sketch"` and a pre-loaded image
### Describe the bug

Running the following code
```py
#Create a black image
img = Image.new("RGB", (512, 512), (0, 0, 0))
img.save("image.png", "PNG")
#Pre-load the black image in the component
gr.Image(value="image.png", interactive=True, tool="color-sketch")
```
Crashes the UI
<img width="635" alt="image" src="https://user-images.githubusercontent.com/788417/220176049-977ac494-1587-41a8-aafb-b2a961131925.png">

### Reproduction

Here's a colab with an example: https://colab.research.google.com/drive/1Gp5pp51P14B0tM331dS7476-Oqe-Bi04


### System Info

```shell
3.19.0
```


### Severity

serious, but I can work around it
Canvas drawing is cut off when the shape is explicitly set to be a square
- [x] I have searched to see if a similar issue already exists.


**Is your feature request related to a problem? Please describe.**  

Right now if we want to make a 512\*512 drawing canvas, the only method is (1) ask users to create a pure white 512\*512 image using third-party software like PhotoShop, then (2) ask users to import that blank image into gr.Image(source='upload', tool='sketch'), then (3) use the resulting mask as the user drawing for any applications. (Besides, the initial width of scribble can not be set by code.)

This is over-complicated. We should have an one-line function to make a simple drawing canvas.

**Describe the solution you'd like**  

We may consider something like

gr.Image(source='blank', tool='sketch', size=(512, 512))



Discussion:
Hi @lllyasviel this is already possible by doing:

```py
gr.Image(source="canvas", shape=(512, 512))
```

For example, you can test this with this Blocks demo:

```py
import gradio as gr

with gr.Blocks() as demo:
    i = gr.Image(source="canvas", shape=(512, 512))
    o = gr.Image()
    i.change(lambda x:x, i, o)

demo.launch()
```
@abidlabs 
No. Right now gradio does not support a simple 512\*512 drawing canvas.

    pip install --upgrade gradio
 
then 

    
![image](https://user-images.githubusercontent.com/19834515/218083430-e759b194-ed1d-41e8-a5df-d79fc1b69e2b.png)

Then the result will be like

![image](https://user-images.githubusercontent.com/19834515/218083524-119b667b-1bf0-4383-afea-ffe64bcc2411.png)

As we can see, this has nothing to do with "shape=(512, 512)", the shape parameter does not control the resolution, it controls a "gradio-style dpi". You can even draw in this long rectangular:

![image](https://user-images.githubusercontent.com/19834515/218084162-ce754d49-4962-4631-8ff3-a58e0c4452f7.png)

When it is processed, the results provide strong evidence that "shape" is related to dpi and cropping, not shape of canvas

![image](https://user-images.githubusercontent.com/19834515/218084453-eeb0aed8-3d7c-48ff-af7d-2128ee1bdde5.png)

If this is an intentional design, it may be worthwhile to reopen this issue to target a more straightforward drawing board.

Hi @lllyasviel so to achieve what you want, I think we need to do control both the image resolution (with is controlled via the `shape` parameter above) and the display size of the Image component, which is controlled via the `.style()` method of the `Image` component. I agree that it's a bit confusing, but I think this should achieve what you want:

```py
import gradio as gr

with gr.Blocks() as demo:
    i = gr.Image(source="canvas", shape=(512, 512)).style(width=512, height=512)
    o = gr.Image().style(width=512, height=512)
    i.change(lambda x:x, i, o)

demo.launch()
```

However, when I tested this, I got the canvas being cut off at the halfway point, preventing me from drawing in the bottom half of the canvas, which is very strange. 

<img width="457" alt="image" src="https://user-images.githubusercontent.com/1778297/218155557-8f766207-6bd1-4848-9344-23380dd41a51.png">


I'm going to reopen this issue so that we can fix this. cc @pngwn 
The UI equally crashes if I do something like: 
```py
def update_image():
  return gr.update(value="image.png")

image = gr.Image(interactive=True, tool="color-sketch")
# ... 
demo.load(update_image, inputs=[], outputs=image)
```

And given that the `source="canvas"` can only initialize with a white canvas, I'm currently failing to see any way to initialise a `color-sketch` with a black image to be drawn on top 

Actually uploading the image **does** work tho.
This is due to a resize loop, the canvas gets too big and eventually crashes. Surprised it isn't happening when uploading an image though.
Hi @lllyasviel this is already possible by doing:

```py
gr.Image(source="canvas", shape=(512, 512))
```

For example, you can test this with this Blocks demo:

```py
import gradio as gr

with gr.Blocks() as demo:
    i = gr.Image(source="canvas", shape=(512, 512))
    o = gr.Image()
    i.change(lambda x:x, i, o)

demo.launch()
```
@abidlabs 
No. Right now gradio does not support a simple 512\*512 drawing canvas.

    pip install --upgrade gradio
 
then 

    
![image](https://user-images.githubusercontent.com/19834515/218083430-e759b194-ed1d-41e8-a5df-d79fc1b69e2b.png)

Then the result will be like

![image](https://user-images.githubusercontent.com/19834515/218083524-119b667b-1bf0-4383-afea-ffe64bcc2411.png)

As we can see, this has nothing to do with "shape=(512, 512)", the shape parameter does not control the resolution, it controls a "gradio-style dpi". You can even draw in this long rectangular:

![image](https://user-images.githubusercontent.com/19834515/218084162-ce754d49-4962-4631-8ff3-a58e0c4452f7.png)

When it is processed, the results provide strong evidence that "shape" is related to dpi and cropping, not shape of canvas

![image](https://user-images.githubusercontent.com/19834515/218084453-eeb0aed8-3d7c-48ff-af7d-2128ee1bdde5.png)

If this is an intentional design, it may be worthwhile to reopen this issue to target a more straightforward drawing board.

Hi @lllyasviel so to achieve what you want, I think we need to do control both the image resolution (with is controlled via the `shape` parameter above) and the display size of the Image component, which is controlled via the `.style()` method of the `Image` component. I agree that it's a bit confusing, but I think this should achieve what you want:

```py
import gradio as gr

with gr.Blocks() as demo:
    i = gr.Image(source="canvas", shape=(512, 512)).style(width=512, height=512)
    o = gr.Image().style(width=512, height=512)
    i.change(lambda x:x, i, o)

demo.launch()
```

However, when I tested this, I got the canvas being cut off at the halfway point, preventing me from drawing in the bottom half of the canvas, which is very strange. 

<img width="457" alt="image" src="https://user-images.githubusercontent.com/1778297/218155557-8f766207-6bd1-4848-9344-23380dd41a51.png">


I'm going to reopen this issue so that we can fix this. cc @pngwn 

Resolving patch:
diff --git a/demo/blocks_flipper/run.py b/demo/blocks_flipper/run.py
--- a/demo/blocks_flipper/run.py
+++ b/demo/blocks_flipper/run.py
@@ -1,12 +1,15 @@
 import numpy as np
 import gradio as gr
 
+
 def flip_text(x):
     return x[::-1]
 
+
 def flip_image(x):
     return np.fliplr(x)
 
+
 with gr.Blocks() as demo:
     gr.Markdown("Flip text or image files using this demo.")
     with gr.Tab("Flip Text"):
@@ -24,6 +27,6 @@ def flip_image(x):
 
     text_button.click(flip_text, inputs=text_input, outputs=text_output)
     image_button.click(flip_image, inputs=image_input, outputs=image_output)
-    
+
 if __name__ == "__main__":
-    demo.launch()
\ No newline at end of file
+    demo.launch()
diff --git a/demo/blocks_mask/run.py b/demo/blocks_mask/run.py
--- a/demo/blocks_mask/run.py
+++ b/demo/blocks_mask/run.py
@@ -1,5 +1,6 @@
 import gradio as gr
 from gradio.components import Markdown as md
+from PIL import Image
 
 demo = gr.Blocks()
 
@@ -8,6 +9,9 @@
 
 io2a = gr.Interface(lambda x: x, gr.Image(source="canvas"), gr.Image())
 io2b = gr.Interface(lambda x: x, gr.Sketchpad(), gr.Image())
+io2c = gr.Interface(
+    lambda x: x, gr.Image(source="canvas", shape=(512, 512)), gr.Image()
+)
 
 io3a = gr.Interface(
     lambda x: [x["mask"], x["image"]],
@@ -53,6 +57,20 @@
 )
 
 
+def save_image(image):
+    image.save("colorede.png")
+    return image
+
+
+img = Image.new("RGB", (512, 512), (150, 150, 150))
+img.save("image.png", "PNG")
+
+io5d = gr.Interface(
+    save_image,
+    gr.Image("image.png", source="upload", tool="color-sketch", type="pil"),
+    gr.Image(),
+)
+
 with demo:
     md("# Different Ways to Use the Image Input Component")
     md(
@@ -71,6 +89,8 @@
         "**2b. Black and White Sketchpad: `gr.Interface(lambda x: x, gr.Sketchpad(), gr.Image())`**"
     )
     io2b.render()
+    md("**2c. Black and White Sketchpad with `shape=(512,512)`**")
+    io2c.render()
     md("**3a. Binary Mask with image upload:**")
     md(
         """```python
@@ -130,7 +150,8 @@
         io3b2.render()
     with gr.Tab("Two"):
         io3b3.render()
-
+    md("**5d. Color Sketchpad with image upload and a default images**")
+    io5d.render()
 
 if __name__ == "__main__":
     demo.launch()
diff --git a/gradio/components.py b/gradio/components.py
--- a/gradio/components.py
+++ b/gradio/components.py
@@ -1379,6 +1379,7 @@ def __init__(
         streaming: bool = False,
         elem_id: str | None = None,
         mirror_webcam: bool = True,
+        brush_radius: int | None = None,
         **kwargs,
     ):
         """
@@ -1398,7 +1399,9 @@ def __init__(
             streaming: If True when used in a `live` interface, will automatically stream webcam feed. Only valid is source is 'webcam'.
             elem_id: An optional string that is assigned as the id of this component in the HTML DOM. Can be used for targeting CSS styles.
             mirror_webcam: If True webcam will be mirrored. Default is True.
+            brush_radius: Size of the brush for Sketch. Default is None which chooses a sensible default
         """
+        self.brush_radius = brush_radius
         self.mirror_webcam = mirror_webcam
         valid_types = ["numpy", "pil", "filepath"]
         if type not in valid_types:
@@ -1446,6 +1449,7 @@ def get_config(self):
             "value": self.value,
             "streaming": self.streaming,
             "mirror_webcam": self.mirror_webcam,
+            "brush_radius": self.brush_radius,
             **IOComponent.get_config(self),
         }
 
@@ -1456,6 +1460,7 @@ def update(
         show_label: bool | None = None,
         interactive: bool | None = None,
         visible: bool | None = None,
+        brush_radius: int | None = None,
     ):
         updated_config = {
             "label": label,
@@ -1463,6 +1468,7 @@ def update(
             "interactive": interactive,
             "visible": visible,
             "value": value,
+            "brush_radius": brush_radius,
             "__type__": "update",
         }
         return IOComponent.add_interactive_to_config(updated_config, interactive)


Test patch:
diff --git a/test/test_components.py b/test/test_components.py
--- a/test/test_components.py
+++ b/test/test_components.py
@@ -625,6 +625,7 @@ def test_component_functions(self):
             source="upload", tool="editor", type="pil", label="Upload Your Image"
         )
         assert image_input.get_config() == {
+            "brush_radius": None,
             "image_mode": "RGB",
             "shape": None,
             "source": "upload",