ruby-libgd 0.2.2 → 0.2.3
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 +4 -4
- data/README.md +301 -0
- data/ext/gd/Makefile +2 -2
- data/ext/gd/gd.c +2 -0
- data/ext/gd/gd.o +0 -0
- data/ext/gd/gd.so +0 -0
- data/ext/gd/gif.c +151 -0
- data/ext/gd/gif.o +0 -0
- data/ext/gd/image.c +0 -22
- data/ext/gd/image.o +0 -0
- data/ext/gd/ruby_gd.h +2 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5f72abe4a18b66efb49a1e30a5ef170e69a31f4a3eb0ccf308e9c9f85603c229
|
|
4
|
+
data.tar.gz: c17c93e69f330862bc24bd655fbc7abb8017be59d0bd57723d5aeb7c9301e70f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7667691652b3fbb70b7945071ac3f0719d8162adb231b80c3b1e7eb17514db6714e1562791f6bdd20676bc77b3d96fc85b1dec0b5bfb7c07678efa86ec82fc48
|
|
7
|
+
data.tar.gz: f02dc5751f96bd1bd25ac4b693f4e18b968cc2fcaceaa687efd269572cca60a8ef506de4b3a79870a98fb91a8c164fd8bea78eb7df6bca1a1b1ab0b255647531
|
data/README.md
CHANGED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
# Ruby Libgd
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://rubystacknews.com">
|
|
5
|
+
<img src="https://img.shields.io/badge/RubyStackNews-CC342D?style=for-the-badge&logo=ruby&logoColor=white" />
|
|
6
|
+
</a>
|
|
7
|
+
<a href="https://x.com/ruby_stack_news">
|
|
8
|
+
<img src="https://img.shields.io/badge/Twitter%20%40RubyStackNews-1DA1F2?style=for-the-badge&logo=twitter&logoColor=white" />
|
|
9
|
+
</a>
|
|
10
|
+
<a href="https://www.linkedin.com/in/germ%C3%A1n-silva-56a12622/">
|
|
11
|
+
<img src="https://img.shields.io/badge/Germán%20Silva-0A66C2?style=for-the-badge&logo=linkedin&logoColor=white" />
|
|
12
|
+
</a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<p align="right">
|
|
16
|
+
<img src="docs/images/logo.png" width="160" />
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
## Library Status
|
|
20
|
+
|
|
21
|
+

