ruby-avif 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9e1602d362c5f0fabc60f05abadf9edb5c0c5430fc27d0072b4b6c62cd54cb7b
4
+ data.tar.gz: 739a3e9949dbaa3720ab2762e5264812792a63dd58559e8099868b3f64ba72fb
5
+ SHA512:
6
+ metadata.gz: ef50fe8c82cf526019ea6dd38c6ca3dc5f1536bc47ea8590e1f42ce12ee8527b7d7bca1b1164ad5d522f2b11c37e4dbc1b05cf7bc3a4b033a712c720162dca79
7
+ data.tar.gz: 5c67622865f6064ac5036595ae697447cf153a53c5f3e7015f67ed05a504ea4f6f7f7699f7071ae96d3a14b766e1a4703d8d5890e09ed37f6012487b95003380
data/ext/avif/avif.c ADDED
@@ -0,0 +1,136 @@
1
+ #include "ruby.h"
2
+ #include "ruby/thread.h"
3
+ #include "avif/avif.h"
4
+ #include <stdio.h>
5
+ #include <string.h>
6
+ #include <stdlib.h>
7
+
8
+ static char *safe_strdup(const char *s) {
9
+ if (!s) return NULL;
10
+ size_t len = strlen(s) + 1;
11
+ char *p = malloc(len);
12
+ if (p) {
13
+ memcpy(p, s, len);
14
+ }
15
+ return p;
16
+ }
17
+
18
+ typedef struct {
19
+ char *path;
20
+ uint8_t *pixels;
21
+ uint32_t width;
22
+ uint32_t height;
23
+ avifResult result;
24
+ char *error_message;
25
+ } decode_payload_t;
26
+
27
+ static void *decode(void *arg) {
28
+ decode_payload_t *payload = (decode_payload_t *)arg;
29
+
30
+ payload->pixels = NULL;
31
+ payload->result = AVIF_RESULT_UNKNOWN_ERROR;
32
+ payload->error_message = NULL;
33
+
34
+ avifDecoder *decoder = avifDecoderCreate();
35
+ if (!decoder) {
36
+ payload->error_message = safe_strdup("avifDecoderCreate failed (out of memory)");
37
+ free(payload->path);
38
+ return NULL;
39
+ }
40
+
41
+ payload->result = avifDecoderSetIOFile(decoder, payload->path);
42
+ free(payload->path);
43
+ payload->path = NULL;
44
+ if (payload->result != AVIF_RESULT_OK) {
45
+ avifDecoderDestroy(decoder);
46
+ return NULL;
47
+ }
48
+
49
+ payload->result = avifDecoderParse(decoder);
50
+ if (payload->result != AVIF_RESULT_OK) {
51
+ avifDecoderDestroy(decoder);
52
+ return NULL;
53
+ }
54
+
55
+ payload->result = avifDecoderNextImage(decoder);
56
+ if (payload->result != AVIF_RESULT_OK) {
57
+ avifDecoderDestroy(decoder);
58
+ return NULL;
59
+ }
60
+
61
+ avifImage *image = decoder->image;
62
+ payload->width = image->width;
63
+ payload->height = image->height;
64
+
65
+ avifRGBImage rgb;
66
+ avifRGBImageSetDefaults(&rgb, image);
67
+ rgb.format = AVIF_RGB_FORMAT_RGBA;
68
+ rgb.depth = 8;
69
+
70
+ size_t pixel_size = (size_t)rgb.width * rgb.height * avifRGBImagePixelSize(&rgb);
71
+ payload->pixels = malloc(pixel_size);
72
+ if (!payload->pixels) {
73
+ payload->error_message = safe_strdup("Failed to allocate pixel buffer (out of memory)");
74
+ avifDecoderDestroy(decoder);
75
+ return NULL;
76
+ }
77
+ rgb.pixels = payload->pixels;
78
+ rgb.rowBytes = rgb.width * avifRGBImagePixelSize(&rgb);
79
+
80
+ payload->result = avifImageYUVToRGB(image, &rgb);
81
+ if (payload->result != AVIF_RESULT_OK) {
82
+ free(payload->pixels);
83
+ payload->pixels = NULL;
84
+ }
85
+
86
+ avifDecoderDestroy(decoder);
87
+ return NULL;
88
+ }
89
+
90
+ static VALUE avif_decode(VALUE self, VALUE path) {
91
+ Check_Type(path, T_STRING);
92
+
93
+ decode_payload_t payload;
94
+
95
+ long path_len = RSTRING_LEN(path);
96
+ payload.path = malloc(path_len + 1);
97
+ if (!payload.path) {
98
+ rb_raise(rb_eNoMemError, "Failed to allocate memory for path string");
99
+ }
100
+ memcpy(payload.path, RSTRING_PTR(path), path_len);
101
+ payload.path[path_len] = '\0';
102
+
103
+ rb_thread_call_without_gvl(decode, &payload, RUBY_UBF_IO, NULL);
104
+
105
+ if (payload.error_message) {
106
+ char *err_msg = payload.error_message;
107
+ rb_raise(rb_eRuntimeError, "AVIF decoding failed: %s", err_msg);
108
+ free(err_msg);
109
+ }
110
+ if (payload.result != AVIF_RESULT_OK) {
111
+ rb_raise(rb_eRuntimeError, "AVIF decoding failed: %s", avifResultToString(payload.result));
112
+ }
113
+
114
+ size_t pixel_bytes = (size_t)payload.width * payload.height * 4;
115
+
116
+ VALUE pixel_string = rb_str_new((const char*)payload.pixels, pixel_bytes);
117
+ free(payload.pixels);
118
+
119
+ rb_str_freeze(pixel_string);
120
+
121
+ VALUE result_array = rb_ary_new_capa(3);
122
+ rb_ary_push(result_array, UINT2NUM(payload.width));
123
+ rb_ary_push(result_array, UINT2NUM(payload.height));
124
+ rb_ary_push(result_array, pixel_string);
125
+
126
+ rb_ary_freeze(result_array);
127
+
128
+ return result_array;
129
+ }
130
+
131
+ void Init_avif(void) {
132
+ rb_ext_ractor_safe(true);
133
+
134
+ VALUE rb_mAvif = rb_define_module("Avif");
135
+ rb_define_singleton_method(rb_mAvif, "decode", avif_decode, 1);
136
+ }
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+
3
+ abort "libavif not found. Please install libavif." unless have_library("avif", "avifDecoderCreate")
4
+ $CFLAGS << ' -std=c99'
5
+
6
+ create_makefile("avif/avif")
@@ -0,0 +1,3 @@
1
+ module Avif
2
+ VERSION = "1.0.0".freeze
3
+ end
data/lib/avif.rb ADDED
@@ -0,0 +1,4 @@
1
+ require_relative 'avif/avif'
2
+ require_relative 'avif/version'
3
+
4
+ module Avif; end
metadata ADDED
@@ -0,0 +1,44 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-avif
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Luka Bak
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2025-07-06 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Ractor safe Ruby FFI binding for decoding AVIF images.
13
+ email: bakluka@gmail.com
14
+ executables: []
15
+ extensions:
16
+ - ext/avif/extconf.rb
17
+ extra_rdoc_files: []
18
+ files:
19
+ - ext/avif/avif.c
20
+ - ext/avif/extconf.rb
21
+ - lib/avif.rb
22
+ - lib/avif/version.rb
23
+ homepage: https://github.com/bakluka/ruby-avif
24
+ licenses:
25
+ - MIT
26
+ metadata: {}
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 3.0.0
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubygems_version: 3.6.2
42
+ specification_version: 4
43
+ summary: Ruby FFI binding for libavif
44
+ test_files: []