Skip to content

Commit 2974f26

Browse files
committed
Merge pull request #3 from bettio/updated-readme
README.Md: add several useful information
2 parents a5c7ce3 + 00bd190 commit 2974f26

File tree

1 file changed

+184
-44
lines changed

1 file changed

+184
-44
lines changed

README.Md

Lines changed: 184 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,72 +6,212 @@
66

77
# AtomGL
88

9-
AtomGL is a display driver for a number of different display models and technologies.
9+
AtomGL is a display driver for various display models and technologies, implementing a declarative
10+
display list approach for rendering.
1011

11-
This component is not meant for being used directly, instead an additional layer should take care of
12-
pushing updates to it. For this reason no additional NIFs are provided.
12+
This component is not meant to be used directly. Instead, an additional layer should handle pushing
13+
updates to it. For this reason, no additional NIFs are provided.
1314

14-
**Please, use this component with a supported AtomVM version, such as v0.6.**
15+
**Please use this component with a supported AtomVM version, such as v0.6.**
16+
17+
## Core Concept: Display Lists
18+
19+
AtomGL uses a **declarative display list** approach where everything rendered on the display is
20+
represented as a list of primitive items (Erlang tuples/records). Unlike imperative graphics APIs
21+
where you issue drawing commands sequentially, AtomGL replaces the entire display content with a new
22+
list on each update.
23+
24+
### Z-Ordering
25+
26+
Items in the display list follow a specific rendering order:
27+
- Items are rendered from **tail to head** (reverse order)
28+
- The **first item** in the list has the highest z-order (appears on top)
29+
- The **last item** has the lowest z-order (appears at the bottom)
30+
31+
```elixir
32+
# Example: "Hello" text over a gray background
33+
items = [
34+
{:text, 10, 20, :default16px, 0x000000, 0x808080, "Hello."}, # Drawn last (on top)
35+
{:rect, 0, 0, width, height, 0x808080} # Drawn first (background)
36+
]
37+
```
38+
39+
**Best Practice:** Always include a full-screen rectangle as the last item in your list to clear the
40+
screen with a background color.
41+
42+
### Declarative vs. Imperative
43+
44+
AtomGL's declarative approach means:
45+
- Each `update` call **completely replaces** the previous display list
46+
- There is no hidden global display state
47+
- You describe *what* should be displayed, not *how* to draw it
48+
- Perfect fit for functional programming paradigms
49+
50+
```erlang
51+
% First update: show background
52+
BackgroundScene = [
53+
{rect, 0, 0, 1024, 600, 16#FFFFFF}
54+
],
55+
ok = port:call(Display, {update, BackgroundScene}),
56+
57+
% Second update: completely replaces the previous scene
58+
ColoredRectScene = [
59+
{rect, 150, 150, 80, 80, 16#FF0000}, % Red square (top)
60+
{rect, 250, 150, 80, 80, 16#00FF00}, % Green square
61+
{rect, 350, 150, 80, 80, 16#0000FF}, % Blue square
62+
{rect, 0, 0, 1024, 600, 16#FFFFFF} % White background (bottom)
63+
],
64+
ok = port:call(Display, {update, ColoredRectScene})
65+
```
66+
67+
Note: Without a background rectangle, some drivers may retain the previous image, but this is not a
68+
guaranteed feature.
69+
70+
### Memory Efficiency
71+
72+
A key benefit of the display list approach is **memory efficiency**. Unlike traditional graphics
73+
systems that require a complete frame buffer in memory, AtomGL drivers can:
74+
- Draw everything in smaller chunks or line by line
75+
- Evaluate display content pixel by pixel on-the-fly
76+
- Drive displays that would require more frame buffer memory than available internal RAM
77+
78+
This makes it possible for microcontrollers with limited memory to drive high-resolution displays
79+
that would otherwise be impossible to support with traditional frame buffer approaches.
1580

1681
## Supported Hardware
1782

18-
* `ilitek,ili9341` / `ilitek,ili9342c`: ILI9341 / ILI9342c: 240x320, 16 bit colors, supported
19-
* `waveshare,5in65-acep-7c`: Waveshare 7-color 5.65" ACeP display module: 600x480, 7 colors +
83+
* `ilitek,ili9341` / `ilitek,ili9342c`: ILI9341 / ILI9342C - 240x320, 16-bit colors
84+
* `waveshare,5in65-acep-7c`: Waveshare 7-color 5.65" ACeP display module - 600x480, 7 colors +
2085
software dithering
21-
* `sharp,memory-lcd`: Sharp Memory LCDs: 400x240 1-bit monochromatic
22-
* `solomon-systech,ssd1306`: Solomon Systech SSD1306: 128x64 1-bit monochromatic
23-
* `sino-wealth,sh1106`: Sino Wealth SH1106: 128x64 1-bit monochromatic
86+
* `sharp,memory-lcd`: Sharp Memory LCDs - 400x240, 1-bit monochrome
87+
* `solomon-systech,ssd1306`: Solomon Systech SSD1306 - 128x64, 1-bit monochrome
88+
* `sino-wealth,sh1106`: Sino Wealth SH1106 - 128x64, 1-bit monochrome
89+
90+
[SDL Linux display](sdl_display/) is also supported and can be built as an AtomVM plugin.
91+
92+
## Platform Support
93+
94+
AtomGL is currently compatible with **ESP32** microcontrollers. Linux with SDL is also supported for
95+
testing and development purposes.
96+
97+
### Building for ESP32
98+
99+
Place AtomGL in the ESP32 components directory as a regular ESP32 component and build using the
100+
standard ESP-IDF process:
101+
102+
```bash
103+
# Add AtomGL to your ESP32 project's components directory
104+
cd /path/to/AtomVM/src/platforms/esp32/components/
105+
git clone [email protected]:atomvm/atomgl.git
106+
107+
# Build with ESP-IDF
108+
idf.py build
109+
```
24110

