rszr 0.5.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c4fc21afb848256445e310a0feebab86b2b10ee6baf464bd7e67a44b282b4d07
4
- data.tar.gz: bfe92bbc780dbf03135b4b3d7296df141bee74279412b6bd3b6d2ba70362a140
3
+ metadata.gz: ea3437991dd4694e35653020632c3fc360f787e1d391834780fdf118991a90ce
4
+ data.tar.gz: 37ccc3f8693e4fe9d41939cfc2d3786ef72ed24e344059f1dd4e4492efcae862
5
5
  SHA512:
6
- metadata.gz: 26d46f7b4d594da5e871d75975d99cd54358bb6585d8b18fbddcba1b4383970099891f7af6d16b51b8dcf19f5ed179b275d001b15f546c3baa58a3b6573613f9
7
- data.tar.gz: 0f5866b917277c7afb518fb15ba83451fc7b448681fec26dad4321d17cfa464854e9182cff88008f7ee25b1c6df135f6a14f3980b64a145db9ef93e035de1d9e
6
+ metadata.gz: 35b8219e81c0f89e4b51719617c13edf71421d7e3ed4e1998f8e26fc017545da76c5164c0bbd63c16e5c617cfdd4f5cb1f07a8e0bbdc83b7bb90ea7fbbd71907
7
+ data.tar.gz: fd4361d89e0ff577032da1dea39686c035604d98e5b6b935ca8de8f1289bdfe6cf5678c8842406c0b9c5829cfab2c03af8350670638824a99deeb176e4675428
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
- [![Gem Version](https://badge.fury.io/rb/rszr.svg)](http://badge.fury.io/rb/rszr) [![Build Status](https://travis-ci.org/mtgrosser/rszr.svg)](https://travis-ci.org/mtgrosser/rszr)
1
+ [![Gem Version](https://badge.fury.io/rb/rszr.svg)](http://badge.fury.io/rb/rszr) [![build](https://github.com/mtgrosser/rszr/actions/workflows/build.yml/badge.svg)](https://github.com/mtgrosser/rszr/actions/workflows/build.yml)
2
2
  # Rszr - fast image resizer for Ruby
3
3
 
4
- Rszr is an image resizer for Ruby based on the Imlib2 library. It is faster and consumes less memory than MiniMagick, rmagick and GD2.
4
+ Rszr is an image resizer for Ruby based on the Imlib2 library.
5
+ It is faster and consumes less memory than MiniMagick, GD2 and VIPS, and comes with an optional drop-in interface for Rails ActiveStorage image processing.
5
6
 
6
7
  ## Installation
7
8
 
@@ -13,7 +14,7 @@ gem 'rszr'
13
14
 
14
15
  ### Imlib2
15
16
 
16
- Rszr requires the Imlib2 library to do the heavy lifting.
17
+ Rszr requires the `Imlib2` library to do the heavy lifting.
17
18
 
18
19
  #### OS X
19
20
 
@@ -27,10 +28,14 @@ brew install imlib2
27
28
 
28
29
  Using your favourite package manager:
29
30
 
31
+ ##### RedHat-based
32
+
30
33
  ```bash
31
34
  yum install imlib2 imlib2-devel
32
35
  ```
33
36
 
37
+ ##### Debian-based
38
+
34
39
  ```bash
35
40
  apt-get install libimlib2 libimlib2-dev
36
41
  ```
@@ -47,7 +52,28 @@ image.save('resized.jpg')
47
52
 
48
53
  # save it as PNG
49
54
  image.save('resized.png')
55
+ ```
50
56
 
57
+ ### Image info
58
+ ```ruby
59
+ image.width => 400
60
+ image.height => 300
61
+ image.dimensions => [400, 300]
62
+ image.format => "jpeg"
63
+ image[0, 0] => <Rszr::Color::RGBA @red=38, @green=115, @blue=141, @alpha=255>
64
+ image[0, 0].to_hex => "26738dff"
65
+ image[0, 0].to_hex(rgb: true) => "26738d"
66
+ ```
67
+
68
+ ### Transformations
69
+
70
+ For each transformation, there is a bang! and non-bang method.
71
+ The bang method changes the image in place, while the non-bang method
72
+ creates a copy of the image in memory.
73
+
74
+ #### Resizing
75
+
76
+ ```ruby
51
77
  # auto height
52
78
  image.resize(400, :auto)
53
79
 
@@ -57,6 +83,22 @@ image.resize(:auto, 300)
57
83
  # scale factor
58
84
  image.resize(0.5)
59
85
 
86
+ # resize to fill
87
+ image.resize(400, 300, crop: true)
88
+
89
+ # resize to fill with gravity
90
+ # where gravity in [:n, :nw, :w, :sw, :w, :se, :e, :ne, :center]
91
+ image.resize(400, 300, crop: gravity)
92
+
93
+ # save memory, do not duplicate instance
94
+ image.resize!(400, :auto)
95
+ ```
96
+
97
+ Check out the [full list and demo of resize options](https://mtgrosser.github.io/rszr/resizing.html)!
98
+
99
+ #### Other transformations
100
+
101
+ ```ruby
60
102
  # crop
61
103
  image.crop(200, 200, 100, 100)
62
104
 
@@ -66,30 +108,110 @@ image.turn!(3)
66
108
  # rotate one time 90 deg counterclockwise
67
109
  image.turn!(-1)
68
110
 
111
+ # rotate by arbitrary angle
112
+ image.rotate(45)
113
+
114
+ # flip vertically
115
+ image.flip
116
+
117
+ # flop horizontally
118
+ image.flop
119
+
69
120
  # initialize copy
70
121
  image.dup
122
+ ```
71
123
 
72
- # save memory, do not duplicate instance
73
- image.resize!(400, :auto)
124
+ ### Filters
74
125
 
75
- # image info
76
- image.width => 400
77
- image.height => 300
78
- image.dimensions => [400, 300]
79
- image.format => "jpeg"
126
+ Filters also support bang! and non-bang methods.
127
+
128
+ ```ruby
129
+ # sharpen image by pixel radius
130
+ image.sharpen!(1)
131
+
132
+ # blur image by pixel radius
133
+ image.blur!(1)
134
+
135
+ # brighten
136
+ image.brighten(0.1)
137
+
138
+ # darken
139
+ image.brighten(-0.1)
140
+
141
+ # contrast
142
+ image.contrast(0.5)
143
+
144
+ # gamma
145
+ image.gamma(1.1)
146
+ ```
147
+
148
+ ### Image auto orientation
149
+
150
+ Auto-rotation is supported for JPEG and TIFF files that include the necessary
151
+ EXIF metadata.
152
+
153
+ ```ruby
154
+ # load and autorotate
155
+ image = Rszr::Image.load('image.jpg', autorotate: true)
156
+ ```
157
+
158
+ To enable autorotation by default:
159
+
160
+ ```ruby
161
+ # auto-rotate by default, for Rails apps put this into an initializer
162
+ Rszr.autorotate = true
163
+ ```
164
+
165
+ ## Rails / ActiveStorage interface
166
+
167
+ Rszr provides a drop-in interface to the `image_processing` gem.
168
+ It is faster than both `mini_magick` and `vips` and way easier to install than the latter.
169
+
170
+ ```ruby
171
+ # Gemfile
172
+ gem 'image_processing'
173
+ gem 'rszr'
174
+
175
+ # config/initializers/rszr.rb
176
+ require 'rszr/image_processing'
177
+
178
+ # config/application.rb
179
+ config.active_storage.variant_processor = :rszr
180
+ ```
181
+
182
+ When creating image variants, you can use all of Rszr's transformation methods:
183
+
184
+ ```erb
185
+ <%= image_tag user.avatar.variant(resize_to_fit: [300, 200]) %>
186
+ ```
187
+
188
+ ## Loading from and saving to memory
189
+
190
+ The `Imlib2` library is mainly file-oriented and doesn't provide a way of loading
191
+ the undecoded image from a memory buffer. Therefore, the functionality is
192
+ implemented on the Ruby side of the gem, writing the memory buffer to a Tempfile.
193
+ Currently, this local write cannot be avoided.
194
+
195
+ ```ruby
196
+ image = Rszr::Image.load_data(binary_data)
197
+
198
+ data = image.save_data(format: :jpeg)
80
199
  ```
81
200
 
82
201
  ## Thread safety
83
202
 
84
- As of version 0.4.0, Rszr is thread safe through the Ruby GIL.
203
+ As of version 0.5.0, Rszr is thread safe through Ruby's global VM lock.
85
204
  Use of any previous versions in a threaded environment is discouraged.
86
205
 
87
206
  ## Speed
88
207
 
89
- Resizing an 1500x997 JPEG image to 800x532, 100 times:
208
+ Resizing a 1500x997 JPEG image to 800x532, 500 times:
209
+ ![Speed](https://github.com/mtgrosser/rszr/blob/master/benchmark/speed.png)
210
+
90
211
 
91
212
  Library | Time
92
213
  ----------------|-----------
93
- MiniMagick | 12.9 s
94
- GD2 | 7.5 s
95
- Rszr | 2.8 s
214
+ MiniMagick | 27.0 s
215
+ GD2 | 28.2 s
216
+ VIPS | 13.6 s
217
+ Rszr | 7.9 s
data/ext/rszr/errors.c CHANGED
@@ -42,10 +42,15 @@ void Init_rszr_errors()
42
42
 
43
43
  static void rszr_raise_error_with_message(VALUE rb_error_class, Imlib_Load_Error error)
44
44
  {
45
- int error_index = (int) error - 1;
45
+ VALUE rb_error;
46
+ int error_index;
47
+
48
+ error_index = (int) error - 1;
49
+
46
50
  if (error_index < 1 || error_index > RSZR_MAX_ERROR_INDEX)
47
- error_index = 13;
48
- VALUE rb_error = rb_exc_new2(rb_error_class, sRszrErrorMessages[error_index]);
51
+ error_index = RSZR_MAX_ERROR_INDEX;
52
+
53
+ rb_error = rb_exc_new2(rb_error_class, sRszrErrorMessages[error_index]);
49
54
  rb_exc_raise(rb_error);
50
55
  }
51
56
 
data/ext/rszr/image.c CHANGED
@@ -27,7 +27,6 @@ static void rszr_image_deallocate(rszr_image_handle * handle)
27
27
  // fprintf(stderr, "\n");
28
28
  }
29
29
 
30
-
31
30
  static VALUE rszr_image_s_allocate(VALUE klass)
32
31
  {
33
32
  rszr_image_handle * handle = calloc(1, sizeof(rszr_image_handle));
@@ -61,13 +60,20 @@ static VALUE rszr_image_s__load(VALUE klass, VALUE rb_path)
61
60
  path = StringValueCStr(rb_path);
62
61
 
63
62
  imlib_set_cache_size(0);
64
- image = imlib_load_image_with_error_return(path, &error);
63
+ image = imlib_load_image_without_cache(path);
65
64
 
66
65
  if (!image) {
67
- rszr_raise_load_error(error);
68
- return Qnil;
66
+ image = imlib_load_image_with_error_return(path, &error);
67
+
68
+ if (!image) {
69
+ rszr_raise_load_error(error);
70
+ return Qnil;
71
+ }
69
72
  }
70
73
 
74
+ imlib_context_set_image(image);
75
+ imlib_image_set_irrelevant_format(0);
76
+
71
77
  oImage = rszr_image_s_allocate(cImage);
72
78
  Data_Get_Struct(oImage, rszr_image_handle, handle);
73
79
  handle->image = image;
@@ -75,7 +81,7 @@ static VALUE rszr_image_s__load(VALUE klass, VALUE rb_path)
75
81
  }
76
82
 
77
83
 
78
- static VALUE rszr_image_format(VALUE self)
84
+ static VALUE rszr_image__format_get(VALUE self)
79
85
  {
80
86
  rszr_image_handle * handle;
81
87
  char * format;
@@ -93,6 +99,20 @@ static VALUE rszr_image_format(VALUE self)
93
99
  }
94
100
 
95
101
 
102
+ static VALUE rszr_image__format_set(VALUE self, VALUE rb_format)
103
+ {
104
+ rszr_image_handle * handle;
105
+ char * format = StringValueCStr(rb_format);
106
+
107
+ Data_Get_Struct(self, rszr_image_handle, handle);
108
+
109
+ imlib_context_set_image(handle->image);
110
+ imlib_image_set_format(format);
111
+
112
+ return self;
113
+ }
114
+
115
+
96
116
  static VALUE rszr_image_width(VALUE self)
97
117
  {
98
118
  rszr_image_handle * handle;
@@ -121,6 +141,70 @@ static VALUE rszr_image_height(VALUE self)
121
141
  }
122
142
 
123
143
 
144
+ static VALUE rszr_image__pixel_get(VALUE self, VALUE rb_x, VALUE rb_y)
145
+ {
146
+ rszr_image_handle * handle;
147
+ Imlib_Color color_return;
148
+ VALUE rb_rgba;
149
+ int x, y;
150
+
151
+ Check_Type(rb_x, T_FIXNUM);
152
+ x = FIX2INT(rb_x);
153
+ Check_Type(rb_y, T_FIXNUM);
154
+ y = FIX2INT(rb_y);
155
+
156
+ Data_Get_Struct(self, rszr_image_handle, handle);
157
+
158
+ imlib_context_set_image(handle->image);
159
+ imlib_image_query_pixel(x, y, &color_return);
160
+
161
+ rb_rgba = rb_ary_new3(4, INT2NUM(color_return.red),
162
+ INT2NUM(color_return.green),
163
+ INT2NUM(color_return.blue),
164
+ INT2NUM(color_return.alpha));
165
+ return rb_rgba;
166
+ }
167
+
168
+
169
+ /*
170
+ static VALUE rszr_image_get_quality(VALUE self)
171
+ {
172
+ rszr_image_handle * handle;
173
+ int quality;
174
+
175
+ Data_Get_Struct(self, rszr_image_handle, handle);
176
+
177
+ imlib_context_set_image(handle->image);
178
+ quality = imlib_image_get_attached_value("quality");
179
+
180
+ if (quality) {
181
+ return INT2NUM(quality);
182
+ } else {
183
+ return Qnil;
184
+ }
185
+ }
186
+
187
+ static VALUE rszr_image_set_quality(VALUE self, VALUE rb_quality)
188
+ {
189
+ rszr_image_handle * handle;
190
+ int quality;
191
+
192
+ Check_Type(rb_quality, T_FIXNUM);
193
+ quality = FIX2INT(rb_quality);
194
+ if (quality <= 0) {
195
+ rb_raise(rb_eArgError, "quality must be >= 0");
196
+ return Qnil;
197
+ }
198
+
199
+ Data_Get_Struct(self, rszr_image_handle, handle);
200
+
201
+ imlib_context_set_image(handle->image);
202
+ imlib_image_attach_data_value("quality", NULL, quality, NULL);
203
+
204
+ return INT2NUM(quality);
205
+ }
206
+ */
207
+
124
208
  static VALUE rszr_image_dup(VALUE self)
125
209
  {
126
210
  rszr_image_handle * handle;
@@ -159,6 +243,105 @@ static VALUE rszr_image__turn_bang(VALUE self, VALUE orientation)
159
243
  }
160
244
 
161
245
 
246
+ static VALUE rszr_image_flop_bang(VALUE self)
247
+ {
248
+ rszr_image_handle * handle;
249
+
250
+ Data_Get_Struct(self, rszr_image_handle, handle);
251
+
252
+ imlib_context_set_image(handle->image);
253
+ imlib_image_flip_horizontal();
254
+
255
+ return self;
256
+ }
257
+
258
+
259
+ static VALUE rszr_image_flip_bang(VALUE self)
260
+ {
261
+ rszr_image_handle * handle;
262
+
263
+ Data_Get_Struct(self, rszr_image_handle, handle);
264
+
265
+ imlib_context_set_image(handle->image);
266
+ imlib_image_flip_vertical();
267
+
268
+ return self;
269
+ }
270
+
271
+
272
+ static VALUE rszr_image__rotate(VALUE self, VALUE bang, VALUE rb_angle)
273
+ {
274
+ rszr_image_handle * handle;
275
+ rszr_image_handle * rotated_handle;
276
+ Imlib_Image rotated_image;
277
+ VALUE oRotatedImage;
278
+ double angle;
279
+
280
+ angle = NUM2DBL(rb_angle);
281
+
282
+ Data_Get_Struct(self, rszr_image_handle, handle);
283
+
284
+ imlib_context_set_image(handle->image);
285
+ rotated_image = imlib_create_rotated_image(angle);
286
+
287
+ if (!rotated_image) {
288
+ rb_raise(eRszrTransformationError, "error rotating image");
289
+ return Qnil;
290
+ }
291
+
292
+ if (RTEST(bang)) {
293
+ rszr_free_image(handle->image);
294
+ handle->image = rotated_image;
295
+
296
+ return self;
297
+ }
298
+ else {
299
+ oRotatedImage = rszr_image_s_allocate(cImage);
300
+ Data_Get_Struct(oRotatedImage, rszr_image_handle, rotated_handle);
301
+ rotated_handle->image = rotated_image;
302
+
303
+ return oRotatedImage;
304
+ }
305
+ }
306
+
307
+
308
+ static VALUE rszr_image_filter_bang(VALUE self, VALUE rb_filter_expr)
309
+ {
310
+ rszr_image_handle * handle;
311
+ char * filter_expr;
312
+
313
+ filter_expr = StringValueCStr(rb_filter_expr);
314
+
315
+ Data_Get_Struct(self, rszr_image_handle, handle);
316
+
317
+ imlib_context_set_image(handle->image);
318
+ imlib_apply_filter(filter_expr);
319
+
320
+ return self;
321
+ }
322
+
323
+
324
+ static VALUE rszr_image__sharpen_bang(VALUE self, VALUE rb_radius)
325
+ {
326
+ rszr_image_handle * handle;
327
+ int radius;
328
+
329
+ radius = NUM2INT(rb_radius);
330
+
331
+ Data_Get_Struct(self, rszr_image_handle, handle);
332
+
333
+ imlib_context_set_image(handle->image);
334
+
335
+ if (radius >= 0) {
336
+ imlib_image_sharpen(radius);
337
+ } else {
338
+ imlib_image_blur(-radius);
339
+ }
340
+
341
+ return self;
342
+ }
343
+
344
+
162
345
  static Imlib_Image rszr_create_cropped_scaled_image(const Imlib_Image image, VALUE rb_src_x, VALUE rb_src_y, VALUE rb_src_w, VALUE rb_src_h, VALUE rb_dst_w, VALUE rb_dst_h)
163
346
  {
164
347
  Imlib_Image resized_image;
@@ -262,20 +445,24 @@ static VALUE rszr_image__crop(VALUE self, VALUE bang, VALUE rb_x, VALUE rb_y, VA
262
445
  }
263
446
 
264
447
 
265
- static VALUE rszr_image__save(VALUE self, VALUE rb_path, VALUE rb_format)
448
+ static VALUE rszr_image__save(VALUE self, VALUE rb_path, VALUE rb_format, VALUE rb_quality)
266
449
  {
267
450
  rszr_image_handle * handle;
268
451
  char * path;
269
452
  char * format;
453
+ int quality;
270
454
  Imlib_Load_Error save_error;
271
455
 
272
456
  path = StringValueCStr(rb_path);
273
457
  format = StringValueCStr(rb_format);
274
-
458
+ quality = (NIL_P(rb_quality)) ? 0 : FIX2INT(rb_quality);
459
+
275
460
  Data_Get_Struct(self, rszr_image_handle, handle);
276
461
 
277
462
  imlib_context_set_image(handle->image);
278
463
  imlib_image_set_format(format);
464
+ if (quality)
465
+ imlib_image_attach_data_value("quality", NULL, quality, NULL);
279
466
  imlib_save_image_with_error_return(path, &save_error);
280
467
 
281
468
  if (save_error) {
@@ -298,12 +485,25 @@ void Init_rszr_image()
298
485
  rb_define_method(cImage, "initialize", rszr_image_initialize, 2);
299
486
  rb_define_method(cImage, "width", rszr_image_width, 0);
300
487
  rb_define_method(cImage, "height", rszr_image_height, 0);
301
- rb_define_method(cImage, "format", rszr_image_format, 0);
302
488
  rb_define_method(cImage, "dup", rszr_image_dup, 0);
489
+ rb_define_method(cImage, "filter!", rszr_image_filter_bang, 1);
490
+ rb_define_method(cImage, "flop!", rszr_image_flop_bang, 0);
491
+ rb_define_method(cImage, "flip!", rszr_image_flip_bang, 0);
492
+
493
+ // rb_define_method(cImage, "quality", rszr_image_get_quality, 0);
494
+ // rb_define_method(cImage, "quality=", rszr_image_set_quality, 1);
495
+
496
+ rb_define_protected_method(cImage, "_format", rszr_image__format_get, 0);
497
+ rb_define_protected_method(cImage, "_format=", rszr_image__format_set, 1);
498
+
303
499
  rb_define_private_method(cImage, "_resize", rszr_image__resize, 7);
304
500
  rb_define_private_method(cImage, "_crop", rszr_image__crop, 5);
305
501
  rb_define_private_method(cImage, "_turn!", rszr_image__turn_bang, 1);
306
- rb_define_private_method(cImage, "_save", rszr_image__save, 2);
502
+ rb_define_private_method(cImage, "_rotate", rszr_image__rotate, 2);
503
+ rb_define_private_method(cImage, "_sharpen!", rszr_image__sharpen_bang, 1);
504
+ rb_define_private_method(cImage, "_pixel", rszr_image__pixel_get, 2);
505
+
506
+ rb_define_private_method(cImage, "_save", rszr_image__save, 3);
307
507
  }
308
508
 
309
509
  #endif
data/ext/rszr/rszr.h CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  #include "ruby.h"
5
5
  #include <Imlib2.h>
6
+ #include <libexif/exif-data.h>
6
7
 
7
8
  extern VALUE mRszr;
8
9
  void Init_rszr();
@@ -0,0 +1,24 @@
1
+ module Rszr
2
+ class BatchTransformation
3
+ attr_reader :transformations, :image
4
+
5
+ def initialize(path, **opts)
6
+ puts "INITIALIZED BATCH for #{path}"
7
+ @image = path.is_a?(Image) ? path : Image.load(path, **opts)
8
+ @transformations = []
9
+ end
10
+
11
+ Image::Transformations.instance_methods.grep(/\w\z/) do |method|
12
+ define_method method do |*args|
13
+ transformations << [method, args]
14
+ self
15
+ end
16
+ end
17
+
18
+ def call
19
+ transformations.each { |method, args| image.public_send("#{method}!", *args) }
20
+ image
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ module Rszr
2
+ module Buffered
3
+ def self.included(base)
4
+ base.extend Buffered
5
+ end
6
+
7
+ private
8
+
9
+ def with_tempfile(format, data = nil)
10
+ raise ArgumentError, 'format is required' unless format
11
+ result = nil
12
+ Tempfile.create(['rszr-buffer', ".#{format}"], encoding: 'BINARY') do |file|
13
+ if data
14
+ file.binmode
15
+ file << data
16
+ file.fsync
17
+ file.rewind
18
+ end
19
+ result = yield(file)
20
+ end
21
+ result
22
+ end
23
+
24
+ end
25
+ end
data/lib/rszr/color.rb ADDED
@@ -0,0 +1,25 @@
1
+ module Rszr
2
+ module Color
3
+
4
+ class RGBA
5
+ attr_reader :red, :green, :blue, :alpha
6
+
7
+ def initialize(red, green, blue, alpha = 255)
8
+ if red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255 || alpha < 0 || alpha > 255
9
+ raise ArgumentError, 'color out of range'
10
+ end
11
+ @red, @green, @blue, @alpha = red, green, blue, alpha
12
+ end
13
+
14
+ def to_i(rgb: false)
15
+ i = red.to_i << 24 | green.to_i << 16 | blue.to_i << 8 | alpha.to_i
16
+ rgb ? i >> 8 : i
17
+ end
18
+
19
+ def to_hex(rgb: false)
20
+ "%0#{rgb ? 6 : 8}x" % to_i(rgb: rgb)
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,60 @@
1
+ # Type reader adapted from fastimage
2
+ # https://github.com/sdsykes/fastimage/
3
+
4
+ module Rszr
5
+ module Identification
6
+
7
+ private
8
+
9
+ def identify(data)
10
+ case data[0, 2]
11
+ when 'BM'
12
+ :bmp
13
+ when 'GI'
14
+ :gif
15
+ when 0xff.chr + 0xd8.chr
16
+ :jpeg
17
+ when 0x89.chr + 'P'
18
+ :png
19
+ when 'II', 'MM'
20
+ case data[0, 11][8..10] # @stream.peek(11)[8..10]
21
+ when 'APC', "CR\002"
22
+ nil # do not recognise CRW or CR2 as tiff
23
+ else
24
+ :tiff
25
+ end
26
+ when '8B'
27
+ :psd
28
+ when "\0\0"
29
+ case data[0, 3].bytes.last #@stream.peek(3).bytes.to_a.last
30
+ when 0
31
+ # http://www.ftyps.com/what.html
32
+ # HEIC is composed of nested "boxes". Each box has a header composed of
33
+ # - Size (32 bit integer)
34
+ # - Box type (4 chars)
35
+ # - Extended size: only if size === 1, the type field is followed by 64 bit integer of extended size
36
+ # - Payload: Type-dependent
37
+ case data[0, 12][4..-1] #@stream.peek(12)[4..-1]
38
+ when 'ftypheic'
39
+ :heic
40
+ when 'ftypmif1'
41
+ :heif
42
+ end
43
+ # ico has either a 1 (for ico format) or 2 (for cursor) at offset 3
44
+ when 1 then :ico
45
+ when 2 then :cur
46
+ end
47
+ when 'RI'
48
+ :webp if data[0, 12][8..11] == 'WEBP' #@stream.peek(12)[8..11] == "WEBP"
49
+ when "<s"
50
+ :svg if data[0, 4] == '<svg' #@stream.peek(4) == "<svg"
51
+ when /\s\s|\s<|<[?!]/, 0xef.chr + 0xbb.chr
52
+ # Peek 10 more chars each time, and if end of file is reached just raise
53
+ # unknown. We assume the <svg tag cannot be within 10 chars of the end of
54
+ # the file, and is within the first 250 chars.
55
+ :svg if (1..25).detect { |n| data[0, 10 * n]&.include?('<svg') }
56
+ end
57
+ end
58
+
59
+ end
60
+ end