|
|
22
|
+
|
|
23
|
+
| Implemented | Implemented | Coming Next |
|
|
24
|
+
| :--: | :--: | :--: |
|
|
25
|
+
| <img src="./examples/charts/images/fruit_chart_with_text.png" width="250"> | <img src="./examples/charts/images/pie_chart.png" width="250"> | <img src="./examples/maps/parana_paracao_route.png" width="250"> |
|
|
26
|
+
| <img src="./examples/image_processing/images/processed.jpg" width="250"> | <img src="./examples/charts/images/stem_plot.png" width="250"> | <img src="./examples/maps/parana_paracao_polygon.png" width="250"> |
|
|
27
|
+
| <img src="./examples/basics/images/vintage.jpg" width="250"> | <img src="./examples/basics/images/antialias_demo.png" width="250"> | |
|
|
28
|
+
|
|
29
|
+
**Note:** The images shown in Table 1, along with the code used to generate them, are available in the `examples/` folder.
|
|
30
|
+
|
|
31
|
+
# Native GD bindings for modern Ruby
|
|
32
|
+
|
|
33
|
+
## What is ruby-libgd?
|
|
34
|
+
|
|
35
|
+
ruby-libgd is a modern native Ruby binding to the GD Graphics Library, providing Ruby with a fast, embeddable, server-side graphics engine.
|
|
36
|
+
|
|
37
|
+
It enables Ruby to generate images, charts, dashboards, GIS tiles, and scientific graphics without spawning external processes.
|
|
38
|
+
|
|
39
|
+
## 🐧Installation (Debian / Ubuntu /Dockerfile)
|
|
40
|
+
|
|
41
|
+
`gem install ruby-libgd`
|
|
42
|
+
|
|
43
|
+
System dependencies:
|
|
44
|
+
|
|
45
|
+
`apt install -y libgd-dev pkg-config`
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 🍎 macOS (Intel & Apple Silicon)
|
|
50
|
+
|
|
51
|
+
ruby-libgd is a native extension and depends on libgd, which must be installed via Homebrew.
|
|
52
|
+
|
|
53
|
+
Install libgd
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
brew install gd
|
|
57
|
+
```
|
|
58
|
+
Verify Homebrew’s install path:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
brew --prefix gd
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Typical values:
|
|
65
|
+
Apple Silicon (**M1 / M2 / M3**): /opt/homebrew
|
|
66
|
+
|
|
67
|
+
Intel Macs: /usr/local
|
|
68
|
+
|
|
69
|
+
### Install ruby-libgd
|
|
70
|
+
|
|
71
|
+
Apple Silicon (**M1 / M2 / M3**)
|
|
72
|
+
|
|
73
|
+
Homebrew installs libraries into **/opt/homebrew**, which Ruby’s native extension builder does not detect automatically.
|
|
74
|
+
|
|
75
|
+
## Install using:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
gem install ruby-libgd -- --with-gd-dir=/opt/homebrew
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Intel macOS
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
gem install ruby-libgd
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 📚 Documentation
|
|
90
|
+
|
|
91
|
+
- 🇬🇧 **English**: https://ggerman.github.io/ruby-libgd/en/
|
|
92
|
+
- 🇯🇵 **日本語**: https://ggerman.github.io/ruby-libgd/jp/
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Examples:
|
|
97
|
+
|
|
98
|
+
All runnable examples live in:
|
|
99
|
+
|
|
100
|
+
examples/
|
|
101
|
+
basics/
|
|
102
|
+
image_processing/
|
|
103
|
+
charts/
|
|
104
|
+
images/
|
|
105
|
+
|
|
106
|
+
Each Ruby script in examples/ generates an image in examples/images/.
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
## Links
|
|
110
|
+
|
|
111
|
+
GitHub: https://github.com/ggerman/ruby-libgd
|
|
112
|
+
RubyGems: https://rubygems.org/gems/ruby-libgd
|
|
113
|
+
Article: https://rubystacknews.com/2026/01/05/ruby-can-create-images-again/
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Build & Runtime Environment
|
|
118
|
+
|
|
119
|
+
**ruby-libgd** is developed and tested against **libgd 2.3.3** using a reproducible Docker environment.
|
|
120
|
+
|
|
121
|
+
This ensures that the native extension is compiled against a known, stable GD ABI and behaves consistently across systems.
|
|
122
|
+
|
|
123
|
+
Reference **Dockerfile**
|
|
124
|
+
```dockerfile
|
|
125
|
+
FROM ruby:3.3
|
|
126
|
+
|
|
127
|
+
RUN apt update && apt -y upgrade
|
|
128
|
+
RUN apt install -y \
|
|
129
|
+
libgd-dev \
|
|
130
|
+
libgd3 \
|
|
131
|
+
libgd-tools \
|
|
132
|
+
pkg-config \
|
|
133
|
+
ruby-dev \
|
|
134
|
+
build-essential \
|
|
135
|
+
valgrind
|
|
136
|
+
|
|
137
|
+
RUN printf "prefix=/usr\n\
|
|
138
|
+
exec_prefix=\${prefix}\n\
|
|
139
|
+
libdir=\${exec_prefix}/lib/x86_64-linux-gnu\n\
|
|
140
|
+
includedir=\${prefix}/include\n\
|
|
141
|
+
\n\
|
|
142
|
+
Name: gd\n\
|
|
143
|
+
Description: GD Graphics Library\n\
|
|
144
|
+
Version: 2.3\n\
|
|
145
|
+
Libs: -L\${libdir} -lgd\n\
|
|
146
|
+
Cflags: -I\${includedir}\n" \
|
|
147
|
+
> /usr/lib/x86_64-linux-gnu/pkgconfig/gd.pc
|
|
148
|
+
|
|
149
|
+
ENV PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
|
|
150
|
+
|
|
151
|
+
WORKDIR /app
|
|
152
|
+
|
|
153
|
+
COPY Gemfile Gemfile.lock ./
|
|
154
|
+
RUN bundle install
|
|
155
|
+
|
|
156
|
+
# Enforce dependency lockfile reproducibility
|
|
157
|
+
RUN bundle config --global frozen 1
|
|
158
|
+
|
|
159
|
+
COPY . .
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Why this matters
|
|
164
|
+
|
|
165
|
+
**ruby-libgd** is a native **C** extension. The exact **libgd** version and build flags directly affect:
|
|
166
|
+
|
|
167
|
+
- Alpha blending
|
|
168
|
+
|
|
169
|
+
- Color accuracy
|
|
170
|
+
|
|
171
|
+
- Filter behavior
|
|
172
|
+
|
|
173
|
+
- Memory safety
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
Using a pinned, containerized build environment guarantees that:
|
|
177
|
+
|
|
178
|
+
- The extension is compiled against libgd 2.3.x
|
|
179
|
+
|
|
180
|
+
- pkg-config resolves the correct headers and linker flags
|
|
181
|
+
|
|
182
|
+
- CI, contributors, and users see identical behavior
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
This is especially important for GIS, map tiles, and image pipelines where pixel-level consistency matters.
|
|
186
|
+
|
|
187
|
+
## Example:
|
|
188
|
+
|
|
189
|
+

