safe_image 0.1.0 → 0.2.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 +4 -4
- data/CHANGELOG.md +158 -0
- data/README.md +475 -281
- data/SECURITY.md +27 -7
- data/lib/safe_image/discourse_compat.rb +267 -98
- data/lib/safe_image/fonts/DEJAVU-LICENSE +187 -0
- data/lib/safe_image/fonts/DejaVuSans.ttf +0 -0
- data/lib/safe_image/ico.rb +286 -0
- data/lib/safe_image/image_magick_backend.rb +39 -3
- data/lib/safe_image/imagemagick_policy/policy.xml +8 -1
- data/lib/safe_image/jpegli_backend.rb +3 -1
- data/lib/safe_image/native.rb +371 -1
- data/lib/safe_image/processor.rb +23 -69
- data/lib/safe_image/remote.rb +15 -3
- data/lib/safe_image/runner.rb +1 -1
- data/lib/safe_image/sandbox.rb +42 -16
- data/lib/safe_image/svg_metadata.rb +59 -29
- data/lib/safe_image/version.rb +1 -1
- data/lib/safe_image/vips_backend.rb +57 -0
- data/lib/safe_image/vips_glue.rb +361 -0
- data/lib/safe_image.rb +143 -36
- metadata +30 -14
- data/ext/safe_image_native/extconf.rb +0 -8
- data/ext/safe_image_native/safe_image_native.c +0 -392
|
@@ -1,392 +0,0 @@
|
|
|
1
|
-
#include <ruby.h>
|
|
2
|
-
#include <vips/vips.h>
|
|
3
|
-
#include <sys/stat.h>
|
|
4
|
-
#include <string.h>
|
|
5
|
-
#include <time.h>
|
|
6
|
-
#include <math.h>
|
|
7
|
-
|
|
8
|
-
static VALUE mSafeImage;
|
|
9
|
-
static VALUE mNative;
|
|
10
|
-
static VALUE eError;
|
|
11
|
-
static VALUE eUnsupported;
|
|
12
|
-
static VALUE eInvalid;
|
|
13
|
-
static VALUE eLimit;
|
|
14
|
-
|
|
15
|
-
/* Default decompression-bomb ceiling applied when the caller does not pass an
|
|
16
|
-
* explicit max_pixels. Mirrors SafeImage::DEFAULT_MAX_PIXELS and the 128MP area
|
|
17
|
-
* limit used on the ImageMagick path, so the libvips fast path is not unbounded
|
|
18
|
-
* by default. Callers that legitimately need larger images pass max_pixels. */
|
|
19
|
-
#define SAFE_IMAGE_DEFAULT_MAX_PIXELS (128LL * 1024 * 1024)
|
|
20
|
-
|
|
21
|
-
static double now_ms(void) {
|
|
22
|
-
struct timespec ts;
|
|
23
|
-
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
24
|
-
return (double)ts.tv_sec * 1000.0 + (double)ts.tv_nsec / 1000000.0;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
static const char *extname(const char *path) {
|
|
28
|
-
const char *dot = strrchr(path, '.');
|
|
29
|
-
return dot ? dot + 1 : "";
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
static int streq_ci(const char *a, const char *b) {
|
|
33
|
-
#ifdef _WIN32
|
|
34
|
-
return _stricmp(a, b) == 0;
|
|
35
|
-
#else
|
|
36
|
-
return strcasecmp(a, b) == 0;
|
|
37
|
-
#endif
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
static const char *normalized_format(const char *fmt) {
|
|
41
|
-
if (streq_ci(fmt, "jpg") || streq_ci(fmt, "jpeg")) return "jpg";
|
|
42
|
-
if (streq_ci(fmt, "png")) return "png";
|
|
43
|
-
if (streq_ci(fmt, "webp")) return "webp";
|
|
44
|
-
if (streq_ci(fmt, "heic") || streq_ci(fmt, "heif")) return "heic";
|
|
45
|
-
if (streq_ci(fmt, "avif")) return "avif";
|
|
46
|
-
return NULL;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
static void raise_vips(void) {
|
|
50
|
-
const char *msg = vips_error_buffer();
|
|
51
|
-
VALUE rb_msg = rb_str_new_cstr(msg && *msg ? msg : "libvips error");
|
|
52
|
-
vips_error_clear();
|
|
53
|
-
rb_exc_raise(rb_exc_new3(eInvalid, rb_msg));
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
static void init_vips_once(void) {
|
|
57
|
-
static int initialized = 0;
|
|
58
|
-
if (initialized) return;
|
|
59
|
-
if (VIPS_INIT("safe_image") != 0) raise_vips();
|
|
60
|
-
|
|
61
|
-
/* Avoid libvips operations that are explicitly tagged as unsafe for
|
|
62
|
-
* untrusted input. Also block ImageMagick-backed loaders by class name;
|
|
63
|
-
* this gem uses explicit native libvips loaders and should never fall back
|
|
64
|
-
* to ImageMagick delegates. */
|
|
65
|
-
vips_block_untrusted_set(TRUE);
|
|
66
|
-
vips_operation_block_set("VipsForeignLoadMagick", TRUE);
|
|
67
|
-
vips_operation_block_set("VipsForeignLoadMagick6", TRUE);
|
|
68
|
-
vips_operation_block_set("VipsForeignLoadMagick7", TRUE);
|
|
69
|
-
|
|
70
|
-
/* Keep the embedded path predictable and bounded. Callers that want harder
|
|
71
|
-
* isolation should run this gem inside a sandboxed worker process. */
|
|
72
|
-
vips_concurrency_set(1);
|
|
73
|
-
vips_cache_set_max(0);
|
|
74
|
-
vips_cache_set_max_mem(0);
|
|
75
|
-
vips_cache_set_max_files(0);
|
|
76
|
-
initialized = 1;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
static VipsImage *load_explicit(const char *path, const char **fmt_out) {
|
|
80
|
-
init_vips_once();
|
|
81
|
-
const char *fmt = normalized_format(extname(path));
|
|
82
|
-
if (!fmt) rb_raise(eUnsupported, "unsupported input format");
|
|
83
|
-
|
|
84
|
-
VipsImage *image = NULL;
|
|
85
|
-
int rc = -1;
|
|
86
|
-
if (strcmp(fmt, "jpg") == 0) {
|
|
87
|
-
rc = vips_jpegload(path, &image,
|
|
88
|
-
"access", VIPS_ACCESS_SEQUENTIAL,
|
|
89
|
-
"fail_on", VIPS_FAIL_ON_ERROR,
|
|
90
|
-
NULL);
|
|
91
|
-
} else if (strcmp(fmt, "png") == 0) {
|
|
92
|
-
rc = vips_pngload(path, &image,
|
|
93
|
-
"access", VIPS_ACCESS_SEQUENTIAL,
|
|
94
|
-
"fail_on", VIPS_FAIL_ON_ERROR,
|
|
95
|
-
NULL);
|
|
96
|
-
} else if (strcmp(fmt, "webp") == 0) {
|
|
97
|
-
rc = vips_webpload(path, &image,
|
|
98
|
-
"access", VIPS_ACCESS_SEQUENTIAL,
|
|
99
|
-
"fail_on", VIPS_FAIL_ON_ERROR,
|
|
100
|
-
NULL);
|
|
101
|
-
} else if (strcmp(fmt, "heic") == 0 || strcmp(fmt, "avif") == 0) {
|
|
102
|
-
rc = vips_heifload(path, &image,
|
|
103
|
-
"access", VIPS_ACCESS_SEQUENTIAL,
|
|
104
|
-
"fail_on", VIPS_FAIL_ON_ERROR,
|
|
105
|
-
NULL);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (rc != 0 || image == NULL) raise_vips();
|
|
109
|
-
*fmt_out = fmt;
|
|
110
|
-
return image;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
static void validate_quality_or_raise(int quality) {
|
|
114
|
-
if (quality < 1 || quality > 100) rb_raise(rb_eArgError, "quality must be 1..100");
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
static void validate_dimensions_or_raise(int width, int height) {
|
|
118
|
-
if (width <= 0 || height <= 0) rb_raise(rb_eArgError, "width and height must be positive");
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
static void validate_scale_or_raise(double scale) {
|
|
122
|
-
if (!isfinite(scale) || scale <= 0.0 || scale > 100.0) rb_raise(rb_eArgError, "scale must be finite and in 0..100");
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
static int pixels_exceed_limit(VipsImage *image, VALUE max_pixels_val, long long *pixels_out, long long *max_out) {
|
|
126
|
-
long long max_pixels;
|
|
127
|
-
if (NIL_P(max_pixels_val)) {
|
|
128
|
-
max_pixels = SAFE_IMAGE_DEFAULT_MAX_PIXELS;
|
|
129
|
-
} else {
|
|
130
|
-
max_pixels = NUM2LL(max_pixels_val);
|
|
131
|
-
if (max_pixels <= 0) rb_raise(rb_eArgError, "max_pixels must be positive");
|
|
132
|
-
}
|
|
133
|
-
if (image->Xsize <= 0 || image->Ysize <= 0) rb_raise(eInvalid, "image dimensions are invalid");
|
|
134
|
-
long long pixels = (long long)image->Xsize * (long long)image->Ysize;
|
|
135
|
-
if (pixels_out) *pixels_out = pixels;
|
|
136
|
-
if (max_out) *max_out = max_pixels;
|
|
137
|
-
return pixels > max_pixels;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
static void raise_pixels_limit(long long pixels, long long max_pixels) {
|
|
141
|
-
rb_raise(eLimit, "image has %lld pixels, exceeds %lld", pixels, max_pixels);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
static int save_explicit(VipsImage *image, const char *path, const char *fmt, int quality) {
|
|
145
|
-
if (strcmp(fmt, "jpg") == 0) {
|
|
146
|
-
return vips_jpegsave(image, path,
|
|
147
|
-
"Q", quality,
|
|
148
|
-
"interlace", FALSE,
|
|
149
|
-
"keep", VIPS_FOREIGN_KEEP_NONE,
|
|
150
|
-
NULL);
|
|
151
|
-
} else if (strcmp(fmt, "png") == 0) {
|
|
152
|
-
return vips_pngsave(image, path,
|
|
153
|
-
"compression", 6,
|
|
154
|
-
"keep", VIPS_FOREIGN_KEEP_NONE,
|
|
155
|
-
NULL);
|
|
156
|
-
} else if (strcmp(fmt, "webp") == 0) {
|
|
157
|
-
return vips_webpsave(image, path,
|
|
158
|
-
"Q", quality,
|
|
159
|
-
"keep", VIPS_FOREIGN_KEEP_NONE,
|
|
160
|
-
NULL);
|
|
161
|
-
} else if (strcmp(fmt, "avif") == 0) {
|
|
162
|
-
return vips_heifsave(image, path,
|
|
163
|
-
"Q", quality,
|
|
164
|
-
"compression", VIPS_FOREIGN_HEIF_COMPRESSION_AV1,
|
|
165
|
-
"keep", VIPS_FOREIGN_KEEP_NONE,
|
|
166
|
-
NULL);
|
|
167
|
-
}
|
|
168
|
-
rb_raise(eUnsupported, "unsupported output format");
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
static VALUE rb_probe(VALUE self, VALUE path_val) {
|
|
172
|
-
Check_Type(path_val, T_STRING);
|
|
173
|
-
double start = now_ms();
|
|
174
|
-
const char *fmt = NULL;
|
|
175
|
-
VipsImage *image = load_explicit(StringValueCStr(path_val), &fmt);
|
|
176
|
-
int width = image->Xsize;
|
|
177
|
-
int height = image->Ysize;
|
|
178
|
-
double duration_ms = now_ms() - start;
|
|
179
|
-
g_object_unref(image);
|
|
180
|
-
|
|
181
|
-
VALUE hash = rb_hash_new();
|
|
182
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("format")), rb_str_new_cstr(fmt));
|
|
183
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("width")), INT2NUM(width));
|
|
184
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("height")), INT2NUM(height));
|
|
185
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("duration_ms")), DBL2NUM(duration_ms));
|
|
186
|
-
return hash;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
static VALUE rb_thumbnail(VALUE self, VALUE input_val, VALUE output_val, VALUE width_val, VALUE height_val, VALUE format_val, VALUE quality_val, VALUE max_pixels_val) {
|
|
190
|
-
Check_Type(input_val, T_STRING);
|
|
191
|
-
Check_Type(output_val, T_STRING);
|
|
192
|
-
Check_Type(format_val, T_STRING);
|
|
193
|
-
int width = NUM2INT(width_val);
|
|
194
|
-
int height = NUM2INT(height_val);
|
|
195
|
-
int quality = NUM2INT(quality_val);
|
|
196
|
-
validate_dimensions_or_raise(width, height);
|
|
197
|
-
validate_quality_or_raise(quality);
|
|
198
|
-
const char *out_fmt = normalized_format(StringValueCStr(format_val));
|
|
199
|
-
if (!out_fmt || strcmp(out_fmt, "heic") == 0) rb_raise(eUnsupported, "unsupported output format");
|
|
200
|
-
|
|
201
|
-
const char *input_path = StringValueCStr(input_val);
|
|
202
|
-
double start = now_ms();
|
|
203
|
-
|
|
204
|
-
/* Read the header through an explicit allowlisted loader. This validates the
|
|
205
|
-
* input format (the loader fails on mismatched bytes) and lets us enforce the
|
|
206
|
-
* pixel-count limit before any full decode happens. */
|
|
207
|
-
const char *input_fmt = NULL;
|
|
208
|
-
VipsImage *header = load_explicit(input_path, &input_fmt);
|
|
209
|
-
long long pixels = 0, max_pixels = 0;
|
|
210
|
-
if (pixels_exceed_limit(header, max_pixels_val, &pixels, &max_pixels)) {
|
|
211
|
-
g_object_unref(header);
|
|
212
|
-
raise_pixels_limit(pixels, max_pixels);
|
|
213
|
-
}
|
|
214
|
-
g_object_unref(header);
|
|
215
|
-
|
|
216
|
-
/* Thumbnail straight from the file so libvips can shrink on load (e.g.
|
|
217
|
-
* libjpeg DCT downscaling) instead of decoding the source at full
|
|
218
|
-
* resolution. vips_thumbnail auto-rotates from the orientation tag by
|
|
219
|
-
* default. ImageMagick loader classes are blocked globally in
|
|
220
|
-
* init_vips_once, so this still cannot reach an ImageMagick delegate. */
|
|
221
|
-
VipsImage *thumb = NULL;
|
|
222
|
-
if (vips_thumbnail(input_path, &thumb, width,
|
|
223
|
-
"height", height,
|
|
224
|
-
"size", VIPS_SIZE_BOTH,
|
|
225
|
-
"crop", VIPS_INTERESTING_CENTRE,
|
|
226
|
-
"fail_on", VIPS_FAIL_ON_ERROR,
|
|
227
|
-
NULL) != 0) {
|
|
228
|
-
raise_vips();
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (save_explicit(thumb, StringValueCStr(output_val), out_fmt, quality) != 0) {
|
|
232
|
-
g_object_unref(thumb);
|
|
233
|
-
raise_vips();
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
int out_width = thumb->Xsize;
|
|
237
|
-
int out_height = thumb->Ysize;
|
|
238
|
-
double duration_ms = now_ms() - start;
|
|
239
|
-
g_object_unref(thumb);
|
|
240
|
-
|
|
241
|
-
VALUE hash = rb_hash_new();
|
|
242
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("input_format")), rb_str_new_cstr(input_fmt));
|
|
243
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("output_format")), rb_str_new_cstr(out_fmt));
|
|
244
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("width")), INT2NUM(out_width));
|
|
245
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("height")), INT2NUM(out_height));
|
|
246
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("duration_ms")), DBL2NUM(duration_ms));
|
|
247
|
-
return hash;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
static VALUE rb_resize(VALUE self, VALUE input_val, VALUE output_val, VALUE scale_val, VALUE format_val, VALUE quality_val, VALUE max_pixels_val) {
|
|
251
|
-
Check_Type(input_val, T_STRING);
|
|
252
|
-
Check_Type(output_val, T_STRING);
|
|
253
|
-
Check_Type(format_val, T_STRING);
|
|
254
|
-
double scale = NUM2DBL(scale_val);
|
|
255
|
-
int quality = NUM2INT(quality_val);
|
|
256
|
-
validate_scale_or_raise(scale);
|
|
257
|
-
validate_quality_or_raise(quality);
|
|
258
|
-
const char *out_fmt = normalized_format(StringValueCStr(format_val));
|
|
259
|
-
if (!out_fmt || strcmp(out_fmt, "heic") == 0) rb_raise(eUnsupported, "unsupported output format");
|
|
260
|
-
|
|
261
|
-
double start = now_ms();
|
|
262
|
-
const char *input_fmt = NULL;
|
|
263
|
-
VipsImage *in = load_explicit(StringValueCStr(input_val), &input_fmt);
|
|
264
|
-
long long pixels = 0, max_pixels = 0;
|
|
265
|
-
if (pixels_exceed_limit(in, max_pixels_val, &pixels, &max_pixels)) {
|
|
266
|
-
g_object_unref(in);
|
|
267
|
-
raise_pixels_limit(pixels, max_pixels);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
VipsImage *rot = NULL;
|
|
271
|
-
if (vips_autorot(in, &rot, NULL) != 0) {
|
|
272
|
-
g_object_unref(in);
|
|
273
|
-
raise_vips();
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
VipsImage *out = NULL;
|
|
277
|
-
if (vips_resize(rot, &out, scale, NULL) != 0) {
|
|
278
|
-
g_object_unref(rot);
|
|
279
|
-
g_object_unref(in);
|
|
280
|
-
raise_vips();
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
if (save_explicit(out, StringValueCStr(output_val), out_fmt, quality) != 0) {
|
|
284
|
-
g_object_unref(out);
|
|
285
|
-
g_object_unref(rot);
|
|
286
|
-
g_object_unref(in);
|
|
287
|
-
raise_vips();
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
int out_width = out->Xsize;
|
|
291
|
-
int out_height = out->Ysize;
|
|
292
|
-
double duration_ms = now_ms() - start;
|
|
293
|
-
g_object_unref(out);
|
|
294
|
-
g_object_unref(rot);
|
|
295
|
-
g_object_unref(in);
|
|
296
|
-
|
|
297
|
-
VALUE hash = rb_hash_new();
|
|
298
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("input_format")), rb_str_new_cstr(input_fmt));
|
|
299
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("output_format")), rb_str_new_cstr(out_fmt));
|
|
300
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("width")), INT2NUM(out_width));
|
|
301
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("height")), INT2NUM(out_height));
|
|
302
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("duration_ms")), DBL2NUM(duration_ms));
|
|
303
|
-
return hash;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
static VALUE rb_crop_north(VALUE self, VALUE input_val, VALUE output_val, VALUE width_val, VALUE height_val, VALUE format_val, VALUE quality_val, VALUE max_pixels_val) {
|
|
307
|
-
Check_Type(input_val, T_STRING);
|
|
308
|
-
Check_Type(output_val, T_STRING);
|
|
309
|
-
Check_Type(format_val, T_STRING);
|
|
310
|
-
int width = NUM2INT(width_val);
|
|
311
|
-
int height = NUM2INT(height_val);
|
|
312
|
-
int quality = NUM2INT(quality_val);
|
|
313
|
-
validate_dimensions_or_raise(width, height);
|
|
314
|
-
validate_quality_or_raise(quality);
|
|
315
|
-
const char *out_fmt = normalized_format(StringValueCStr(format_val));
|
|
316
|
-
if (!out_fmt || strcmp(out_fmt, "heic") == 0) rb_raise(eUnsupported, "unsupported output format");
|
|
317
|
-
|
|
318
|
-
double start = now_ms();
|
|
319
|
-
const char *input_fmt = NULL;
|
|
320
|
-
VipsImage *in = load_explicit(StringValueCStr(input_val), &input_fmt);
|
|
321
|
-
long long pixels = 0, max_pixels = 0;
|
|
322
|
-
if (pixels_exceed_limit(in, max_pixels_val, &pixels, &max_pixels)) {
|
|
323
|
-
g_object_unref(in);
|
|
324
|
-
raise_pixels_limit(pixels, max_pixels);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
VipsImage *rot = NULL;
|
|
328
|
-
if (vips_autorot(in, &rot, NULL) != 0) {
|
|
329
|
-
g_object_unref(in);
|
|
330
|
-
raise_vips();
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
double sx = (double)width / (double)rot->Xsize;
|
|
334
|
-
double sy = (double)height / (double)rot->Ysize;
|
|
335
|
-
double scale = sx > sy ? sx : sy;
|
|
336
|
-
scale *= 1.0000001;
|
|
337
|
-
|
|
338
|
-
VipsImage *resized = NULL;
|
|
339
|
-
if (vips_resize(rot, &resized, scale, NULL) != 0) {
|
|
340
|
-
g_object_unref(rot);
|
|
341
|
-
g_object_unref(in);
|
|
342
|
-
raise_vips();
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
int left = (resized->Xsize - width) / 2;
|
|
346
|
-
if (left < 0) left = 0;
|
|
347
|
-
|
|
348
|
-
VipsImage *crop = NULL;
|
|
349
|
-
if (vips_extract_area(resized, &crop, left, 0, width, height, NULL) != 0) {
|
|
350
|
-
g_object_unref(resized);
|
|
351
|
-
g_object_unref(rot);
|
|
352
|
-
g_object_unref(in);
|
|
353
|
-
raise_vips();
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
if (save_explicit(crop, StringValueCStr(output_val), out_fmt, quality) != 0) {
|
|
357
|
-
g_object_unref(crop);
|
|
358
|
-
g_object_unref(resized);
|
|
359
|
-
g_object_unref(rot);
|
|
360
|
-
g_object_unref(in);
|
|
361
|
-
raise_vips();
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
int out_width = crop->Xsize;
|
|
365
|
-
int out_height = crop->Ysize;
|
|
366
|
-
double duration_ms = now_ms() - start;
|
|
367
|
-
g_object_unref(crop);
|
|
368
|
-
g_object_unref(resized);
|
|
369
|
-
g_object_unref(rot);
|
|
370
|
-
g_object_unref(in);
|
|
371
|
-
|
|
372
|
-
VALUE hash = rb_hash_new();
|
|
373
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("input_format")), rb_str_new_cstr(input_fmt));
|
|
374
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("output_format")), rb_str_new_cstr(out_fmt));
|
|
375
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("width")), INT2NUM(out_width));
|
|
376
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("height")), INT2NUM(out_height));
|
|
377
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("duration_ms")), DBL2NUM(duration_ms));
|
|
378
|
-
return hash;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
void Init_safe_image_native(void) {
|
|
382
|
-
mSafeImage = rb_define_module("SafeImage");
|
|
383
|
-
eError = rb_const_get(mSafeImage, rb_intern("Error"));
|
|
384
|
-
eUnsupported = rb_const_get(mSafeImage, rb_intern("UnsupportedFormatError"));
|
|
385
|
-
eInvalid = rb_const_get(mSafeImage, rb_intern("InvalidImageError"));
|
|
386
|
-
eLimit = rb_const_get(mSafeImage, rb_intern("LimitError"));
|
|
387
|
-
mNative = rb_define_module_under(mSafeImage, "Native");
|
|
388
|
-
rb_define_singleton_method(mNative, "probe", rb_probe, 1);
|
|
389
|
-
rb_define_singleton_method(mNative, "thumbnail", rb_thumbnail, 7);
|
|
390
|
-
rb_define_singleton_method(mNative, "resize", rb_resize, 6);
|
|
391
|
-
rb_define_singleton_method(mNative, "crop_north", rb_crop_north, 7);
|
|
392
|
-
}
|