Canvas

Drawlib manages the canvas, allowing you to draw various elements such as lines and shapes onto it. Once your drawing is complete, you can save it as an image.

The canvas is a pivotal concept in drawlib, although it remains internal and isn’t directly exposed to users. Below is a succinct overview of drawlib’s canvas architecture:

../../../_images/image_architecture1.png

Canvas architecture

In this architecture, drawlib internally incorporates core functions and methods that are accessible to users through APIs defined in drawlib.apis. When you invoke APIs like config(), these functions internally interact with the canvas state.

For example, using the public API config(width=200, height=100) triggers an internal method that adjusts the canvas’s dimensions to 200 pixels wide and 100 pixels high. Similarly, calling the circle(...) API invokes an internal method that adds a circle to the canvas.

While it’s technically possible to create your own instance of the Canvas and perform drawing operations, this approach isn’t recommended. This is because functionalities such as file handling and theme settings are integrated with drawlib’s internal canvas management. Our architecture is designed to ensure:

  • Accessibility for users unfamiliar with Python and object-oriented programming.

  • Ease of learning and use.

  • Simplicity akin to scripting for drawing operations.

config()

The config() API in drawlib manages various canvas configurations, encompassing:

  • Size (width, height): Specifies the dimensions of the canvas in coordinate units.

  • DPI (resolution): Sets the Dots Per Inch for the canvas, influencing image clarity.

  • Grid: Enables a grid overlay on the canvas for visual alignment assistance.

  • Canvas color (background color): Defines the background color of the canvas.

  • Theme: Applies predefined styles across drawings, including item colors and line thicknesses.

We’ll start with an exploration of grid settings, as they form the foundational basis and are used in conjunction with other configuration options.

Configuring Canvas Grid

During the process of creating illustrations, you may find yourself running your code multiple times to check how your changes affect the resulting image. As you iterate through this write/check/fix cycle, displaying a grid might prove beneficial for understanding item coordinates and sizes, aiding in aligning multiple objects within a single image.

Enabling the grid is straightforward; simply set the grid option to True. By default, this option is set to False. Below is an example code snippet:

image_grid1.py
1from drawlib.apis import *
2
3config(width=100, height=50, grid=True)
4circle((50, 25), radius=20)
5save()

Executing this code will generate two files:

  • image_grid1.png: The resulting image without the grid.

  • image_grid1_grid.png: The same image with the grid displayed.

By default, the file name is the same as the script file name. The grid version appends _grid to the file name.

The former image without the grid:

../../../_images/image_grid1.png

image_grid1.png

The latter image with the grid displayed:

../../../_images/image_grid1_grid.png

image_grid1_grid.png

Normally, grid lines are removed after completing the creation of the drawing code. However, if you need to modify the illustration later, having the grid displayed again might be useful. To avoid repeatedly enabling/disabling the grid in your code, you can export both versions of the image files.

If you only require the grid illustration, you can use the grid_only=True option instead:

image_grid2.py
1from drawlib.apis import *
2
3config(width=100, height=50, grid_only=True)
4circle((50, 25), radius=20)
5save()

This code will generate an image with grid lines but without the additional grid version file (image_grid2_grid.png).

../../../_images/image_grid2.png

Grid only option generate grid image only

To apply a specific line style to the grid, provide a LineStyle object to the following options:

  • grid_style: Sets the style for all grid lines.

  • grid_centerstyle: Overrides the style for the center grid lines.

Here’s an example code snippet demonstrating the use of grid styles:

image_grid3.py
 1from drawlib.apis import *
 2
 3config(
 4    width=100,
 5    height=50,
 6    grid_only=True,
 7    grid_style=LineStyle(width=1, color=Colors.Red, style="dashed"),
 8    grid_centerstyle=LineStyle(width=2, color=Colors.Blue, style="dashed"),
 9)
10circle((50, 25), radius=20)
11save()

