Skip to content

Commit e2b3e65

Browse files
committed
ImageSurface
* Add `ImageSurface` struct in Elixir and Rust * `new` * `format` * `width` * `height` * `stride` * `write_to_png` * Add `Format` enum/atoms * Start adding `XairoError` mapping to cairo::Error - [ ] (eventually move existing errors over) * Add `Utilities` module * `stride_for_width`
1 parent b36d229 commit e2b3e65

File tree

13 files changed

+314
-2
lines changed

13 files changed

+314
-2
lines changed

.projections.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"lib/*.ex": {
3+
"alternate": "test/{}_test.exs",
4+
"type": "source",
5+
"template": [
6+
"defmodule {camelcase|capitalize|dot} do",
7+
"end"
8+
]
9+
},
10+
"test/*_test.exs": {
11+
"alternate": "lib/{}.ex",
12+
"type": "test",
13+
"template": [
14+
"defmodule {camelcase|capitalize|dot}Test do",
15+
" use ExUnit.Case, async: true",
16+
"",
17+
" alias {camelcase|capitalize|dot}",
18+
"end"
19+
]
20+
}
21+
}

lib/xairo/image_surface.ex

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
defmodule Xairo.ImageSurface do
2+
@moduledoc false
3+
4+
defstruct [:resource]
5+
6+
def new(format, width, height) do
7+
with {:ok, image_surface} <- Xairo.Native.image_surface_create(format, width, height) do
8+
%__MODULE__{
9+
resource: image_surface
10+
}
11+
end
12+
end
13+
14+
def format(%__MODULE__{resource: resource}) do
15+
Xairo.Native.image_surface_format(resource)
16+
end
17+
18+
def width(%__MODULE__{resource: resource}) do
19+
Xairo.Native.image_surface_width(resource)
20+
end
21+
22+
def height(%__MODULE__{resource: resource}) do
23+
Xairo.Native.image_surface_height(resource)
24+
end
25+
26+
def stride(%__MODULE__{resource: resource}) do
27+
Xairo.Native.image_surface_stride(resource)
28+
end
29+
30+
def write_to_png(%__MODULE__{resource: resource}, filename) do
31+
Xairo.Native.image_surface_write_to_png(resource, filename)
32+
end
33+
end

lib/xairo/native.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,5 +120,14 @@ defmodule Xairo.Native do
120120

121121
def set_surface_pattern_source(_i, _p), do: error()
122122

123+
def image_surface_create(_f, _w, _h), do: error()
124+
def image_surface_format(_is), do: error()
125+
def image_surface_width(_is), do: error()
126+
def image_surface_height(_is), do: error()
127+
def image_surface_stride(_is), do: error()
128+
def image_surface_write_to_png(_is, _f), do: error()
129+
130+
def stride_for_width(_f, _w), do: error()
131+
123132
defp error, do: :erlang.nif_error(:nif_not_loaded)
124133
end

lib/xairo/utilities.ex

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
defmodule Xairo.Utilities do
2+
@moduledoc false
3+
4+
def stride_for_width(format, width) do
5+
case Xairo.Native.stride_for_width(format, width) do
6+
{:ok, stride} -> stride
7+
{:error, _} = error -> error
8+
error -> {:error, error}
9+
end
10+
end
11+
end

native/xairo/src/enums.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
use crate::error::XairoError;
2+
use std::convert::From;
3+
4+
#[derive(Copy, Clone, Debug, NifUnitEnum)]
5+
pub enum Format {
6+
Invalid,
7+
Argb32,
8+
Rgb24,
9+
A8,
10+
A1,
11+
Rgb16_565,
12+
Rgb30,
13+
}
14+
15+
impl Format {
16+
fn stride_for_width(self, width: u32) -> Result<i32, XairoError> {
17+
match cairo::Format::from(self).stride_for_width(width) {
18+
Ok(stride) => Ok(stride),
19+
Err(err) => Err(err.into()), //Err(Error::Stride),
20+
}
21+
}
22+
}
23+
24+
#[rustler::nif]
25+
fn stride_for_width(format: Format, width: u32) -> Result<i32, XairoError> {
26+
if width <= i32::MAX as u32 {
27+
format.stride_for_width(width)
28+
} else {
29+
Err(XairoError::InvalidStride)
30+
}
31+
}
32+
33+
impl From<Format> for cairo::Format {
34+
fn from(format: Format) -> Self {
35+
match format {
36+
Format::Invalid => cairo::Format::Invalid,
37+
Format::Argb32 => cairo::Format::ARgb32,
38+
Format::Rgb24 => cairo::Format::Rgb24,
39+
Format::A8 => cairo::Format::A8,
40+
Format::A1 => cairo::Format::A1,
41+
Format::Rgb16_565 => cairo::Format::Rgb16_565,
42+
Format::Rgb30 => cairo::Format::Rgb30,
43+
}
44+
}
45+
}
46+
47+
impl From<cairo::Format> for Format {
48+
fn from(format: cairo::Format) -> Self {
49+
match format {
50+
cairo::Format::ARgb32 => Format::Argb32,
51+
cairo::Format::Rgb24 => Format::Rgb24,
52+
cairo::Format::A8 => Format::A8,
53+
cairo::Format::A1 => Format::A1,
54+
cairo::Format::Rgb16_565 => Format::Rgb16_565,
55+
cairo::Format::Rgb30 => Format::Rgb30,
56+
_ => Format::Invalid,
57+
}
58+
}
59+
}

