Update ili9x and gc9a01 drivers for image display.

pull/70/head
Peter Hinch 2024-06-07 09:54:42 +01:00
rodzic b972e8a3aa
commit 1bd163d72d
6 zmienionych plików z 154 dodań i 50 usunięć

81
IMAGE_DISPLAY.md 100644
Wyświetl plik

@ -0,0 +1,81 @@
# 1. Displaying photo images
The display drivers in this repo were primarily designed for displaying geometric shapes
and fonts. With a minor update they may also be used for image display. The method used is
ideal for full screen images however with suitable user code smaller images may be
rendered. It is also possible to overlay an image with GUI controls, although transparency
is not supported.
The following notes apply
[nanogui](https://github.com/peterhinch/micropython-nano-gui)
[micro-gui](https://github.com/peterhinch/micropython-micro-gui) and
[micropython-touch](https://github.com/peterhinch/micropython-touch).
Images for display should be converted to a [netpbm format](https://en.wikipedia.org/wiki/Netpbm),
namely a `.pgm` file for a monochrome image or `.ppm` for color. This may be
done using a utility such as Gimp. Netpbm files use 8-bit values. These are then
converted to RGB565 for 16-bit drivers, RRRGGGBB for 8-bit color, or 4-bit
greyscale to enable a monochrome image to display on a 4-bit driver. This is
done using a CPython utility `img_cvt.py` documented below.
An updated driver has a `greyscale` method enabling the frame buffer contents to
be interpreted at show time as either color or greyscale. This
## 1.2 Supported drivers
Currently only gc9a01 drivers are supported.
## 1.3 Monochrome images
These may be displayed using 8-bit or 16-bit drivers by treating it as if it
were color: exporting the image from the graphics program as a `.ppm` color
image and using `img_cvt.py` to convert it to the correct color mode.
On 4-bit drivers the image should be exported as a `.pgm` greyscale;
`img_cvt.py` will convert it to 4-bit format. In testing this produced good
results.
## 1.4 Color images
These cannot be displayed on 4-bit drivers. On 8 or 16 bit drivers these should
be exported from the graphics program as a `.ppm` color image. Then `img_cvt.py`
is used to convert the file to the correct color mode.
## 1.5 Code samples
Files produced by `img_cvt.py` are binary files. The first four bytes comprise
two 16-bit integers defining the numbers of rows and columns in the image. The
following is an example of a full-screen image display in microgui or
micropython-touch:
```py
class MoonScreen(Screen):
def __init__(self):
super().__init__()
def after_open(self):
fn = "test.bin" # Image created by`img_cvt.py`
# The following line is required if a 4-bit driver is in use
# ssd.greyscale(True)
with open(fn, "rb") as f:
_ = f.read(4) # Read and discard rows and cols
f.readinto(ssd.mvb) # Read the image into the frame buffer
```
On nano-gui:
```py
from color_setup import ssd # Create a display instance
from gui.core.nanogui import refresh
refresh(ssd) # Initialise display.
fn = "test.bin" # Image created by`img_cvt.py`
# The following line is required if a 4-bit driver is in use
# ssd.greyscale(True)
with open(fn, "rb") as f:
_ = f.read(4) # Read and discard rows and cols
f.readinto(ssd.mvb) # Read the image into the frame buffer
refresh(ssd)
```
These examples rely on the images being configured to precisely match the screen
size. In other cases the rows and cols values must be used to populate a subset
of the frame buffer pixels or to display a subset of the image pixels. Secondly
the built-in flash of some platforms can be slow. If there is a visible pause in
displaying the image this is likely to be the cause.

Wyświetl plik

@ -81,7 +81,7 @@ class GC9A01(framebuf.FrameBuffer):
self.palette = BoolPalette(mode) self.palette = BoolPalette(mode)
gc.collect() gc.collect()
buf = bytearray(height * width // 2) # Frame buffer buf = bytearray(height * width // 2) # Frame buffer
self._mvb = memoryview(buf) self.mvb = memoryview(buf)
super().__init__(buf, width, height, mode) super().__init__(buf, width, height, mode)
self._linebuf = bytearray(width * 2) # Line buffer (16-bit colors) self._linebuf = bytearray(width * 2) # Line buffer (16-bit colors)
@ -188,7 +188,7 @@ class GC9A01(framebuf.FrameBuffer):
def show(self): # Physical display is in portrait mode def show(self): # Physical display is in portrait mode
clut = GC9A01.lut clut = GC9A01.lut
lb = self._linebuf lb = self._linebuf
buf = self._mvb buf = self.mvb
if self._spi_init: # A callback was passed if self._spi_init: # A callback was passed
self._spi_init(self._spi) # Bus may be shared self._spi_init(self._spi) # Bus may be shared
self._wcmd(b"\x2c") # WRITE_RAM self._wcmd(b"\x2c") # WRITE_RAM
@ -209,7 +209,7 @@ class GC9A01(framebuf.FrameBuffer):
raise ValueError("Invalid do_refresh arg.") raise ValueError("Invalid do_refresh arg.")
clut = GC9A01.lut clut = GC9A01.lut
lb = self._linebuf lb = self._linebuf
buf = self._mvb buf = self.mvb
self._wcmd(b"\x2c") # WRITE_RAM self._wcmd(b"\x2c") # WRITE_RAM
self._dc(1) self._dc(1)
wd = self.width // 2 wd = self.width // 2

Wyświetl plik

@ -23,17 +23,14 @@ from drivers.boolpalette import BoolPalette
# g4 g3 g2 b7 b6 b5 b4 b3 r7 r6 r5 r4 r3 g7 g6 g5 # g4 g3 g2 b7 b6 b5 b4 b3 r7 r6 r5 r4 r3 g7 g6 g5
@micropython.viper @micropython.viper
def _lcopy(dest: ptr16, source: ptr8, length: int, gscale: bool): def _lcopy(dest: ptr16, source: ptr8, length: int):
# rgb565 - 16bit/pixel # rgb565 - 16bit/pixel
n: int = 0 n: int = 0
while length: while length:
c = source[n] c = source[n]
if gscale: # Source byte holds 8-bit greyscale # Source byte holds 8-bit rrrgggbb
# dest rrrr rggg gggb bbbb # dest 000b b000 rrr0 0ggg
dest[n] = (c & 0xF1) | (c >> 5) | ((c & 0x1C) << 11) | ((c & 0xF1) << 5) dest[n] = (c & 0xE0) | ((c & 0x1C) >> 2) | ((c & 0x03) << 11)
else: # Source byte holds 8-bit rrrgggbb
# dest 000b b000 rrr0 0ggg
dest[n] = (c & 0xE0) | ((c & 0x1C) >> 2) | ((c & 0x03) << 11)
n += 1 n += 1
length -= 1 length -= 1
@ -66,12 +63,11 @@ class GC9A01(framebuf.FrameBuffer):
self.height = height # Logical dimensions for GUIs self.height = height # Logical dimensions for GUIs
self.width = width self.width = width
self._spi_init = init_spi self._spi_init = init_spi
self._gscale = False # Interpret buffer as rrrgggbb color
mode = framebuf.GS8 # Use 8bit greyscale for 8 bit color. mode = framebuf.GS8 # Use 8bit greyscale for 8 bit color.
self.palette = BoolPalette(mode) self.palette = BoolPalette(mode)
gc.collect() gc.collect()
buf = bytearray(height * width) # Frame buffer buf = bytearray(height * width) # Frame buffer
self._mvb = memoryview(buf) self.mvb = memoryview(buf)
super().__init__(buf, width, height, mode) super().__init__(buf, width, height, mode)
self._linebuf = bytearray(width * 2) # Line buffer (16-bit colors) self._linebuf = bytearray(width * 2) # Line buffer (16-bit colors)
@ -171,14 +167,9 @@ class GC9A01(framebuf.FrameBuffer):
self._spi.write(data) self._spi.write(data)
self._cs(1) self._cs(1)
def greyscale(self, gs=None):
if gs is not None:
self._gscale = gs
return self._gscale
def show(self): # Physical display is in portrait mode def show(self): # Physical display is in portrait mode
lb = self._linebuf lb = self._linebuf
buf = self._mvb buf = self.mvb
if self._spi_init: # A callback was passed if self._spi_init: # A callback was passed
self._spi_init(self._spi) # Bus may be shared self._spi_init(self._spi) # Bus may be shared
self._wcmd(b"\x2c") # WRITE_RAM self._wcmd(b"\x2c") # WRITE_RAM
@ -186,9 +177,8 @@ class GC9A01(framebuf.FrameBuffer):
self._cs(0) self._cs(0)
wd = self.width wd = self.width
ht = self.height ht = self.height
cm = self._gscale # color False, greyscale True
for start in range(0, wd * ht, wd): # For each line for start in range(0, wd * ht, wd): # For each line
_lcopy(lb, buf[start:], wd, cm) # Copy and map colors _lcopy(lb, buf[start:], wd) # Copy and map colors
self._spi.write(lb) self._spi.write(lb)
self._cs(1) self._cs(1)
@ -198,18 +188,17 @@ class GC9A01(framebuf.FrameBuffer):
if mod: if mod:
raise ValueError("Invalid do_refresh arg.") raise ValueError("Invalid do_refresh arg.")
lb = self._linebuf lb = self._linebuf
buf = self._mvb buf = self.mvb
self._wcmd(b"\x2c") # WRITE_RAM self._wcmd(b"\x2c") # WRITE_RAM
self._dc(1) self._dc(1)
wd = self.width wd = self.width
cm = self._gscale # color False, greyscale True
line = 0 line = 0
for _ in range(split): # For each segment for _ in range(split): # For each segment
if self._spi_init: # A callback was passed if self._spi_init: # A callback was passed
self._spi_init(self._spi) # Bus may be shared self._spi_init(self._spi) # Bus may be shared
self._cs(0) self._cs(0)
for start in range(wd * line, wd * (line + lines), wd): # For each line for start in range(wd * line, wd * (line + lines), wd): # For each line
_lcopy(lb, buf[start:], wd, cm) # Copy and map colors _lcopy(lb, buf[start:], wd) # Copy and map colors
self._spi.write(lb) self._spi.write(lb)
line += lines line += lines
self._cs(1) # Allow other tasks to use bus self._cs(1) # Allow other tasks to use bus

Wyświetl plik

@ -15,17 +15,26 @@ import asyncio
from drivers.boolpalette import BoolPalette from drivers.boolpalette import BoolPalette
# Output RGB565 format, 16 bit/pixel:
# g4 g3 g2 b7 b6 b5 b4 b3 r7 r6 r5 r4 r3 g7 g6 g5
# ~80μs on RP2 @ 250MHz. # ~80μs on RP2 @ 250MHz.
@micropython.viper @micropython.viper
def _lcopy(dest: ptr16, source: ptr8, lut: ptr16, length: int): def _lcopy(dest: ptr16, source: ptr8, lut: ptr16, length: int, gscale: bool):
# rgb565 - 16bit/pixel # rgb565 - 16bit/pixel
n: int = 0 n: int = 0
x: int = 0 x: int = 0
while length: while length:
c = source[x] c = source[x]
dest[n] = lut[c >> 4] # current pixel p = c >> 4 # current pixel
n += 1 q = c & 0x0F # next pixel
dest[n] = lut[c & 0x0F] # next pixel if gscale:
dest[n] = p >> 1 | p << 4 | p << 9 | ((p & 0x01) << 15)
n += 1
dest[n] = q >> 1 | q << 4 | q << 9 | ((q & 0x01) << 15)
else:
dest[n] = lut[p] # current pixel
n += 1
dest[n] = lut[q] # next pixel
n += 1 n += 1
x += 1 x += 1
length -= 1 length -= 1
@ -52,11 +61,12 @@ class ILI9341(framebuf.FrameBuffer):
self.height = height self.height = height
self.width = width self.width = width
self._spi_init = init_spi self._spi_init = init_spi
self._gscale = False # Interpret buffer as index into color LUT
mode = framebuf.GS4_HMSB mode = framebuf.GS4_HMSB
self.palette = BoolPalette(mode) self.palette = BoolPalette(mode)
gc.collect() gc.collect()
buf = bytearray(self.height * self.width // 2) buf = bytearray(self.height * self.width // 2)
self._mvb = memoryview(buf) self.mvb = memoryview(buf)
super().__init__(buf, self.width, self.height, mode) super().__init__(buf, self.width, self.height, mode)
self._linebuf = bytearray(self.width * 2) self._linebuf = bytearray(self.width * 2)
# Hardware reset # Hardware reset
@ -118,6 +128,11 @@ class ILI9341(framebuf.FrameBuffer):
self._spi.write(data) self._spi.write(data)
self._cs(1) self._cs(1)
def greyscale(self, gs=None):
if gs is not None:
self._gscale = gs
return self._gscale
# Time (ESP32 stock freq) 196ms portrait, 185ms landscape. # Time (ESP32 stock freq) 196ms portrait, 185ms landscape.
# mem free on ESP32 43472 bytes (vs 110192) # mem free on ESP32 43472 bytes (vs 110192)
@micropython.native @micropython.native
@ -125,8 +140,9 @@ class ILI9341(framebuf.FrameBuffer):
clut = ILI9341.lut clut = ILI9341.lut
wd = self.width // 2 wd = self.width // 2
ht = self.height ht = self.height
cm = self._gscale # color False, greyscale True
lb = self._linebuf lb = self._linebuf
buf = self._mvb buf = self.mvb
if self._spi_init: # A callback was passed if self._spi_init: # A callback was passed
self._spi_init(self._spi) # Bus may be shared self._spi_init(self._spi) # Bus may be shared
# Commands needed to start data write # Commands needed to start data write
@ -136,7 +152,7 @@ class ILI9341(framebuf.FrameBuffer):
self._dc(1) self._dc(1)
self._cs(0) self._cs(0)
for start in range(0, wd * ht, wd): # For each line for start in range(0, wd * ht, wd): # For each line
_lcopy(lb, buf[start:], clut, wd) # Copy and map colors _lcopy(lb, buf[start:], clut, wd, cm) # Copy and map colors
self._spi.write(lb) self._spi.write(lb)
self._cs(1) self._cs(1)
@ -148,8 +164,9 @@ class ILI9341(framebuf.FrameBuffer):
clut = ILI9341.lut clut = ILI9341.lut
wd = self.width // 2 wd = self.width // 2
ht = self.height ht = self.height
cm = self._gscale # color False, greyscale True
lb = self._linebuf lb = self._linebuf
buf = self._mvb buf = self.mvb
# Commands needed to start data write # Commands needed to start data write
self._wcd(b"\x2a", int.to_bytes(self.width, 4, "big")) # SET_COLUMN self._wcd(b"\x2a", int.to_bytes(self.width, 4, "big")) # SET_COLUMN
self._wcd(b"\x2b", int.to_bytes(ht, 4, "big")) # SET_PAGE self._wcd(b"\x2b", int.to_bytes(ht, 4, "big")) # SET_PAGE
@ -161,7 +178,7 @@ class ILI9341(framebuf.FrameBuffer):
self._spi_init(self._spi) # Bus may be shared self._spi_init(self._spi) # Bus may be shared
self._cs(0) self._cs(0)
for start in range(wd * line, wd * (line + lines), wd): # For each line for start in range(wd * line, wd * (line + lines), wd): # For each line
_lcopy(lb, buf[start:], clut, wd) # Copy and map colors _lcopy(lb, buf[start:], clut, wd, cm) # Copy and map colors
self._spi.write(lb) self._spi.write(lb)
line += lines line += lines
self._cs(1) # Allow other tasks to use bus self._cs(1) # Allow other tasks to use bus

Wyświetl plik

@ -20,15 +20,22 @@ from drivers.boolpalette import BoolPalette
# Portrait mode # Portrait mode
@micropython.viper @micropython.viper
def _lcopy(dest: ptr16, source: ptr8, lut: ptr16, length: int): def _lcopy(dest: ptr16, source: ptr8, lut: ptr16, length: int, gscale: bool):
# rgb565 - 16bit/pixel # rgb565 - 16bit/pixel
n: int = 0 n: int = 0
x: int = 0 x: int = 0
while length: while length:
c = source[x] c = source[x]
dest[n] = lut[c >> 4] # current pixel p = c >> 4 # current pixel
n += 1 q = c & 0x0F # next pixel
dest[n] = lut[c & 0x0F] # next pixel if gscale:
dest[n] = p >> 1 | p << 4 | p << 9 | ((p & 0x01) << 15)
n += 1
dest[n] = q >> 1 | q << 4 | q << 9 | ((q & 0x01) << 15)
else:
dest[n] = lut[p] # current pixel
n += 1
dest[n] = lut[q] # next pixel
n += 1 n += 1
x += 1 x += 1
length -= 1 length -= 1
@ -36,8 +43,8 @@ def _lcopy(dest: ptr16, source: ptr8, lut: ptr16, length: int):
# FB is in landscape mode, hence issue a column at a time to portrait mode hardware. # FB is in landscape mode, hence issue a column at a time to portrait mode hardware.
@micropython.viper @micropython.viper
def _lscopy(dest: ptr16, source: ptr8, lut: ptr16, ch: int): def _lscopy(dest: ptr16, source: ptr8, lut: ptr16, ch: int, gscale: bool):
col = ch & 0x1FF # Unpack (viper 4 parameter limit) col = ch & 0x1FF # Unpack (viper old 4 parameter limit)
height = (ch >> 9) & 0x1FF height = (ch >> 9) & 0x1FF
wbytes = ch >> 19 # Width in bytes is width // 2 wbytes = ch >> 19 # Width in bytes is width // 2
# rgb565 - 16bit/pixel # rgb565 - 16bit/pixel
@ -49,7 +56,8 @@ def _lscopy(dest: ptr16, source: ptr8, lut: ptr16, ch: int):
c = source[idx] & 0x0F c = source[idx] & 0x0F
else: else:
c = source[idx] >> 4 c = source[idx] >> 4
dest[n] = lut[c] # 16 bit transfer of rightmost 4-bit pixel dest[n] = c >> 1 | c << 4 | c << 9 | ((c & 0x01) << 15) if gscale else lut[c]
# dest[n] = lut[c] # 16 bit transfer of rightmost 4-bit pixel
n += 1 # 16 bit n += 1 # 16 bit
idx += wbytes idx += wbytes
height -= 1 height -= 1
@ -83,11 +91,12 @@ class ILI9486(framebuf.FrameBuffer):
self._long = max(height, width) # Physical dimensions of screen and aspect ratio self._long = max(height, width) # Physical dimensions of screen and aspect ratio
self._short = min(height, width) self._short = min(height, width)
self._spi_init = init_spi self._spi_init = init_spi
self._gscale = False # Interpret buffer as index into color LUT
mode = framebuf.GS4_HMSB mode = framebuf.GS4_HMSB
self.palette = BoolPalette(mode) self.palette = BoolPalette(mode)
gc.collect() gc.collect()
buf = bytearray(height * width // 2) buf = bytearray(height * width // 2)
self._mvb = memoryview(buf) self.mvb = memoryview(buf)
super().__init__(buf, width, height, mode) # Logical aspect ratio super().__init__(buf, width, height, mode) # Logical aspect ratio
self._linebuf = bytearray(self._short * 2) self._linebuf = bytearray(self._short * 2)
@ -140,11 +149,17 @@ class ILI9486(framebuf.FrameBuffer):
self._spi.write(data) self._spi.write(data)
self._cs(1) self._cs(1)
def greyscale(self, gs=None):
if gs is not None:
self._gscale = gs
return self._gscale
# @micropython.native # Made almost no difference to timing # @micropython.native # Made almost no difference to timing
def show(self): # Physical display is in portrait mode def show(self): # Physical display is in portrait mode
clut = ILI9486.lut clut = ILI9486.lut
lb = self._linebuf lb = self._linebuf
buf = self._mvb buf = self.mvb
cm = self._gscale # color False, greyscale True
if self._spi_init: # A callback was passed if self._spi_init: # A callback was passed
self._spi_init(self._spi) # Bus may be shared self._spi_init(self._spi) # Bus may be shared
self._wcmd(b"\x2c") # WRITE_RAM self._wcmd(b"\x2c") # WRITE_RAM
@ -154,14 +169,14 @@ class ILI9486(framebuf.FrameBuffer):
wd = self.width // 2 wd = self.width // 2
ht = self.height ht = self.height
for start in range(0, wd * ht, wd): # For each line for start in range(0, wd * ht, wd): # For each line
_lcopy(lb, buf[start:], clut, wd) # Copy and map colors _lcopy(lb, buf[start:], clut, wd, cm) # Copy and map colors
self._spi.write(lb) self._spi.write(lb)
else: # Landscpe 264ms on RP2 120MHz, 30MHz SPI clock else: # Landscpe 264ms on RP2 120MHz, 30MHz SPI clock
width = self.width width = self.width
wd = width - 1 wd = width - 1
cargs = (self.height << 9) + (width << 18) # Viper 4-arg limit cargs = (self.height << 9) + (width << 18) # Viper 4-arg limit
for col in range(width): # For each column of landscape display for col in range(width): # For each column of landscape display
_lscopy(lb, buf, clut, wd - col + cargs) # Copy and map colors _lscopy(lb, buf, clut, wd - col + cargs, cm) # Copy and map colors
self._spi.write(lb) self._spi.write(lb)
self._cs(1) self._cs(1)
@ -172,7 +187,8 @@ class ILI9486(framebuf.FrameBuffer):
raise ValueError("Invalid do_refresh arg.") raise ValueError("Invalid do_refresh arg.")
clut = ILI9486.lut clut = ILI9486.lut
lb = self._linebuf lb = self._linebuf
buf = self._mvb buf = self.mvb
cm = self._gscale # color False, greyscale True
self._wcmd(b"\x2c") # WRITE_RAM self._wcmd(b"\x2c") # WRITE_RAM
self._dc(1) self._dc(1)
if self.width < self.height: # Portrait: write sets of rows if self.width < self.height: # Portrait: write sets of rows
@ -183,7 +199,7 @@ class ILI9486(framebuf.FrameBuffer):
self._spi_init(self._spi) # Bus may be shared self._spi_init(self._spi) # Bus may be shared
self._cs(0) self._cs(0)
for start in range(wd * line, wd * (line + lines), wd): # For each line for start in range(wd * line, wd * (line + lines), wd): # For each line
_lcopy(lb, buf[start:], clut, wd) # Copy and map colors _lcopy(lb, buf[start:], clut, wd, cm) # Copy and map colors
self._spi.write(lb) self._spi.write(lb)
line += lines line += lines
self._cs(1) # Allow other tasks to use bus self._cs(1) # Allow other tasks to use bus
@ -197,7 +213,7 @@ class ILI9486(framebuf.FrameBuffer):
self._spi_init(self._spi) # Bus may be shared self._spi_init(self._spi) # Bus may be shared
self._cs(0) self._cs(0)
for col in range(sc, ec, -1): # For each column of landscape display for col in range(sc, ec, -1): # For each column of landscape display
_lscopy(lb, buf, clut, col + cargs) # Copy and map colors _lscopy(lb, buf, clut, col + cargs, cm) # Copy and map colors
self._spi.write(lb) self._spi.write(lb)
sc -= lines sc -= lines
ec -= lines ec -= lines

Wyświetl plik

@ -5,6 +5,7 @@
# As written, supports: # As written, supports:
# gc9a01 240x240 circular display on Pi Pico # gc9a01 240x240 circular display on Pi Pico
# Pin mapping is for Waveshare RP2040-Touch-LCD-1.28
# Edit the driver import for other displays. # Edit the driver import for other displays.
# Demo of initialisation procedure designed to minimise risk of memory fail # Demo of initialisation procedure designed to minimise risk of memory fail
@ -26,13 +27,13 @@ from machine import Pin, SPI
import gc import gc
from drivers.gc9a01.gc9a01 import GC9A01 as SSD from drivers.gc9a01.gc9a01 import GC9A01 as SSD
# from drivers.gc9a01.gc9a01_8_bit import GC9A01 as SSD from drivers.gc9a01.gc9a01_8_bit import GC9A01 as SSD
pdc = Pin(8, Pin.OUT, value=0) # Arbitrary pins pdc = Pin(8, Pin.OUT, value=0) # Arbitrary pins
prst = Pin(9, Pin.OUT, value=1) prst = Pin(13, Pin.OUT, value=1)
pcs = Pin(10, Pin.OUT, value=1) pcs = Pin(9, Pin.OUT, value=1)
gc.collect() # Precaution before instantiating framebuf gc.collect() # Precaution before instantiating framebuf
# See DRIVERS.md # See DRIVERS.md
spi = SPI(0, sck=Pin(6), mosi=Pin(7), miso=Pin(4), baudrate=33_000_000) spi = SPI(1, sck=Pin(10), mosi=Pin(11), miso=Pin(12), baudrate=33_000_000)
ssd = SSD(spi, dc=pdc, cs=pcs, rst=prst, lscape=False, usd=False, mirror=False) ssd = SSD(spi, dc=pdc, cs=pcs, rst=prst, lscape=False, usd=False, mirror=False)