This code will generate an image with the grid displayed using the provided line style. Note that providing a grid_style automatically sets the grid option to True, but you can override this by explicitly setting grid=False.

../../../_images/image_grid3.png

Changing grid line

Configuring Canvas Size

The canvas size and resolution are distinct concepts in Drawlib. Size refers to the coordinate system’s values (the range of xy), while resolution pertains to the output image’s pixel dimensions.

When configuring the canvas size, note that it doesn’t directly affect the pixel dimensions of the output image by default. For example, setting width=10, height=10 and width=100, height=100 both generate images of the same pixel size. However, to maintain the aspect ratio, width=10, height=5 would produce an image with half the pixel height.

By default, Drawlib’s canvas size is:

  • Width: 100

  • Height: 100

which can be adjusted using config(width=<value>, height=<value>). This default size usually suffices for typical situations. Therefore, we typically only adjust the height when we need to create horizontally wider images. There’s no reason to set a very small value that requires using coordinate values close to 0.01. Conversely, excessively large values may become unwieldy.

Let’s explore how changing the size affects the output. Below is a code snippet with config(width=100, height=100, ...):

image_size1.py
 1from drawlib.apis import *
 2
 3config(width=100, height=100, grid_only=True)
 4circle(
 5    (50, 50),
 6    radius=30,
 7    text="(50,50)",
 8    textstyle=ShapeTextStyle(size=36),
 9)
10save()

Executing this code generates the following image:

../../../_images/image_size1.png

width=100, height=100

Now, let’s adjust the size to config(width=200, height=200, ...):

image_size2.py
 1from drawlib.apis import *
 2
 3config(width=200, height=200, grid_only=True)
 4circle(
 5    (50, 50),
 6    radius=30,
 7    text="(50,50)",
 8    textstyle=ShapeTextStyle(size=36),
 9)
10save()

This change results in a new image:

../../../_images/image_size2.png

width=200, height=200

For wider images, increase the width relative to the height. You can either increase the width or decrease the height. In the following example, we set the canvas coordinate size to full HD (1920x1080):

image_size3.py
 1from drawlib.apis import *
 2
 3config(width=1920, height=1080, grid_only=True)
 4circle(
 5    (960, 540),
 6    radius=300,
 7    text="(960,540)",
 8    textstyle=ShapeTextStyle(size=36),
 9)
10save()

This produces the image below:

../../../_images/image_size3.png

width=1920, height=1080

When setting the size, using simple values such as 50, 100, 150, 200 makes mathematical calculations easier. It’s more challenging to compute half or quarter values of 1920 compared to 100.

Configuring DPI and Resolution

Understanding DPI (Dots Per Inch) is crucial for grasping drawlib’s rendering quality. However, before delving into DPI, it’s essential to comprehend the actual implementation of drawlib’s canvas size.

In the previous examples, you configured config(width=100, height=100), config(width=200, height=200) and config(width=1920, height=1080). One might assume that larger width and height values result in higher resolution images. However, let’s check the actual image sizes:

$ file image_size1.png
image_size1.png: PNG image data, 1000 x 1000, 8-bit/color RGBA, non-interlaced

$ file image_size2.png
image_size2.png: PNG image data, 1000 x 1000, 8-bit/color RGBA, non-interlaced

$ file image_size3.png
image_size3.png: PNG image data, 1000 x 562, 8-bit/color RGBA, non-interlaced

As observed, all images have a pixel width of 1000.

DPI refers to the density of dots within one inch. The default DPI value in drawlib is 100, and the canvas width is always considered to be 10 inches. Hence, the 1000-pixel width arises from 10 inches x 100 DPI.

The first and second images have heights equal to their widths, resulting in a height of 1000 pixels. However, the third image (image_size3.png) has a coordinate width of 1920 and a height of 1080. In this case, 1920 correspond to 10 inches, while 1080 equate to 5.625 inches. Then, 562 pixel height.

