ruby-libgd 0.1.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.
data/ext/gd/polygon.c ADDED
@@ -0,0 +1,98 @@
1
+ /**
2
+ - [ ] imagefilledpolygon — Draw a filled polygon
3
+ - [ ] imageopenpolygon — Draws an open polygon
4
+ - [ ] imagepolygon — Draws a polygon
5
+ */
6
+ #include "ruby_gd.h"
7
+ #include <string.h>
8
+
9
+ static gdPointPtr build_points(VALUE points, int *count_out) {
10
+ Check_Type(points, T_ARRAY);
11
+
12
+ long n = RARRAY_LEN(points);
13
+ if (n <= 0) {
14
+ *count_out = 0;
15
+ return NULL;
16
+ }
17
+
18
+ // Primero construimos todos los puntos (como antes)
19
+ gdPoint *raw_pts = ALLOC_N(gdPoint, n);
20
+ for (long i = 0; i < n; i++) {
21
+ VALUE pair = rb_ary_entry(points, i);
22
+ Check_Type(pair, T_ARRAY);
23
+ if (RARRAY_LEN(pair) < 2) {
24
+ xfree(raw_pts);
25
+ rb_raise(rb_eArgError, "each point must be [x, y]");
26
+ }
27
+ raw_pts[i].x = NUM2INT(rb_ary_entry(pair, 0));
28
+ raw_pts[i].y = NUM2INT(rb_ary_entry(pair, 1));
29
+ }
30
+
31
+ // Ahora eliminamos duplicados consecutivos
32
+ long unique_n = 0;
33
+ for (long i = 0; i < n; i++) {
34
+ if (unique_n == 0 ||
35
+ raw_pts[unique_n - 1].x != raw_pts[i].x ||
36
+ raw_pts[unique_n - 1].y != raw_pts[i].y) {
37
+ raw_pts[unique_n++] = raw_pts[i];
38
+ }
39
+ }
40
+
41
+ *count_out = (int)unique_n;
42
+
43
+ // Si hay menos de 3 puntos únicos, fallamos
44
+ if (unique_n < 3) {
45
+ xfree(raw_pts);
46
+ *count_out = 0;
47
+ return NULL;
48
+ }
49
+
50
+ // Reasignamos memoria para evitar desperdicio (opcional pero limpio)
51
+ gdPoint *clean_pts = ALLOC_N(gdPoint, unique_n);
52
+ MEMCPY(clean_pts, raw_pts, gdPoint, unique_n);
53
+ xfree(raw_pts);
54
+
55
+ return (gdPointPtr)clean_pts;
56
+ }
57
+
58
+ static VALUE gd_image_polygon(VALUE self, VALUE points, VALUE color) {
59
+ gd_image_wrapper *wrap;
60
+ TypedData_Get_Struct(self, gd_image_wrapper, &gd_image_type, wrap);
61
+
62
+ int count = 0;
63
+ gdPointPtr pts = build_points(points, &count);
64
+ int c = color_to_gd(wrap->img, color);
65
+
66
+ gdImagePolygon(wrap->img, pts, count, c);
67
+
68
+ xfree(pts);
69
+ return self;
70
+ }
71
+
72
+ static VALUE gd_image_filled_polygon(VALUE self, VALUE points, VALUE color) {
73
+ gd_image_wrapper *wrap;
74
+ TypedData_Get_Struct(self, gd_image_wrapper, &gd_image_type, wrap);
75
+
76
+ if (!wrap || !wrap->img) {
77
+ rb_raise(rb_eRuntimeError, "uninitialized GD::Image");
78
+ }
79
+
80
+ int count = 0;
81
+ gdPointPtr pts = build_points(points, &count);
82
+
83
+ if (!pts || count < 3) {
84
+ if (pts) xfree(pts);
85
+ rb_raise(rb_eArgError, "points must be an Array of at least 3 [x,y] pairs");
86
+ }
87
+
88
+ int c = color_to_gd(wrap->img, color);
89
+ gdImageFilledPolygon(wrap->img, pts, count, c);
90
+
91
+ xfree(pts);
92
+ return self;
93
+ }
94
+
95
+ void gd_define_polygon(VALUE cGDImage) {
96
+ rb_define_method(cGDImage, "polygon", gd_image_polygon, 2);
97
+ rb_define_method(cGDImage, "filled_polygon", gd_image_filled_polygon, 2);
98
+ }
data/ext/gd/polygon.o ADDED
Binary file
@@ -0,0 +1,41 @@
1
+ #include "ruby_gd.h"
2
+ /**
3
+ - [ ] imagerectangle — Draw a rectangle
4
+ - [ ] imagefilledrectangle — Draw a filled rectangle
5
+ */
6
+ static VALUE gd_image_rect(VALUE self, VALUE x1, VALUE y1, VALUE x2, VALUE y2, VALUE color) {
7
+ gd_image_wrapper *wrap;
8
+ TypedData_Get_Struct(self, gd_image_wrapper, &gd_image_type, wrap);
9
+
10
+ int r = NUM2INT(rb_ary_entry(color, 0));
11
+ int g = NUM2INT(rb_ary_entry(color, 1));
12
+ int b = NUM2INT(rb_ary_entry(color, 2));
13
+
14
+ int c = gdImageColorAllocate(wrap->img, r, g, b);
15
+ gdImageRectangle(wrap->img,
16
+ NUM2INT(x1), NUM2INT(y1),
17
+ NUM2INT(x2), NUM2INT(y2),
18
+ c);
19
+ return Qnil;
20
+ }
21
+
22
+ static VALUE gd_image_filled_rect(VALUE self, VALUE x1, VALUE y1, VALUE x2, VALUE y2, VALUE color) {
23
+ gd_image_wrapper *wrap;
24
+ TypedData_Get_Struct(self, gd_image_wrapper, &gd_image_type, wrap);
25
+
26
+ int r = NUM2INT(rb_ary_entry(color, 0));
27
+ int g = NUM2INT(rb_ary_entry(color, 1));
28
+ int b = NUM2INT(rb_ary_entry(color, 2));
29
+
30
+ int c = gdImageColorAllocate(wrap->img, r, g, b);
31
+ gdImageFilledRectangle(wrap->img,
32
+ NUM2INT(x1), NUM2INT(y1),
33
+ NUM2INT(x2), NUM2INT(y2),
34
+ c);
35
+ return Qnil;
36
+ }
37
+
38
+ void gd_define_rect(VALUE cGDImage) {
39
+ rb_define_method(cGDImage, "rect", gd_image_rect, 5);
40
+ rb_define_method(cGDImage, "filled_rect", gd_image_filled_rect, 5);
41
+ }
Binary file
data/ext/gd/ruby_gd.h ADDED
@@ -0,0 +1,38 @@
1
+ #ifndef RUBY_GD_H
2
+ #define RUBY_GD_H
3
+
4
+ #include <ruby.h>
5
+ #include <gd.h>
6
+
7
+ typedef struct {
8
+ gdImagePtr img;
9
+ int owner;
10
+ } gd_image_wrapper;
11
+
12
+ extern const rb_data_type_t gd_image_type;
13
+
14
+ int color_to_gd(gdImagePtr im, VALUE color);
15
+
16
+ /* Image */
17
+ void gd_define_image(VALUE mGD);
18
+ void gd_define_blit(VALUE cGDImage);
19
+ void gd_define_fill(VALUE cGDImage);
20
+
21
+ void gd_define_pixel(VALUE cGDImage);
22
+ void gd_define_line(VALUE cGDImage);
23
+ void gd_define_arc(VALUE cGDImage);
24
+ void gd_define_rect(VALUE cGDImage);
25
+ void gd_define_circle(VALUE cGDImage);
26
+ void gd_define_polygon(VALUE cGDImage);
27
+
28
+ void gd_define_text(VALUE cGDImage);
29
+
30
+ void gd_define_transform(VALUE cGDImage);
31
+ void gd_define_filter(VALUE cGDImage);
32
+ void gd_define_encode(VALUE cGDImage);
33
+
34
+ /* Color */
35
+ void gd_define_color(VALUE mGD);
36
+ void gd_define_version(VALUE mGD);
37
+
38
+ #endif
data/ext/gd/text.c ADDED
@@ -0,0 +1,48 @@
1
+ #include "ruby_gd.h"
2
+ /*
3
+ - [ ] imagefontheight — Get font height
4
+ - [ ] imagefontwidth — Get font width
5
+ - [ ] imageftbbox — Give the bounding box of a text using fonts via freetype2
6
+ - [ ] imagefttext — Write text to the image using fonts using FreeType 2
7
+ */
8
+
9
+ static VALUE gd_image_text(VALUE self, VALUE text, VALUE opts) {
10
+ gd_image_wrapper *wrap;
11
+ TypedData_Get_Struct(self, gd_image_wrapper, &gd_image_type, wrap);
12
+
13
+ VALUE x = rb_hash_aref(opts, ID2SYM(rb_intern("x")));
14
+ VALUE y = rb_hash_aref(opts, ID2SYM(rb_intern("y")));
15
+ VALUE size = rb_hash_aref(opts, ID2SYM(rb_intern("size")));
16
+ VALUE color = rb_hash_aref(opts, ID2SYM(rb_intern("color")));
17
+ VALUE font = rb_hash_aref(opts, ID2SYM(rb_intern("font")));
18
+
19
+ if (NIL_P(x) || NIL_P(y) || NIL_P(size) || NIL_P(color) || NIL_P(font))
20
+ rb_raise(rb_eArgError, "missing options");
21
+
22
+ int r = NUM2INT(rb_ary_entry(color,0));
23
+ int g = NUM2INT(rb_ary_entry(color,1));
24
+ int b = NUM2INT(rb_ary_entry(color,2));
25
+
26
+ int fg = gdImageColorAllocate(wrap->img, r, g, b);
27
+
28
+ int brect[8];
29
+ char *err = gdImageStringFT(
30
+ wrap->img,
31
+ brect,
32
+ fg,
33
+ StringValueCStr(font),
34
+ NUM2DBL(size),
35
+ 0,
36
+ NUM2INT(x),
37
+ NUM2INT(y),
38
+ StringValueCStr(text)
39
+ );
40
+
41
+ if (err) rb_raise(rb_eRuntimeError, "%s", err);
42
+
43
+ return Qnil;
44
+ }
45
+
46
+ void gd_define_text(VALUE cGDImage) {
47
+ rb_define_method(cGDImage, "text", gd_image_text, 2);
48
+ }
data/ext/gd/text.o ADDED
Binary file
@@ -0,0 +1,44 @@
1
+ #include "ruby_gd.h"
2
+
3
+ static VALUE wrap_new_image(gdImagePtr im, VALUE klass) {
4
+ gd_image_wrapper *w;
5
+ VALUE obj = TypedData_Make_Struct(klass, gd_image_wrapper, &gd_image_type, w);
6
+ w->img = im;
7
+ w->owner = 1;
8
+ return obj;
9
+ }
10
+
11
+ static VALUE gd_image_crop(VALUE self, VALUE vx, VALUE vy, VALUE vw, VALUE vh) {
12
+ gd_image_wrapper *wrap;
13
+ TypedData_Get_Struct(self, gd_image_wrapper, &gd_image_type, wrap);
14
+
15
+ gdRect r;
16
+ r.x = NUM2INT(vx);
17
+ r.y = NUM2INT(vy);
18
+ r.width = NUM2INT(vw);
19
+ r.height = NUM2INT(vh);
20
+
21
+ gdImagePtr out = gdImageCrop(wrap->img, &r);
22
+ if (!out) rb_raise(rb_eRuntimeError, "crop failed");
23
+
24
+ return wrap_new_image(out, CLASS_OF(self));
25
+ }
26
+
27
+ static VALUE gd_image_scale(VALUE self, VALUE vw, VALUE vh) {
28
+ gd_image_wrapper *wrap;
29
+ TypedData_Get_Struct(self, gd_image_wrapper, &gd_image_type, wrap);
30
+
31
+ int w = NUM2INT(vw);
32
+ int h = NUM2INT(vh);
33
+
34
+ gdImagePtr out = gdImageScale(wrap->img, w, h);
35
+ if (!out) rb_raise(rb_eRuntimeError, "scale failed");
36
+
37
+ return wrap_new_image(out, CLASS_OF(self));
38
+ }
39
+
40
+ void gd_define_transform(VALUE cGDImage) {
41
+ rb_define_method(cGDImage, "crop", gd_image_crop, 4);
42
+ rb_define_method(cGDImage, "scale", gd_image_scale, 2);
43
+ rb_define_method(cGDImage, "resize", gd_image_scale, 2);
44
+ }
Binary file
data/ext/gd/version.c ADDED
@@ -0,0 +1,9 @@
1
+ #include "ruby_gd.h"
2
+
3
+ VALUE rb_gd_version(VALUE self) {
4
+ return rb_str_new_cstr(gdVersionString());
5
+ }
6
+
7
+ void gd_define_version(VALUE mGD) {
8
+ rb_define_singleton_method(mGD, "version", rb_gd_version, 0);
9
+ }
data/ext/gd/version.o ADDED
Binary file
data/lib/gd.rb ADDED
@@ -0,0 +1,2 @@
1
+ require "gd/gd"
2
+
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-libgd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Germán Alberto Giménez Silva
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-01-02 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: High-performance native bindings to libgd for image generation
14
+ email:
15
+ - ggerman@gmail.com
16
+ executables: []
17
+ extensions:
18
+ - ext/gd/extconf.rb
19
+ extra_rdoc_files: []
20
+ files:
21
+ - ext/gd/Makefile
22
+ - ext/gd/arc.c
23
+ - ext/gd/arc.o
24
+ - ext/gd/blit.c
25
+ - ext/gd/blit.o
26
+ - ext/gd/char.c
27
+ - ext/gd/char.o
28
+ - ext/gd/circle.c
29
+ - ext/gd/circle.o
30
+ - ext/gd/clip.c
31
+ - ext/gd/clip.h
32
+ - ext/gd/clip.o
33
+ - ext/gd/color.c
34
+ - ext/gd/color.o
35
+ - ext/gd/draw_line.c
36
+ - ext/gd/draw_line.o
37
+ - ext/gd/ellipse.c
38
+ - ext/gd/ellipse.o
39
+ - ext/gd/encode.c
40
+ - ext/gd/encode.o
41
+ - ext/gd/extconf.rb
42
+ - ext/gd/fill.c
43
+ - ext/gd/fill.o
44
+ - ext/gd/filter.c
45
+ - ext/gd/filter.o
46
+ - ext/gd/gd.c
47
+ - ext/gd/gd.o
48
+ - ext/gd/gd.so
49
+ - ext/gd/image.c
50
+ - ext/gd/image.o
51
+ - ext/gd/join.sh
52
+ - ext/gd/mkmf.log
53
+ - ext/gd/pixel.c
54
+ - ext/gd/pixel.o
55
+ - ext/gd/polygon.c
56
+ - ext/gd/polygon.o
57
+ - ext/gd/rectangle.c
58
+ - ext/gd/rectangle.o
59
+ - ext/gd/ruby_gd.h
60
+ - ext/gd/text.c
61
+ - ext/gd/text.o
62
+ - ext/gd/transform.c
63
+ - ext/gd/transform.o
64
+ - ext/gd/version.c
65
+ - ext/gd/version.o
66
+ - lib/gd.rb
67
+ homepage:
68
+ licenses: []
69
+ metadata: {}
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubygems_version: 3.5.22
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Native Ruby bindings for libgd
89
+ test_files: []