Skip to content

Commit

Permalink
Merge pull request #1 from pappersverk/development
Browse files Browse the repository at this point in the history
Add extra primitives
  • Loading branch information
luisgabrielroldan authored Dec 21, 2019
2 parents c240bb2 + 594c94e commit 5afe5af
Show file tree
Hide file tree
Showing 10 changed files with 209 additions and 161 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ Graphic primitives
- [x] Points
- [x] Lines
- [x] Rects
- [ ] Circles
- [ ] Polygons
- [ ] Filled Rects
- [x] Filled Rects
- [x] Circles
- [ ] Filled Circles
- [ ] Polygons
- [ ] Filled Polygons
- [ ] Text rendering
- [ ] Text rendering (Try [Chisel](https://github.com/luisgabrielroldan/chisel))



Expand All @@ -45,7 +45,7 @@ Graphic primitives
```elixir
def deps do
[
{:oled, "~> 0.1.0"}
{:oled, "~> 0.3.0"}
]
end
```
Expand Down
29 changes: 29 additions & 0 deletions lib/oled/display.ex
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ defmodule OLED.Display do
def rect(x, y, width, height, opts \\ []),
do: Server.rect(@me, x, y, width, height, opts)

def circle(x0, y0, r, opts \\ []),
do: Server.circle(@me, x0, y0, r, opts)

def fill_rect(x, y, width, height, opts \\ []),
do: Server.fill_rect(@me, x, y, width, height, opts)

def get_dimensions(),
do: Server.get_dimensions(@me)
end
Expand Down Expand Up @@ -178,6 +184,29 @@ defmodule OLED.Display do
opts :: Server.pixel_opts()
) :: :ok

@doc """
Draw a circle
Origin `(x0, y0)` with radius `r`.
"""
@callback circle(
x0 :: integer(),
y0 :: integer(),
r :: integer(),
opts :: Server.pixel_opts()
) :: :ok

@doc """
Draw a filled rect
"""
@callback fill_rect(
x :: integer(),
y :: integer(),
width :: integer(),
height :: integer(),
opts :: Server.pixel_opts()
) :: :ok

@doc """
Get display dimensions
"""
Expand Down
46 changes: 31 additions & 15 deletions lib/oled/display/impl/ssd_1306.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,6 @@ defmodule OLED.Display.Impl.SSD1306 do
@ssd1306_deactivate_scroll 0x2E
# @ssd1306_activate_scroll 0x2F

# Full width of SSD1306 controller memory
@lcd_total_width 128
# Full height of SSD1306 controller memory
@lcd_total_height 64

@default_config [
width: 128,
height: 64,
Expand All @@ -62,10 +57,12 @@ defmodule OLED.Display.Impl.SSD1306 do
external_vcc: nil

defdelegate put_pixel(state, x, y, opts), to: Draw
defdelegate circle(state, x0, y0, r, opts), to: Draw
defdelegate rect(state, x, y, width, height, opts), to: Draw
defdelegate line(state, x1, y1, x2, y2, opts), to: Draw
defdelegate line_h(state, x, y, width, opts), to: Draw
defdelegate line_v(state, x, y, height, opts), to: Draw
defdelegate fill_rect(state, x, y, width, height, opts), to: Draw

def init_dev(config) do
case Keyword.get(config, :type) do
Expand Down Expand Up @@ -125,27 +122,46 @@ defmodule OLED.Display.Impl.SSD1306 do
end

def display(%__MODULE__{} = state, opts \\ []) do
%{buffer: buffer, width: width} = state
opts = Keyword.merge(@display_opts, opts)

memory_mode = get_memory_mode(opts[:memory_mode] || :horizontal)
buffer = translate_buffer(buffer, width, opts[:memory_mode])

state
|> command([@ssd1306_memorymode, memory_mode])
|> command([@ssd1306_pageaddr, 0, trunc(state.height / 8 - 1)])
|> command([@ssd1306_columnaddr, 0, state.width - 1])
|> transfer(state.buffer)
|> command([@ssd1306_memorymode, 0])
display_frame(state, buffer, opts)
end

defp translate_buffer(buffer, width, :horizontal) do
for <<page::binary-size(width) <- buffer>> do
for(<<b::1 <- page>>, do: b)
|> Enum.chunk_every(width)
|> Enum.zip()
|> Enum.map(fn bits ->
bits
|> Tuple.to_list()
|> Enum.reverse()
|> Enum.into(<<>>, fn bit -> <<bit::1>> end)
end)
end
|> List.flatten()
|> Enum.into(<<>>)
end

def display_frame(%__MODULE__{} = state, data, opts) do
memory_mode = get_memory_mode(opts[:memory_mode] || :horizontal)

if byte_size(data) == state.width * state.height / 8 do
display(%{state | buffer: data}, opts)
state
|> command([@ssd1306_memorymode, memory_mode])
|> command([@ssd1306_pageaddr, 0, trunc(state.height / 8 - 1)])
|> command([@ssd1306_columnaddr, 0, state.width - 1])
|> transfer(data)
|> command([@ssd1306_memorymode, 0])
else
{:error, :invalid_data_size}
end
end

def clear_buffer(%__MODULE__{} = state, pixel_state)
def clear_buffer(%__MODULE__{width: w, height: h} = state, pixel_state)
when pixel_state in [:on, :off] do
value =
case pixel_state do
Expand All @@ -157,7 +173,7 @@ defmodule OLED.Display.Impl.SSD1306 do
end

buffer =
for _ <- 1..trunc(@lcd_total_width * @lcd_total_height / 8), into: <<>> do
for _ <- 1..trunc(w * h / 8), into: <<>> do
value
end

Expand Down
166 changes: 61 additions & 105 deletions lib/oled/display/impl/ssd_1306/draw.ex
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
defmodule OLED.Display.Impl.SSD1306.Draw do
@moduledoc false

@bits_per_seg 8

use Bitwise, only_operators: true

def put_pixel(%{width: w, height: h} = state, x, y, opts)
when x >= 0 and x < w and y >= 0 and y < h do
%{buffer: buffer, width: width} = state

seg_offset = x + trunc(y / @bits_per_seg) * width
offset = width * y + x

<<prev::bitstring-size(offset), p::1, next::bitstring>> = buffer

<<
prev::bytes-size(seg_offset),
seg::bytes-size(1),
rest::binary
>> = buffer
pixel_state = opts[:state] || :on
mode = opts[:mode] || :normal

seg_value =
calc_seg_value(
seg,
y,
opts[:state] || :on,
opts[:mode] || :normal
)
np = if pixel_state == :on, do: 1, else: 0

np =
case mode do
:normal -> p ||| np
:xor -> p ^^^ np
end

buffer = build_buffer(prev, seg_value, rest)
buffer = <<prev::bitstring, np::1, next::bitstring>>

%{state | buffer: buffer}
end
Expand All @@ -50,6 +47,12 @@ defmodule OLED.Display.Impl.SSD1306.Draw do
end
end

def fill_rect(state, x, y, width, height, opts) do
Enum.reduce(y..(y + height), state, fn y1, state ->
line_h(state, x, y1, width, opts)
end)
end

def line(state, x1, y1, x2, y2, opts) do
# sort points
{x1, y1, x2, y2} =
Expand All @@ -68,71 +71,13 @@ defmodule OLED.Display.Impl.SSD1306.Draw do
end)
end

def line_h(state, x, y, width, opts) do
cond do
x < 0 and width + x < 0 ->
:skip

x >= state.width ->
:skip

y >= state.height ->
:skip

y < 0 ->
:skip

true ->
x2 =
cond do
x < 0 -> 0
x > state.width - 1 -> state.width - 1
true -> x
end

y2 =
cond do
y < 0 -> 0
y > state.height - 1 -> state.height - 1
true -> y
end

width2 = width - (x2 - x)
def line_h(state, _x, _y, width, _opts) when width < 1,
do: state

{x2, y2, width2}
end
|> case do
{x, y, width} ->
seg_offset = x + trunc(y / @bits_per_seg) * state.width

total_segs =
if width > state.width - x do
state.width - x
else
width
end

<<
prev::bytes-size(seg_offset),
seg::bytes-size(total_segs),
rest::binary
>> = state.buffer

seg =
write_line_h(
seg,
y,
opts[:state] || :on,
opts[:mode] || :normal
)

buffer = <<prev <> seg <> rest>>

%{state | buffer: buffer}

:skip ->
state
end
def line_h(state, x, y, width, opts) do
state
|> put_pixel(x, y, opts)
|> line_h(x + 1, y, width - 1, opts)
end

def line_v(state, _x, _y, height, _opts) when height < 1,
Expand All @@ -144,36 +89,47 @@ defmodule OLED.Display.Impl.SSD1306.Draw do
|> line_v(x, y + 1, height - 1, opts)
end

defp write_line_h(<<s::bytes-size(1), tail::binary>>, y, state, mode) do
seg_value =
calc_seg_value(
s,
y,
state,
mode
)
def circle(state, x0, y0, r, opts) do
x = 0
y = r

<<(<<seg_value>> <> write_line_h(tail, y, state, mode))>>
end

defp write_line_h(<<>>, _y, _state, _mode),
do: <<>>

defp build_buffer(prev, seg_value, rest) do
seg = <<seg_value>>
state =
state
|> put_pixel(x0, y0 + r, opts)
|> put_pixel(x0, y0 - r, opts)
|> put_pixel(x0 + r, y0, opts)
|> put_pixel(x0 - r, y0, opts)

<<prev <> seg <> rest>>
draw_circle(x0, y0, x, y, 1 - r, 1, -2 * r, opts, state)
end

defp calc_seg_value(<<seg_value>>, y, :on, :normal),
do: seg_value ||| 1 <<< rem(y, @bits_per_seg)
defp draw_circle(x0, y0, x, y, f, ddF_x, ddF_y, opts, state)
when x < y do
{y, ddF_y, f} =
if f >= 0 do
{y - 1, ddF_y + 2, f + ddF_y}
else
{y, ddF_y, f}
end

defp calc_seg_value(<<seg_value>>, y, :off, :normal),
do: seg_value &&& ~~~(1 <<< rem(y, @bits_per_seg))
x = x + 1
ddF_x = ddF_x + 2
f = f + ddF_x

defp calc_seg_value(<<seg_value>>, y, :on, :xor),
do: seg_value ^^^ (1 <<< rem(y, @bits_per_seg))
state =
state
|> put_pixel(x0 + x, y0 + y, opts)
|> put_pixel(x0 - x, y0 + y, opts)
|> put_pixel(x0 + x, y0 - y, opts)
|> put_pixel(x0 - x, y0 - y, opts)
|> put_pixel(x0 + y, y0 + x, opts)
|> put_pixel(x0 - y, y0 + x, opts)
|> put_pixel(x0 + y, y0 - x, opts)
|> put_pixel(x0 - y, y0 - x, opts)

draw_circle(x0, y0, x, y, f, ddF_x, ddF_y, opts, state)
end

defp calc_seg_value(<<seg_value>>, _y, _state, _mode),
do: seg_value
defp draw_circle(_x0, _y0, _x, _y, _f, _ddF_x, _ddF_y, _opts, state),
do: state
end
Loading

0 comments on commit 5afe5af

Please sign in to comment.