<jupyter_start><jupyter_text>Python Image I/O`skia-python` supports several conversion between common data types in Python.- [Image files](Reading-and-writing-to-image-files)- [NumPy array](Numpy-array)- [PIL Image](Converting-image-from/to-PIL)<jupyter_code>import skia
import PIL.Image
import numpy as np
import io
import matplotlib.pyplot as plt

from IPython.display import display<jupyter_output><empty_output><jupyter_text>Reading and writing to image files `skia.Image` is the primary read-only image data structure in Skia. `skia.Image` can be read from an image file:<jupyter_code>image = skia.Image.open('../skia/resources/images/rainbow-gradient.png')

display(image)<jupyter_output><empty_output><jupyter_text>Or, can be exported via `skia.Surface.makeImageSnapshot` call:<jupyter_code>surface = skia.Surface(100, 100)
with surface as canvas:
    canvas.clear(skia.ColorYELLOW)
    paint = skia.Paint(AntiAlias=True, Color=skia.ColorCYAN)
    canvas.drawCircle((50, 50), 25, paint)
image = surface.makeImageSnapshot()

display(image)<jupyter_output><empty_output><jupyter_text>The following resizes the image:<jupyter_code>display(image.resize(50, 50))<jupyter_output><empty_output><jupyter_text>Write to a specific image format is available via `skia.Image.save` or `skia.Image.encodeToImage`:<jupyter_code>import os
import io
import tempfile

with tempfile.TemporaryDirectory() as t:
    image.save(os.path.join(t, 'output.png'), skia.kPNG)
    image.save(os.path.join(t, 'output.jpg'), skia.kJPEG, quality=95)
    
data = image.encodeToData(skia.kWEBP, 100)
bytes(data)[:16]<jupyter_output><empty_output><jupyter_text>NumPy array`skia.Image` supports NumPy array import and export via `fromarray` and `toarray` methods:<jupyter_code>array = np.zeros((64, 64, 4), dtype=np.uint8)
array[24:48, 24:48, 3] = 255

image = skia.Image.fromarray(array)
display(image)

image = skia.Image.open('../skia/resources/images/rainbow-gradient.png')
array = image.toarray()<jupyter_output><empty_output><jupyter_text>Also, NumPy arrays can be directly used as a pixel buffer behind `skia.Canvas`. This approach enables direct drawing into the given array:<jupyter_code>array = np.zeros((256, 256, 4), dtype=np.uint8)

with skia.Surface(array) as canvas:
    paint = skia.Paint(AntiAlias=True, Color=skia.ColorCYAN)
    canvas.drawCircle((128, 128), 64, paint)

plt.figure(figsize=(2, 2))
plt.imshow(array)
plt.show()<jupyter_output><empty_output><jupyter_text>One pitfall is that generic pixel format is usually unpremultiplied alpha (`skia.kUnpremul_AlphaType`), but skia defaults to premultiplied alpha (`skia.kPremul_AlphaType`). Don't forget to specify `alphaType` parameter when needed. Many APIs in skia do not copy buffer data but only shares a reference. Therefore, buffer lifetime should be carefully taken into account when converting between different format. The following results in crash.```pythonarray = np.zeros((64, 64, 4), dtype=np.uint8)surface = skia.Surface(array)del arraysurface.makeImageSnapshot()   This will crash``` Canvas or surface can also export to numpy array. This is convenient when surface backend is not raster.<jupyter_code>surface = skia.Surface(128, 128)
canvas = surface.getCanvas()
paint = skia.Paint(AntiAlias=True, Color=skia.ColorCYAN)
canvas.drawCircle((64, 64), 32, paint)

plt.figure(figsize=(4, 8))
plt.subplot(1, 2, 1), plt.imshow(canvas.toarray())
plt.subplot(1, 2, 2), plt.imshow(surface.toarray())
plt.show()<jupyter_output><empty_output><jupyter_text>Converting image from/to PILIt is possible to directly convert `skia.Image`, `skia.Bitmap` or `skia.Pixmap` to `PIL.Image` using `fromarray` method.Note that `PIL.Image` supports limited image modes. Apart from `skia.kRGBA_8888_ColorType` format, many pixel formats in skia are not compatible.Default alpha type of `skia.Image` is `skia.kPremul_AlphaType`. To convert `skia.Image` to `PIL.Image`, make sure to first convert the alpha type before calling `fromarray`:<jupyter_code>skia_image = skia.Image.open('../skia/resources/images/rainbow-gradient.png')

pil_image = PIL.Image.fromarray(skia_image.convert(alphaType=skia.kUnpremul_AlphaType))<jupyter_output><empty_output><jupyter_text>Alternatively, use `RGBa` (premultiplied alpha mode) in `PIL.Image`:<jupyter_code>pil_image = PIL.Image.fromarray(skia_image, 'RGBa')<jupyter_output><empty_output><jupyter_text>Image is also convertible via encoded image buffer:<jupyter_code>with io.BytesIO(skia_image.encodeToData()) as f:
    pil_image = PIL.Image.open(f)
    pil_image.load()  # Ensure to make a copy of buffer<jupyter_output><empty_output><jupyter_text>Importing a `PIL.Image` requires conversion to supported color type:<jupyter_code>pil_image = PIL.Image.open('../skia/resources/images/rainbow-gradient.png')
skia_image = skia.Image.frombytes(
    pil_image.convert('RGBA').tobytes(), pil_image.size, skia.kRGBA_8888_ColorType)

skia_image = skia.Image.frombytes(
    pil_image.convert('RGBX').tobytes(), pil_image.size, skia.kRGB_888x_ColorType)

skia_image = skia.Image.frombytes(
    pil_image.convert('L').tobytes(), pil_image.size, skia.kGray_8_ColorType)<jupyter_output><empty_output><jupyter_text>Alternatively, it is possible to use encoded bytes:<jupyter_code>pil_image = PIL.Image.open('../skia/resources/images/rainbow-gradient.png')

with io.BytesIO() as f:
    pil_image.save(f, 'png')
    skia_image = skia.Image.open(f)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12394106813996317, 'fraction_numerical': 0.017863720073664824, 'mean_word_length': 6.5325936199722605, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '570376', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 1999, 'n_tokens_neox': 1887, 'n_words': 423, 'path': 'notebooks/Python-Image-IO.ipynb', 'repo_name': 'naveen521kk/skia-python', 'size': '5430'}