Skip to content

Commit

Permalink
bindings for erlang/egd
Browse files Browse the repository at this point in the history
  • Loading branch information
stevetoro committed Sep 8, 2024
0 parents commit a8f89d1
Show file tree
Hide file tree
Showing 16 changed files with 1,871 additions and 0 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: test

on:
push:
branches:
- master
- main
pull_request:

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
otp-version: "26.0.2"
gleam-version: "1.4.1"
rebar3-version: "3"
# elixir-version: "1.15.4"
- run: gleam deps download
- run: gleam test
- run: gleam format --check src test
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*.beam
*.ez
/build
erl_crash.dump
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# glee_gd

[![Package Version](https://img.shields.io/hexpm/v/glee_gd)](https://hex.pm/packages/glee_gd)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/glee_gd/)

Gleam bindings for the [Erlang Graphical Drawer (EGD)](https://github.com/erlang/egd) library.

```sh
gleam add glee_gd
```
```gleam
import glee_gd
pub fn main() {
let image = glee_gd.create(500, 500)
let color = glee_gd.color(#(109, 74, 126))
glee_gd.filled_ellipse(image, #(100, 400), #(400, 100), color)
let binary = glee_gd.render(image)
// Do something with binary in memory or write it to a file.
}
```

Further documentation can be found at <https://hexdocs.pm/glee_gd>.

## Development

```sh
gleam test # Run the tests
```
20 changes: 20 additions & 0 deletions gleam.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name = "glee_gd"
version = "1.0.0"

# Fill out these fields if you intend to generate HTML documentation or publish
# your project to the Hex package manager.
#
description = "Gleam bindings for erlang/egd."
licences = ["Apache-2.0"]
repository = { type = "github", user = "stevetoro", repo = "glee_gd" }
# links = [{ title = "Website", href = "" }]
#
# For a full reference of all the available options, you can have a look at
# https://gleam.run/writing-gleam/gleam-toml/.

[dependencies]
gleam_stdlib = ">= 0.40.0 and < 1.0.0"
simplifile = ">= 2.1.0 and < 3.0.0"

[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"
14 changes: 14 additions & 0 deletions manifest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# This file was generated by Gleam
# You typically do not need to edit this file

packages = [
{ name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" },
{ name = "gleam_stdlib", version = "0.40.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86606B75A600BBD05E539EB59FABC6E307EEEA7B1E5865AFB6D980A93BCB2181" },
{ name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" },
{ name = "simplifile", version = "2.1.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "BDD04F5D31D6D34E2EDFAEF0B68A6297AEC939888C3BFCE61133DE13857F6DA2" },
]

[requirements]
gleam_stdlib = { version = ">= 0.40.0 and < 1.0.0" }
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
simplifile = { version = ">= 2.1.0 and < 3.0.0" }
275 changes: 275 additions & 0 deletions src/egd.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%

%%
%% @doc egd - erlang graphical drawer
%%
%%

-module(egd).

-export([create/2, destroy/1, information/1]).
-export([text/5, line/4, color/1, color/2]).
-export([rectangle/4, filledRectangle/4, filledEllipse/4]).
-export([arc/4, arc/5]).
-export([render/1, render/2, render/3]).

-export([filledTriangle/5, polygon/3]).

-export([save/2]).

-include("egd.hrl").

%%==========================================================================
%% Type definitions
%%==========================================================================

%% @type egd_image()
%% @type font()
%% @type point() = {integer(), integer()}
%% @type color()
%% @type render_option() = {render_engine, opaque} | {render_engine, alpha}

-type egd_image() :: pid().
-type point() :: {non_neg_integer(), non_neg_integer()}.
-type render_option() :: {'render_engine', 'opaque'} | {'render_engine', 'alpha'}.
-type color() :: {float(), float(), float(), float()}.

%%==========================================================================
%% Interface functions
%%==========================================================================

%% @spec create(integer(), integer()) -> egd_image()
%% @doc Creates an image area and returns its reference.

-spec create(Width :: integer(), Height :: integer()) -> egd_image().

create(Width,Height) ->
spawn_link(fun() -> init(trunc(Width),trunc(Height)) end).


%% @spec destroy(egd_image()) -> ok
%% @doc Destroys the image.

-spec destroy(Image :: egd_image()) -> ok.

destroy(Image) ->
cast(Image, destroy).


%% @spec render(egd_image()) -> binary()
%% @equiv render(Image, png, [{render_engine, opaque}])

-spec render(Image :: egd_image()) -> binary().

render(Image) ->
render(Image, png, [{render_engine, opaque}]).

%% @spec render(egd_image(), png | raw_bitmap) -> binary()
%% @equiv render(Image, Type, [{render_engine, opaque}])

render(Image, Type) ->
render(Image, Type, [{render_engine, opaque}]).

%% @spec render(egd_image(), png | raw_bitmap, [render_option()]) -> binary()
%% @doc Renders a binary from the primitives specified by egd_image(). The
%% binary can either be a raw bitmap with rgb tripplets or a binary in png
%% format.

-spec render(
Image :: egd_image(),
Type :: 'png' | 'raw_bitmap' | 'eps',
Options :: [render_option()]) -> binary().

render(Image, Type, Options) ->
{render_engine, RenderType} = proplists:lookup(render_engine, Options),
call(Image, {render, Type, RenderType}).


%% @spec information(egd_image()) -> ok
%% @hidden
%% @doc Writes out information about the image. This is a debug feature
%% mainly.

information(Pid) ->
cast(Pid, information).

%% @spec line(egd_image(), point(), point(), color()) -> ok
%% @doc Creates a line object from P1 to P2 in the image.

-spec line(
Image :: egd_image(),
P1 :: point(),
P2 :: point(),
Color :: color()) -> 'ok'.

line(Image, P1, P2, Color) ->
cast(Image, {line, P1, P2, Color}).

%% @spec color( Value | Name ) -> color()
%% where
%% Value = {byte(), byte(), byte()} | {byte(), byte(), byte(), byte()}
%% Name = black | silver | gray | white | maroon | red | purple | fuchia | green | lime | olive | yellow | navy | blue | teal | aqua
%% @doc Creates a color reference.

-spec color(Value :: {byte(), byte(), byte()} | {byte(), byte(), byte(), byte()} | atom()) ->
color().

color(Color) ->
egd_primitives:color(Color).

%% @spec color(egd_image(), {byte(), byte(), byte()}) -> color()
%% @doc Creates a color reference.
%% @hidden

color(_Image, Color) ->
egd_primitives:color(Color).

%% @spec text(egd_image(), point(), font(), string(), color()) -> ok
%% @doc Creates a text object.

text(Image, P, Font, Text, Color) ->
cast(Image, {text, P, Font, Text, Color}).

%% @spec rectangle(egd_image(), point(), point(), color()) -> ok
%% @doc Creates a rectangle object.

rectangle(Image, P1, P2, Color) ->
cast(Image, {rectangle, P1, P2, Color}).

%% @spec filledRectangle(egd_image(), point(), point(), color()) -> ok
%% @doc Creates a filled rectangle object.

filledRectangle(Image, P1, P2, Color) ->
cast(Image, {filled_rectangle, P1, P2, Color}).

%% @spec filledEllipse(egd_image(), point(), point(), color()) -> ok
%% @doc Creates a filled ellipse object.

filledEllipse(Image, P1, P2, Color) ->
cast(Image, {filled_ellipse, P1, P2, Color}).

%% @spec filledTriangle(egd_image(), point(), point(), point(), color()) -> ok
%% @hidden
%% @doc Creates a filled triangle object.

filledTriangle(Image, P1, P2, P3, Color) ->
cast(Image, {filled_triangle, P1, P2, P3, Color}).

%% @spec polygon(egd_image(), [point()], color()) -> ok
%% @hidden
%% @doc Creates a filled filled polygon object.

polygon(Image, Pts, Color) ->
cast(Image, {polygon, Pts, Color}).

%% @spec arc(egd_image(), point(), point(), color()) -> ok
%% @hidden
%% @doc Creates an arc with radius of bbx corner.

arc(Image, P1, P2, Color) ->
cast(Image, {arc, P1, P2, Color}).

%% @spec arc(egd_image(), point(), point(), integer(), color()) -> ok
%% @hidden
%% @doc Creates an arc.

arc(Image, P1, P2, D, Color) ->
cast(Image, {arc, P1, P2, D, Color}).

%% @spec save(binary(), string()) -> ok
%% @doc Saves the binary to file.

save(Binary, Filename) when is_binary(Binary) ->
ok = file:write_file(Filename, Binary),
ok.
% ---------------------------------
% Aux functions
% ---------------------------------

cast(Pid, Command) ->
Pid ! {egd, self(), Command},
ok.

call(Pid, Command) ->
Pid ! {egd, self(), Command},
receive {egd, Pid, Result} -> Result end.

% ---------------------------------
% Server loop
% ---------------------------------

init(W,H) ->
Image = egd_primitives:create(W,H),
loop(Image).

loop(Image) ->
receive
% Quitting
{egd, _Pid, destroy} -> ok;

% Rendering
{egd, Pid, {render, BinaryType, RenderType}} ->
case BinaryType of
raw_bitmap ->
Bitmap = egd_render:binary(Image, RenderType),
Pid ! {egd, self(), Bitmap},
loop(Image);
eps ->
Eps = egd_render:eps(Image),
Pid ! {egd, self(), Eps},
loop(Image);
png ->
Bitmap = egd_render:binary(Image, RenderType),
Png = egd_png:binary(
Image#image.width,
Image#image.height,
Bitmap),
Pid ! {egd, self(), Png},
loop(Image);
Unhandled ->
Pid ! {egd, self(), {error, {format, Unhandled}}},
loop(Image)
end;

% Drawing primitives
{egd, _Pid, {line, P1, P2, C}} ->
loop(egd_primitives:line(Image, P1, P2, C));
{egd, _Pid, {text, P, Font, Text, C}} ->
loop(egd_primitives:text(Image, P, Font, Text, C));
{egd, _Pid, {filled_ellipse, P1, P2, C}} ->
loop(egd_primitives:filledEllipse(Image, P1, P2, C));
{egd, _Pid, {filled_rectangle, P1, P2, C}} ->
loop(egd_primitives:filledRectangle(Image, P1, P2, C));
{egd, _Pid, {filled_triangle, P1, P2, P3, C}} ->
loop(egd_primitives:filledTriangle(Image, P1, P2, P3, C));
{egd, _Pid, {polygon, Pts, C}} ->
loop(egd_primitives:polygon(Image, Pts, C));
{egd, _Pid, {arc, P1, P2, C}} ->
loop(egd_primitives:arc(Image, P1, P2, C));
{egd, _Pid, {arc, P1, P2, D, C}} ->
loop(egd_primitives:arc(Image, P1, P2, D, C));
{egd, _Pid, {rectangle, P1, P2, C}} ->
loop(egd_primitives:rectangle(Image, P1, P2, C));
{egd, _Pid, information} ->
egd_primitives:info(Image),
loop(Image);
_ ->
loop(Image)
end.
Loading

0 comments on commit a8f89d1

Please sign in to comment.