|
|
190
|
+
|
|
191
|
+
```Ruby
|
|
192
|
+
require "gd"
|
|
193
|
+
|
|
194
|
+
WIDTH = 800
|
|
195
|
+
HEIGHT = 400
|
|
196
|
+
|
|
197
|
+
img = GD::Image.new(WIDTH, HEIGHT)
|
|
198
|
+
|
|
199
|
+
# ----------------------------
|
|
200
|
+
# Pastel palette
|
|
201
|
+
# ----------------------------
|
|
202
|
+
bg_top = [244, 240, 255] # lavender
|
|
203
|
+
bg_bottom = [230, 248, 255] # light sky
|
|
204
|
+
|
|
205
|
+
accent1 = [255, 140, 180] # pastel pink
|
|
206
|
+
accent2 = [120, 200, 255] # pastel blue
|
|
207
|
+
text_main = [60, 70, 120] # deep indigo
|
|
208
|
+
shadow = [180, 190, 220]
|
|
209
|
+
|
|
210
|
+
# ----------------------------
|
|
211
|
+
# Soft gradient background
|
|
212
|
+
# ----------------------------
|
|
213
|
+
(0...HEIGHT).each do |y|
|
|
214
|
+
t = y.to_f / (HEIGHT - 1)
|
|
215
|
+
|
|
216
|
+
r = (bg_top[0] + (bg_bottom[0] - bg_top[0]) * t).to_i
|
|
217
|
+
g = (bg_top[1] + (bg_bottom[1] - bg_top[1]) * t).to_i
|
|
218
|
+
b = (bg_top[2] + (bg_bottom[2] - bg_top[2]) * t).to_i
|
|
219
|
+
|
|
220
|
+
img.line(0, y, WIDTH - 1, y, [r, g, b])
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# ----------------------------
|
|
224
|
+
# Floating pastel blobs
|
|
225
|
+
# ----------------------------
|
|
226
|
+
img.filled_circle(120, 100, 80, accent2)
|
|
227
|
+
img.filled_circle(680, 280, 100, accent1)
|
|
228
|
+
img.filled_circle(700, 80, 50, [200, 230, 255])
|
|
229
|
+
|
|
230
|
+
# ----------------------------
|
|
231
|
+
# Text
|
|
232
|
+
# ----------------------------
|
|
233
|
+
font = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
|
|
234
|
+
title_size = 52
|
|
235
|
+
subtitle_size = 20
|
|
236
|
+
|
|
237
|
+
title = "RubyStackNews"
|
|
238
|
+
subtitle = "The pulse of the Ruby ecosystem"
|
|
239
|
+
|
|
240
|
+
# Manual centering
|
|
241
|
+
title_x = 80
|
|
242
|
+
title_y = 190
|
|
243
|
+
|
|
244
|
+
# Soft shadow
|
|
245
|
+
img.text(title, {
|
|
246
|
+
x: title_x + 2,
|
|
247
|
+
y: title_y + 2,
|
|
248
|
+
size: title_size,
|
|
249
|
+
color: shadow,
|
|
250
|
+
font: font
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
# Main title
|
|
254
|
+
img.text(title, {
|
|
255
|
+
x: title_x,
|
|
256
|
+
y: title_y,
|
|
257
|
+
size: title_size,
|
|
258
|
+
color: text_main,
|
|
259
|
+
font: font
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
# Subtitle
|
|
263
|
+
img.text(subtitle, {
|
|
264
|
+
x: title_x,
|
|
265
|
+
y: title_y + 45,
|
|
266
|
+
size: subtitle_size,
|
|
267
|
+
color: [100,110,160],
|
|
268
|
+
font: font
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
# ----------------------------
|
|
272
|
+
# Call-to-action pill
|
|
273
|
+
# ----------------------------
|
|
274
|
+
pill_x = 80
|
|
275
|
+
pill_y = 260
|
|
276
|
+
pill_w = 260
|
|
277
|
+
pill_h = 44
|
|
278
|
+
|
|
279
|
+
img.filled_rectangle(pill_x, pill_y, pill_x + pill_w, pill_y + pill_h, [255,255,255])
|
|
280
|
+
img.rectangle(pill_x, pill_y, pill_x + pill_w, pill_y + pill_h, accent2, thickness: 2)
|
|
281
|
+
|
|
282
|
+
img.text("rubystacknews.com", {
|
|
283
|
+
x: pill_x + 22,
|
|
284
|
+
y: pill_y + 30,
|
|
285
|
+
size: 20,
|
|
286
|
+
color: text_main,
|
|
287
|
+
font: font
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
# ----------------------------
|
|
291
|
+
# Save
|
|
292
|
+
# ----------------------------
|
|
293
|
+
img.save("images/rubystacknews-banner.png")
|
|
294
|
+
puts "Saved images/rubystacknews-banner.png"
|
|
295
|
+
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## License
|
|
299
|
+
|
|
300
|
+
MIT License.
|
|
301
|
+
Copyright (c) 2025 Germán Alberto Giménez Silva.
|
data/ext/gd/Makefile
CHANGED
|
@@ -140,9 +140,9 @@ extout_prefix =
|
|
|
140
140
|
target_prefix = /gd
|
|
141
141
|
LOCAL_LIBS =
|
|
142
142
|
LIBS = $(LIBRUBYARG_SHARED) -lm -lgd -lm -lpthread -lc
|
|
143
|
-
ORIG_SRCS = arc.c blit.c char.c circle.c clip.c color.c draw_line.c ellipse.c encode.c fill.c filter.c gd.c image.c pixel.c polygon.c rectangle.c text.c transform.c version.c
|
|
143
|
+
ORIG_SRCS = arc.c blit.c char.c circle.c clip.c color.c draw_line.c ellipse.c encode.c fill.c filter.c gd.c gif.c image.c pixel.c polygon.c rectangle.c text.c transform.c version.c
|
|
144
144
|
SRCS = $(ORIG_SRCS)
|
|
145
|
-
OBJS = arc.o blit.o char.o circle.o clip.o color.o draw_line.o ellipse.o encode.o fill.o filter.o gd.o image.o pixel.o polygon.o rectangle.o text.o transform.o version.o
|
|
145
|
+
OBJS = arc.o blit.o char.o circle.o clip.o color.o draw_line.o ellipse.o encode.o fill.o filter.o gd.o gif.o image.o pixel.o polygon.o rectangle.o text.o transform.o version.o
|
|
146
146
|
HDRS = $(srcdir)/clip.h $(srcdir)/ruby_gd.h
|
|
147
147
|
LOCAL_HDRS =
|
|
148
148
|
TARGET = gd
|
data/ext/gd/gd.c
CHANGED
data/ext/gd/gd.o
CHANGED
|
Binary file
|
data/ext/gd/gd.so
CHANGED
|
Binary file
|
data/ext/gd/gif.c
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#include "ruby_gd.h"
|
|
2
|
+
|
|
3
|
+
typedef struct {
|
|
4
|
+
FILE *out;
|
|
5
|
+
gdImagePtr prev;
|
|
6
|
+
VALUE prev_rb;
|
|
7
|
+
int started;
|
|
8
|
+
int loop;
|
|
9
|
+
} gd_gif_wrapper;
|
|
10
|
+
|
|
11
|
+
static void gd_gif_free(void *p) {
|
|
12
|
+
gd_gif_wrapper *gif = (gd_gif_wrapper *)p;
|
|
13
|
+
|
|
14
|
+
if (gif->out) {
|
|
15
|
+
if (gif->started) {
|
|
16
|
+
gdImageGifAnimEnd(gif->out);
|
|
17
|
+
}
|
|
18
|
+
fclose(gif->out);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
xfree(gif);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static size_t gd_gif_memsize(const void *p) {
|
|
25
|
+
return sizeof(gd_gif_wrapper);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static const rb_data_type_t gd_gif_type = {
|
|
29
|
+
"GD::Gif",
|
|
30
|
+
{
|
|
31
|
+
0,
|
|
32
|
+
gd_gif_free,
|
|
33
|
+
gd_gif_memsize,
|
|
34
|
+
},
|
|
35
|
+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
static VALUE gd_gif_alloc(VALUE klass) {
|
|
39
|
+
gd_gif_wrapper *gif;
|
|
40
|
+
VALUE obj = TypedData_Make_Struct(klass, gd_gif_wrapper, &gd_gif_type, gif);
|
|
41
|
+
|
|
42
|
+
gif->out = NULL;
|
|
43
|
+
gif->prev = NULL;
|
|
44
|
+
gif->prev_rb = Qnil;
|
|
45
|
+
gif->started = 0;
|
|
46
|
+
gif->loop = 0;
|
|
47
|
+
|
|
48
|
+
return obj;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/*
|
|
52
|
+
* GD::Gif.new("file.gif", loop: true)
|
|
53
|
+
*/
|
|
54
|
+
static VALUE gd_gif_initialize(int argc, VALUE *argv, VALUE self) {
|
|
55
|
+
VALUE path, opts;
|
|
56
|
+
rb_scan_args(argc, argv, "11", &path, &opts);
|
|
57
|
+
|
|
58
|
+
gd_gif_wrapper *gif;
|
|
59
|
+
TypedData_Get_Struct(self, gd_gif_wrapper, &gd_gif_type, gif);
|
|
60
|
+
|
|
61
|
+
const char *filename = StringValueCStr(path);
|
|
62
|
+
gif->out = fopen(filename, "wb");
|
|
63
|
+
if (!gif->out) rb_sys_fail(filename);
|
|
64
|
+
|
|
65
|
+
gif->started = 0;
|
|
66
|
+
gif->prev = NULL;
|
|
67
|
+
gif->prev_rb = Qnil;
|
|
68
|
+
|
|
69
|
+
gif->loop = 0;
|
|
70
|
+
if (opts != Qnil) {
|
|
71
|
+
VALUE loopv = rb_hash_aref(opts, ID2SYM(rb_intern("loop")));
|
|
72
|
+
if (loopv == Qfalse) {
|
|
73
|
+
gif->loop = -1;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return self;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/*
|
|
81
|
+
* gif.add_frame(img, delay: 50)
|
|
82
|
+
*/
|
|
83
|
+
static VALUE gd_gif_add_frame(int argc, VALUE *argv, VALUE self) {
|
|
84
|
+
VALUE rb_img, opts;
|
|
85
|
+
rb_scan_args(argc, argv, "11", &rb_img, &opts);
|
|
86
|
+
|
|
87
|
+
gd_image_wrapper *wrap;
|
|
88
|
+
TypedData_Get_Struct(rb_img, gd_image_wrapper, &gd_image_type, wrap);
|
|
89
|
+
|
|
90
|
+
gd_gif_wrapper *gif;
|
|
91
|
+
TypedData_Get_Struct(self, gd_gif_wrapper, &gd_gif_type, gif);
|
|
92
|
+
|
|
93
|
+
int delay = 50;
|
|
94
|
+
if (opts != Qnil) {
|
|
95
|
+
VALUE d = rb_hash_aref(opts, ID2SYM(rb_intern("delay")));
|
|
96
|
+
if (!NIL_P(d)) delay = NUM2INT(d);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!gif->started) {
|
|
100
|
+
gdImageGifAnimBegin(wrap->img, gif->out, 1, gif->loop);
|
|
101
|
+
gif->started = 1;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
gdImageGifAnimAdd(
|
|
105
|
+
wrap->img,
|
|
106
|
+
gif->out,
|
|
107
|
+
1,
|
|
108
|
+
0, 0,
|
|
109
|
+
delay,
|
|
110
|
+
1,
|
|
111
|
+
gif->prev
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
gif->prev = wrap->img;
|
|
115
|
+
gif->prev_rb = rb_img; /* prevent GC */
|
|
116
|
+
|
|
117
|
+
return Qtrue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/*
|
|
121
|
+
* gif.close
|
|
122
|
+
*/
|
|
123
|
+
static VALUE gd_gif_close(VALUE self) {
|
|
124
|
+
gd_gif_wrapper *gif;
|
|
125
|
+
TypedData_Get_Struct(self, gd_gif_wrapper, &gd_gif_type, gif);
|
|
126
|
+
|
|
127
|
+
if (gif->out) {
|
|
128
|
+
if (gif->started) {
|
|
129
|
+
gdImageGifAnimEnd(gif->out);
|
|
130
|
+
}
|
|
131
|
+
fclose(gif->out);
|
|
132
|
+
gif->out = NULL;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
gif->prev = NULL;
|
|
136
|
+
gif->prev_rb = Qnil;
|
|
137
|
+
|
|
138
|
+
return Qtrue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/*
|
|
142
|
+
* Init hook
|
|
143
|
+
*/
|
|
144
|
+
void gd_define_gif(VALUE mGD) {
|
|
145
|
+
VALUE cGif = rb_define_class_under(mGD, "Gif", rb_cObject);
|
|
146
|
+
|
|
147
|
+
rb_define_alloc_func(cGif, gd_gif_alloc);
|
|
148
|
+
rb_define_method(cGif, "initialize", gd_gif_initialize, -1);
|
|
149
|
+
rb_define_method(cGif, "add_frame", gd_gif_add_frame, -1);
|
|
150
|
+
rb_define_method(cGif, "close", gd_gif_close, 0);
|
|
151
|
+
}
|
data/ext/gd/gif.o
ADDED
|
Binary file
|
data/ext/gd/image.c
CHANGED
|
@@ -36,13 +36,6 @@ static VALUE gd_image_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
36
36
|
return self;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
static VALUE gd_image_initialize_empty(VALUE self) {
|
|
40
|
-
gd_image_wrapper *wrap;
|
|
41
|
-
TypedData_Get_Struct(self, gd_image_wrapper, &gd_image_type, wrap);
|
|
42
|
-
wrap->img = NULL;
|
|
43
|
-
return self;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
39
|
static VALUE gd_image_initialize_true_color(VALUE self, VALUE width, VALUE height) {
|
|
47
40
|
gd_image_wrapper *wrap;
|
|
48
41
|
TypedData_Get_Struct(self, gd_image_wrapper, &gd_image_type, wrap);
|
|
@@ -123,21 +116,6 @@ static VALUE gd_image_alloc(VALUE klass) {
|
|
|
123
116
|
return obj;
|
|
124
117
|
}
|
|
125
118
|
|
|
126
|
-
static VALUE gd_image_new(VALUE klass, VALUE w, VALUE h) {
|
|
127
|
-
VALUE obj = rb_class_new_instance(0, NULL, klass);
|
|
128
|
-
gd_image_wrapper *wrap;
|
|
129
|
-
TypedData_Get_Struct(obj, gd_image_wrapper, &gd_image_type, wrap);
|
|
130
|
-
|
|
131
|
-
int width = NUM2INT(w);
|
|
132
|
-
int height = NUM2INT(h);
|
|
133
|
-
if (width <= 0 || height <= 0)
|
|
134
|
-
rb_raise(rb_eArgError, "width and height must be positive");
|
|
135
|
-
|
|
136
|
-
wrap->img = gdImageCreateTrueColor(width, height);
|
|
137
|
-
if (!wrap->img) rb_raise(rb_eRuntimeError, "gdImageCreateTrueColor failed");
|
|
138
|
-
return obj;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
119
|
static VALUE gd_image_open(VALUE klass, VALUE path) {
|
|
142
120
|
const char *filename = StringValueCStr(path);
|
|
143
121
|
|
data/ext/gd/image.o
CHANGED
|
Binary file
|
data/ext/gd/ruby_gd.h
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-libgd
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Germán Alberto Giménez Silva
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-01-
|
|
11
|
+
date: 2026-01-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: High-performance native Ruby bindings to libgd for image generation,
|
|
14
14
|
drawing, filters, alpha blending, and transformations.
|
|
@@ -48,6 +48,8 @@ files:
|
|
|
48
48
|
- ext/gd/gd.c
|
|
49
49
|
- ext/gd/gd.o
|
|
50
50
|
- ext/gd/gd.so
|
|
51
|
+
- ext/gd/gif.c
|
|
52
|
+
- ext/gd/gif.o
|
|
51
53
|
- ext/gd/image.c
|
|
52
54
|
- ext/gd/image.o
|
|
53
55
|
- ext/gd/mkmf.log
|