|
1 | 1 | #!/usr/bin/python3
|
2 | 2 | """
|
3 |
| -Mirror a scaled copy of the framebuffer to 64x32 matrices, |
| 3 | +Mirror a scaled copy of the framebuffer to RGB matrices, |
4 | 4 |
|
5 |
| -The upper left corner of the framebuffer is displayed until the user hits ctrl-c. |
| 5 | +A portion of the framebuffer is displayed until the user hits ctrl-c. |
6 | 6 |
|
7 | 7 | Control scale, matrix size, and orientation with command line arguments.
|
8 | 8 |
|
9 |
| -python fbmirror_scaled.py [scale] [width] [height] [orientation] |
| 9 | +Usage: fbmirror_scaled.py [OPTIONS] |
| 10 | +
|
| 11 | +Options: |
| 12 | + --x-offset INTEGER The x offset of top left corner of the |
| 13 | + region to mirror |
| 14 | + --y-offset INTEGER The y offset of top left corner of the |
| 15 | + region to mirror |
| 16 | + --scale INTEGER The scale factor to reduce the display down |
| 17 | + by. |
| 18 | + --num-address-lines INTEGER The number of address lines used by the |
| 19 | + panels |
| 20 | + --num-planes INTEGER The number of bit planes (color depth. Lower |
| 21 | + values can improve refresh rate in frames |
| 22 | + per second |
| 23 | + --orientation [Normal|R180|CCW|CW] |
| 24 | + The overall orientation (rotation) of the |
| 25 | + panels |
| 26 | + --pinout [AdafruitMatrixBonnet|AdafruitMatrixBonnetBGR|AdafruitMatrixHat|AdafruitMatrixHatBGR] |
| 27 | + The details of the electrical connection to |
| 28 | + the panels |
| 29 | + --serpentine / --no-serpentine The organization of multiple panels |
| 30 | + --height INTEGER The panel height in pixels |
| 31 | + --width INTEGER The panel width in pixels |
| 32 | + --help Show this message and exit. |
10 | 33 |
|
11 |
| - scale int: How many times to scale down the display framebuffer. Default is 3. |
12 |
| - width int: Total width of matrices in pixels. Default is 64. |
13 |
| - height int: Total height of matrices in pixels. Default is 32. |
14 |
| - orientation int: Orientation in degrees, must be 0, 90, 180, or 270. |
15 |
| - Default is 0 or Normal orientation. |
16 | 34 |
|
17 | 35 | The `/dev/fb0` special file will exist if a monitor is plugged in at boot time,
|
18 | 36 | or if `/boot/firmware/cmdline.txt` specifies a resolution such as
|
19 | 37 | `... video=HDMI-A-1:640x480M@60D`.
|
20 | 38 | """
|
21 |
| -import sys |
22 | 39 |
|
23 |
| -import adafruit_raspberry_pi5_piomatter |
| 40 | +import adafruit_raspberry_pi5_piomatter as piomatter |
| 41 | +import click |
24 | 42 | import numpy as np
|
25 | 43 | import PIL.Image as Image
|
26 |
| - |
27 |
| -if len(sys.argv) >= 2: |
28 |
| - scale = int(sys.argv[1]) |
29 |
| -else: |
30 |
| - scale = 3 |
31 |
| - |
32 |
| -if len(sys.argv) >= 3: |
33 |
| - width = int(sys.argv[2]) |
34 |
| -else: |
35 |
| - width = 64 |
36 |
| - |
37 |
| -if len(sys.argv) >= 4: |
38 |
| - height = int(sys.argv[3]) |
39 |
| -else: |
40 |
| - height = 32 |
41 |
| - |
42 |
| -if len(sys.argv) >= 5: |
43 |
| - rotation = int(sys.argv[4]) |
44 |
| - if rotation == 90: |
45 |
| - rotation = adafruit_raspberry_pi5_piomatter.Orientation.CW |
46 |
| - elif rotation == 180: |
47 |
| - rotation = adafruit_raspberry_pi5_piomatter.Orientation.R180 |
48 |
| - elif rotation == 270: |
49 |
| - rotation = adafruit_raspberry_pi5_piomatter.Orientation.CCW |
50 |
| - elif rotation == 0: |
51 |
| - rotation = adafruit_raspberry_pi5_piomatter.Orientation.Normal |
52 |
| - else: |
53 |
| - raise ValueError("Invalid rotation. Must be 0, 90, 180, or 270.") |
54 |
| -else: |
55 |
| - rotation = adafruit_raspberry_pi5_piomatter.Orientation.Normal |
| 44 | +import piomatter_click |
56 | 45 |
|
57 | 46 | with open("/sys/class/graphics/fb0/virtual_size") as f:
|
58 | 47 | screenx, screeny = [int(word) for word in f.read().split(",")]
|
|
70 | 59 |
|
71 | 60 | linux_framebuffer = np.memmap('/dev/fb0',mode='r', shape=(screeny, stride // bytes_per_pixel), dtype=dtype)
|
72 | 61 |
|
73 |
| -xoffset = 0 |
74 |
| -yoffset = 0 |
75 |
| - |
76 |
| -geometry = adafruit_raspberry_pi5_piomatter.Geometry(width=width, height=height, n_addr_lines=4, rotation=rotation) |
77 |
| -matrix_framebuffer = np.zeros(shape=(geometry.height, geometry.width, 3), dtype=np.uint8) |
78 |
| -matrix = adafruit_raspberry_pi5_piomatter.AdafruitMatrixBonnetRGB888Packed(matrix_framebuffer, geometry) |
79 |
| - |
80 |
| -while True: |
81 |
| - tmp = linux_framebuffer[yoffset:yoffset+height*scale, xoffset:xoffset+width*scale] |
82 |
| - # Convert the RGB565 framebuffer into RGB888Packed (so that we can use PIL image operations to rescale it) |
83 |
| - r = (tmp & 0xf800) >> 8 |
84 |
| - r = r | (r >> 5) |
85 |
| - r = r.astype(np.uint8) |
86 |
| - g = (tmp & 0x07e0) >> 3 |
87 |
| - g = g | (g >> 6) |
88 |
| - g = g.astype(np.uint8) |
89 |
| - b = (tmp & 0x001f) << 3 |
90 |
| - b = b | (b >> 5) |
91 |
| - b = b.astype(np.uint8) |
92 |
| - img = Image.fromarray(np.stack([r, g, b], -1)) |
93 |
| - img = img.resize((width, height)) |
94 |
| - matrix_framebuffer[:,:] = np.asarray(img) |
95 |
| - matrix.show() |
| 62 | + |
| 63 | +@click.command |
| 64 | +@click.option("--x-offset", "xoffset", type=int, help="The x offset of top left corner of the region to mirror", default=0) |
| 65 | +@click.option("--y-offset", "yoffset", type=int, help="The y offset of top left corner of the region to mirror", default=0) |
| 66 | +@click.option("--scale", "scale", type=int, help="The scale factor to reduce the display down by.", default=3) |
| 67 | +@piomatter_click.standard_options |
| 68 | +def main(xoffset, yoffset, scale, width, height, serpentine, rotation, pinout, n_planes, n_addr_lines): |
| 69 | + geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines, rotation=rotation) |
| 70 | + matrix_framebuffer = np.zeros(shape=(geometry.height, geometry.width, 3), dtype=np.uint8) |
| 71 | + matrix = piomatter.PioMatter(colorspace=piomatter.Colorspace.RGB888Packed, pinout=pinout, framebuffer=matrix_framebuffer, geometry=geometry) |
| 72 | + |
| 73 | + while True: |
| 74 | + tmp = linux_framebuffer[yoffset:yoffset + height * scale, xoffset:xoffset + width * scale] |
| 75 | + # Convert the RGB565 framebuffer into RGB888Packed (so that we can use PIL image operations to rescale it) |
| 76 | + r = (tmp & 0xf800) >> 8 |
| 77 | + r = r | (r >> 5) |
| 78 | + r = r.astype(np.uint8) |
| 79 | + g = (tmp & 0x07e0) >> 3 |
| 80 | + g = g | (g >> 6) |
| 81 | + g = g.astype(np.uint8) |
| 82 | + b = (tmp & 0x001f) << 3 |
| 83 | + b = b | (b >> 5) |
| 84 | + b = b.astype(np.uint8) |
| 85 | + img = Image.fromarray(np.stack([r, g, b], -1)) |
| 86 | + img = img.resize((width, height)) |
| 87 | + matrix_framebuffer[:, :] = np.array(img) |
| 88 | + matrix.show() |
| 89 | + |
| 90 | +if __name__ == '__main__': |
| 91 | + main() |
0 commit comments