25-
[SDL Linux display](sdl_display/) is also supported and can be built as AtomVM plugin.
111+
### Building for Linux (SDL)
112+
113+
For testing on Linux using SDL, see the [SDL display driver documentation](sdl_display/README.md).
26114

27115
## Getting Started
28116

29-
1. Add this component to the ESP32 components directory
30-
2. Open a display port using the right options
31-
3. Start an [avm_scene](https://github.com/atomvm/avm_scene) that will push updates to the display
117+
### Basic Setup
118+
119+
1. Open a display port using the appropriate options
120+
2. Start an [avm_scene](https://github.com/atomvm/avm_scene) to push updates to the display
32121

33-
### Display Option Example
122+
### Display Configuration Example
34123

35-
The following is an example for ILI9341:
124+
Example configuration for ILI9341:
36125

37126
```elixir
38-
[...]
39-
# spi is already open
40-
41-
ili_display_opts = [
42-
width: 320,
43-
height: 240,
44-
compatible: "ilitek,ili9341",
45-
reset: 18,
46-
cs: 22,
47-
dc: 21,
48-
backlight: 5,
49-
backlight_active: :low,
50-
backlight_enabled: true,
51-
rotation: 1,
52-
enable_tft_invon: false,
53-
spi_host: spi
127+
# spi is already open
128+
129+
ili_display_opts = [
130+
width: 320,
131+
height: 240,
132+
compatible: "ilitek,ili9341",
133+
reset: 18,
134+
cs: 22,
135+
dc: 21,
136+
backlight: 5,
137+
backlight_active: :low,
138+
backlight_enabled: true,
139+
rotation: 1,
140+
enable_tft_invon: false,
141+
spi_host: spi
142+
]
143+
144+
:erlang.open_port({:spawn, "display"}, display_opts)
145+
```
146+
147+
### Direct Port Usage
148+
149+
For direct port communication (without avm_scene):
150+
151+
```erlang
152+
% Create your display list
153+
DisplayList = [
154+
{text, 10, 20, default16px, 16#000000, transparent, "Hello, World!"},
155+
{rect, 0, 0, 320, 240, 16#FFFFFF} % White background
156+
],
157+
158+
% Update the display
159+
port:call(DisplayPort, {update, DisplayList})
160+
```
161+
162+
### Using avm_scene (Recommended)
163+
164+
The `avm_scene` library provides a more ergonomic interface that handles the update mechanics:
165+
166+
```elixir
167+
[...]
168+
169+
def start_link(args, opts) do
170+
:avm_scene.start_link(__MODULE__, args, opts)
171+
end
172+
173+
[...]
174+
175+
def handle_info(:show_hello, %{width: width, height: height} = state) do
176+
items = [
177+
{:text, 10, 20, :default16px, 0x000000, 0x808080, "Hello."},
178+
{:rect, 0, 0, width, height, 0x808080}
54179
]
55180

56-
:erlang.open_port({:spawn, "display"}, display_opts)
57-
[...]
181+
{:noreply, state, [{:push, items}]}
182+
end
183+
184+
[...]
58185
```
59186

60187
## Primitives
61188

62-
The display driver takes care of drawing a list of primitive items. Such as:
189+
AtomGL supports the following primitive types:
190+
191+
* **image** - Display bitmap images
192+
* **scaled_cropped_image** - Display scaled and cropped images
193+
* **rect** - Draw filled rectangles
194+
* **text** - Render text with specified font
195+
196+
Example display list with multiple primitives:
63197

64198
```elixir
65-
rendered = [
66-
{:text, 10, 20, :default16px, 0x000000, 0x808080, "Hello."},
67-
{:rect, 0, 0, width, height, 0x808080}
68-
]
199+
rendered = [
200+
{:text, 10, 20, :default16px, 0x000000, 0x808080, "Hello."},
201+
{:rect, 0, 0, width, height, 0x808080}
202+
]
69203
```
70204

71-
Following primitives are supported:
72-
* image
73-
* scaled_cropped_image
74-
* rect
75-
* text
205+
See the [primitives documentation](docs/primitives.md) for detailed information about each primitive type.
206+
207+
## Important Notes
208+
209+
- The display list approach means you always define the complete display state
210+
- Each update replaces the entire previous state (no incremental drawing)
211+
- Z-order is determined by position in the list (first = topmost)
212+
- Always include a background rectangle to ensure proper screen clearing
213+
- The `update` command is the only imperative operation
214+
215+
## License
76216

77-
See also [documentation](docs/primitives.md) for more information.
217+
Apache-2.0

0 commit comments

Comments
 (0)