To export high-resolution images, you can adjust the DPI setting. For instance, 10 inches x 100 DPI equals 1000 pixels, whereas 10 inches x 200 DPI yields 2000 pixels. Let’s demonstrate this with an example:

image_dpi1.py
 1from drawlib.apis import *
 2
 3config(width=100, height=100, dpi=200, grid_only=True)
 4circle(
 5    (50, 50),
 6    radius=30,
 7    text="(50,50)",
 8    textstyle=ShapeTextStyle(size=36),
 9)
10save()

Executing this code generates the following image:

../../../_images/image_dpi1.png

dpi=200

It seems there are no big differences from previous outputs. However, it has different pixel width.

$ file image_dpi1.png
image_dpi1.png: PNG image data, 2000 x 2000, 8-bit/color RGBA, non-interlaced

Doubling the DPI results in a twofold increase in resolution. If you want to 1920px output for previous image, you can set config(width=1920, height=1080, dpi=192) instead. 10 inch width x 192 DPI generates 1920 pixel width image.

It’s important to note that higher DPI settings can slow down image generation and require more disk space. Hence, setting excessively large values such as dpi=1000 might not be advisable in typical scenarios.

Configuring Background Color and Alpha

There are situations where you may want to customize the background color of your illustrations, such as placing a black background image on a black background page. In such cases, you can configure the background color and alpha using the config() function.

By default, drawlib’s background color depends on the adopted theme, but typically, it is white with an alpha value of 1.0 (completely opaque). You can adjust these settings by changing the theme’s default background color and alpha. However, it’s much simpler and takes precedence to configure them directly using config().

Let’s look at an example:

image_background1.py
1from drawlib.apis import *
2
3config(background_color=Colors140.Orange, background_alpha=0.2)
4circle((50, 50), radius=30)
5save()

In this example, we’ve configured the background color to orange with an alpha value of 0.2. Executing this code generates the following output:

../../../_images/image_background1.png

Background color orange. Alpha 0.2

If you wish to create a completely transparent background image, you can set the background color to Colors.Transparent or set the alpha value to 0.

It’s important to note that some image formats, such as JPG, do not support alpha values. Therefore, a non-transparent JPG file is not a bug; it’s a limitation of the file format.

save()

The save() function is used to save the canvas as an image file. If no arguments are provided, it defaults to saving a PNG image with the same name as the script file. For instance, calling save() in a script named myimage.py will generate myimage.png.

We recommend using no arguments for save() because:

  • It helps in easily understanding which script generated which image.

  • PNG format suffices for normal situations.

Changing the Saving Filename

The save() function offers optional arguments. If you wish to save the image with a specific name, you can use the file option. Here’s an example:

image_save1.py
1from drawlib.apis import *
2
3config(width=100, height=50)
4circle((50, 25), radius=20)
5save(file="myimage.webp")

Executing this code generates the following image:

../../../_images/myimage.webp

File name: myimage.webp

In this case, you didn’t specify a file path. In such situations, the image will be created in the same directory as the script file. Providing a file path works as follows:

  • Absolute path: Saves the image normally.

  • Relative path: Saves the image relative to the script file’s directory.

Normally, Python handles relative paths from the entry point (where the program is started). However, drawlib handles relative paths from the script file’s directory. If you prefer the traditional Python file path behavior, please convert the relative path to an absolute path first, then provide it to the save() function. It will work as you expect.

Changing the Saving Format

If you want to save the image with the script file name but specify a file format other than PNG, the format option is useful. It behaves almost the same as save() without options, but you can specify the file format. Here’s an example:

image_save2.py
1from drawlib.apis import *
2
3config(width=100, height=50)
4circle((50, 25), radius=20)
5save(format="jpg")

This code generate this file.

../../../_images/image_save2.jpg

File name: image_save2.jpg

Drawlib supports the following formats for now:

  • PNG

  • JPG

  • WEBP

  • PDF