native/xairo/src/error.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,42 @@ pub enum Error {
4949
RadialCircles,
5050
#[error("Error retrieving color for solid pattern")]
5151
SolidPatternColor,
52+
#[error("Error calculating stride for width")]
53+
Stride,
5254
}
5355

5456
impl rustler::Encoder for Error {
5557
fn encode<'a>(&self, env: Env<'a>) -> Term<'a> {
5658
format!("{}", self).encode(env)
5759
}
5860
}
61+
62+
#[derive(Copy, Clone, Debug, NifUnitEnum)]
63+
pub enum XairoError {
64+
WriteError,
65+
InvalidSize,
66+
InvalidStride,
67+
UnknownError,
68+
}
69+
70+
impl From<XairoError> for cairo::Error {
71+
fn from(error: XairoError) -> Self {
72+
match error {
73+
XairoError::InvalidStride => cairo::Error::InvalidStride,
74+
XairoError::InvalidSize => cairo::Error::InvalidSize,
75+
XairoError::WriteError => cairo::Error::WriteError,
76+
XairoError::UnknownError => cairo::Error::LastStatus,
77+
}
78+
}
79+
}
80+
81+
impl From<cairo::Error> for XairoError {
82+
fn from(error: cairo::Error) -> Self {
83+
match error {
84+
cairo::Error::InvalidStride => XairoError::InvalidStride,
85+
cairo::Error::InvalidSize => XairoError::InvalidSize,
86+
cairo::Error::WriteError => XairoError::WriteError,
87+
_ => XairoError::UnknownError,
88+
}
89+
}
90+
}

native/xairo/src/image_surface.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use crate::enums::Format;
2+
use crate::error::{Error, XairoError};
3+
use rustler::ResourceArc;
4+
use std::fs::File;
5+
6+
pub struct ImageSurfaceRaw {
7+
pub surface: cairo::ImageSurface,
8+
}
9+
10+
unsafe impl Send for ImageSurfaceRaw {}
11+
unsafe impl Sync for ImageSurfaceRaw {}
12+
13+
pub type ImageSurface = ResourceArc<ImageSurfaceRaw>;
14+
15+
#[rustler::nif]
16+
fn image_surface_create(
17+
format: Format,
18+
width: i32,
19+
height: i32,
20+
) -> Result<ImageSurface, XairoError> {
21+
let format: cairo::Format = format.into();
22+
match cairo::ImageSurface::create(format, width, height) {
23+
Ok(surface) => Ok(ResourceArc::new(ImageSurfaceRaw { surface })),
24+
Err(err) => Err(err.into()),
25+
}
26+
}
27+
28+
#[rustler::nif]
29+
fn image_surface_format(surface: ImageSurface) -> Format {
30+
surface.surface.format().into()
31+
}
32+
33+
#[rustler::nif]
34+
fn image_surface_width(surface: ImageSurface) -> i32 {
35+
surface.surface.width()
36+
}
37+
38+
#[rustler::nif]
39+
fn image_surface_height(surface: ImageSurface) -> i32 {
40+
surface.surface.height()
41+
}
42+
43+
#[rustler::nif]
44+
fn image_surface_stride(surface: ImageSurface) -> i32 {
45+
surface.surface.stride()
46+
}
47+
48+
#[rustler::nif]
49+
fn image_surface_write_to_png(surface: ImageSurface, filename: String) -> Result<(), XairoError> {
50+
match File::create(filename) {
51+
Ok(mut file) => match surface.surface.write_to_png(&mut file) {
52+
Ok(_) => Ok(()),
53+
Err(_) => Err(XairoError::WriteError),
54+
},
55+
Err(_) => Err(XairoError::WriteError),
56+
}
57+
}

native/xairo/src/lib.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ use rustler::{Env, Term};
44

55
mod color;
66
mod drawing;
7+
mod enums;
78
mod error;
89
mod extents;
10+
mod image_surface;
11+
use image_surface::ImageSurfaceRaw;
912
mod line_cap;
1013
mod line_join;
1114
mod linear_gradient;
@@ -133,7 +136,16 @@ rustler::init!(
133136
path::copy_path_flat,
134137
path::append_path,
135138
path::get_tolerance,
136-
path::set_tolerance
139+
path::set_tolerance,
140+
// image_surface
141+
image_surface::image_surface_create,
142+
image_surface::image_surface_format,
143+
image_surface::image_surface_width,
144+
image_surface::image_surface_height,
145+
image_surface::image_surface_stride,
146+
image_surface::image_surface_write_to_png,
147+
// enums
148+
enums::stride_for_width,
137149
],
138150
load = on_load
139151
);
@@ -145,5 +157,6 @@ fn on_load(env: Env, _info: Term) -> bool {
145157
rustler::resource!(XairoLinearGradient, env);
146158
rustler::resource!(XairoRadialGradient, env);
147159
rustler::resource!(XairoSolidPattern, env);
160+
rustler::resource!(ImageSurfaceRaw, env);
148161
true
149162
}

priv/native/libxairo.so

164 KB
Binary file not shown.

test/helpers/image_helpers.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ defmodule Xairo.Helpers.ImageHelpers do
3939
:ok = File.rm(filename)
4040
end
4141

42-
defp hash(file) do
42+
def hash(file) do
4343
File.stream!(file)
4444
|> Enum.reduce(:crypto.hash_init(:sha256), fn line, acc ->
4545
:crypto.hash_update(acc, line)

0 commit comments

Comments
 (0)