tinyimg 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +29 -0
- data/LICENSE +21 -0
- data/README.md +74 -0
- data/ext/tinyimg/extconf.rb +35 -0
- data/ext/tinyimg/tinyimg.c +255 -0
- data/lib/tinyimg.rb +138 -0
- data/spec/samples/duck.png +0 -0
- data/spec/tinyimg_spec.rb +122 -0
- data/spec/tmp/.keep +0 -0
- data/tinyimg.gemspec +20 -0
- metadata +74 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a953ed5351abc1984d8739f8ec3d91a1bed9654a
|
4
|
+
data.tar.gz: c73c4110056238bc19a1c6e7ae6204b4c98a9968
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 810f4ae575ca67c08c7a1551eb0abf4f16ca6fb12c77d4d9dd9227bd448856644d063424c0d34a6251aa2a52ef0be38ad9180e29f05d3e2a81ccb487d7c260b3
|
7
|
+
data.tar.gz: f7021348faf7e950ed87c51c12383684238bdfef806fb9f11d36bdb0c1e51c600576bb7fe7ac2ef48f31361d6769ffb50b988166ffabbb625cdf6983e7142560
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
timyimg (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.2.5)
|
10
|
+
rspec (3.3.0)
|
11
|
+
rspec-core (~> 3.3.0)
|
12
|
+
rspec-expectations (~> 3.3.0)
|
13
|
+
rspec-mocks (~> 3.3.0)
|
14
|
+
rspec-core (3.3.2)
|
15
|
+
rspec-support (~> 3.3.0)
|
16
|
+
rspec-expectations (3.3.1)
|
17
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
18
|
+
rspec-support (~> 3.3.0)
|
19
|
+
rspec-mocks (3.3.2)
|
20
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
21
|
+
rspec-support (~> 3.3.0)
|
22
|
+
rspec-support (3.3.0)
|
23
|
+
|
24
|
+
PLATFORMS
|
25
|
+
ruby
|
26
|
+
|
27
|
+
DEPENDENCIES
|
28
|
+
rspec (~> 3.0)
|
29
|
+
timyimg!
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright 2015 Roger Nesbitt
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Tinyimg
|
2
|
+
|
3
|
+
Load a JPEG or PNG, get its dimensions, resize it, and extract it again as either a JPEG or PNG.
|
4
|
+
|
5
|
+
This gem can work from image data stored in memory, as well as from a file or IO stream.
|
6
|
+
It's been coded to be as efficient as possible, so doesn't use temporary files.
|
7
|
+
|
8
|
+
Tinyimg uses libgd for its image processing. libgd is significantly less painful to install than
|
9
|
+
ImageMagick and friends! Other than that, all that is required is Ruby 2.0+.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
If you don't already have libgd installed on your system, install it.
|
14
|
+
|
15
|
+
On OS X, install Homebrew and
|
16
|
+
|
17
|
+
brew install libgd
|
18
|
+
|
19
|
+
On a Linux system, use your package manager. For example, on Debian/Ubuntu:
|
20
|
+
|
21
|
+
sudo apt-get install libgd-dev
|
22
|
+
|
23
|
+
Then add tinyimg to your project's Gemfile
|
24
|
+
|
25
|
+
gem 'tinyimg'
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
Load the image with the method that fits your use case:
|
30
|
+
|
31
|
+
image = Tinyimg.from_string(an_image_that_is_already_loaded)
|
32
|
+
image = Tinyimg.from_file("some_image.png")
|
33
|
+
image = Tinyimg.from_io(params[:uploaded_file])
|
34
|
+
|
35
|
+
Manipulate it by using one of the resize commands:
|
36
|
+
|
37
|
+
image.resize_to_fit!(100, 100) # image will be 100x100 maximum
|
38
|
+
image.resize_to_fill!(100, 100) # image will be 100x100 minimum
|
39
|
+
image.resize!(100, 100) # forces image to be exactly 100x100
|
40
|
+
|
41
|
+
Then get an image back:
|
42
|
+
|
43
|
+
image.to_png # returns a string
|
44
|
+
image.to_jpeg # returns a string
|
45
|
+
image.save("some_image.jpg") # file type auto-determined by extension
|
46
|
+
|
47
|
+
You can ask for the image's dimensions:
|
48
|
+
|
49
|
+
image.width # => 120
|
50
|
+
image.height # => 80
|
51
|
+
image.dimensions # => [120, 80]
|
52
|
+
|
53
|
+
You can also use the non-! versions of the resize methods: `resize`, `resize_to_fit` and `resize_to_fill`.
|
54
|
+
These create a new image in memory and return it, leaving the old image untouched. This is useful if you want
|
55
|
+
to resize an original image to multiple sizes. Using these methods will take more memory.
|
56
|
+
|
57
|
+
## Examples
|
58
|
+
|
59
|
+
Take an uploaded file, save the original as a JPEG, then resize to create a thumbnail and save that too:
|
60
|
+
|
61
|
+
Tinyimg
|
62
|
+
.from_io(params[:uploaded_file])
|
63
|
+
.save("#{path}/full_size.jpg")
|
64
|
+
.resize_to_fit!(100, 100)
|
65
|
+
.save("#{path}/thumbnail.jpg")
|
66
|
+
|
67
|
+
Load a file from disk, make a thumbnail, and return it as a JPEG so we can save it into our database:
|
68
|
+
|
69
|
+
data = Tinyimg.from_file(image_filename).resize_to_fit!(100, 100).to_jpeg
|
70
|
+
user.update!(thumbnail_image: data)
|
71
|
+
|
72
|
+
## Author and licence
|
73
|
+
|
74
|
+
Copyright 2015 Roger Nesbitt. Licenced under the MIT licence.
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
def filter_dirs(dirs)
|
4
|
+
dirs.select {|dir| Dir.exists?(dir)}
|
5
|
+
end
|
6
|
+
|
7
|
+
def cannot_find_gd
|
8
|
+
abort <<-TEXT
|
9
|
+
*******************************************************************************
|
10
|
+
It looks like libgd is not installed on your system.
|
11
|
+
|
12
|
+
If you're on OS X, install homebrew and try
|
13
|
+
brew install libgd
|
14
|
+
|
15
|
+
If you're on Debian/Ubuntu linux, try
|
16
|
+
apt-get install libgd-dev
|
17
|
+
*******************************************************************************
|
18
|
+
TEXT
|
19
|
+
end
|
20
|
+
|
21
|
+
header_dirs = %w(/opt/local/include /usr/local/include /usr/include)
|
22
|
+
lib_dirs = %w(/opt/local/lib /usr/local/lib /usr/lib)
|
23
|
+
|
24
|
+
dir_config('gd', filter_dirs(header_dirs), filter_dirs(lib_dirs))
|
25
|
+
|
26
|
+
have_library('gd', 'gdFree') or cannot_find_gd
|
27
|
+
have_header('gd.h') or cannot_find_gd
|
28
|
+
have_func('gdImageJpegPtr') or abort "Your libgd is too old! You need at least version 1.8.0, and preferably 2.1.1+."
|
29
|
+
have_func('gdImageFile')
|
30
|
+
have_func('gdImageCreateFromFile')
|
31
|
+
have_func('gdImageAlphaBlending')
|
32
|
+
have_func('gdImageClone')
|
33
|
+
have_func('gdImagePngPtrEx')
|
34
|
+
|
35
|
+
create_makefile 'tinyimg/tinyimg'
|
@@ -0,0 +1,255 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <gd.h>
|
3
|
+
|
4
|
+
gdImagePtr get_image_data(VALUE self)
|
5
|
+
{
|
6
|
+
gdImagePtr image;
|
7
|
+
VALUE wrapped_image = rb_iv_get(self, "@data");
|
8
|
+
Data_Get_Struct(wrapped_image, struct gdImageStruct, image);
|
9
|
+
return image;
|
10
|
+
}
|
11
|
+
|
12
|
+
void set_image_data(VALUE self, gdImagePtr image)
|
13
|
+
{
|
14
|
+
VALUE klass, value;
|
15
|
+
|
16
|
+
klass = rb_const_get_at(rb_class_of(self), rb_intern("Image"));
|
17
|
+
value = Data_Wrap_Struct(klass, 0, gdImageDestroy, image);
|
18
|
+
rb_iv_set(self, "@data", value);
|
19
|
+
}
|
20
|
+
|
21
|
+
void set_alpha(gdImagePtr image)
|
22
|
+
{
|
23
|
+
#ifdef HAVE_GDIMAGEALPHABLENDING
|
24
|
+
gdImageAlphaBlending(image, 0);
|
25
|
+
gdImageSaveAlpha(image, 1);
|
26
|
+
#endif
|
27
|
+
}
|
28
|
+
|
29
|
+
VALUE retrieve_image_dimensions(VALUE self)
|
30
|
+
{
|
31
|
+
gdImagePtr image = get_image_data(self);
|
32
|
+
|
33
|
+
rb_iv_set(self, "@width", INT2FIX(gdImageSX(image)));
|
34
|
+
rb_iv_set(self, "@height", INT2FIX(gdImageSY(image)));
|
35
|
+
|
36
|
+
return Qnil;
|
37
|
+
}
|
38
|
+
|
39
|
+
VALUE load_from_string(VALUE self, VALUE input, VALUE type)
|
40
|
+
{
|
41
|
+
gdImagePtr image;
|
42
|
+
ID type_id;
|
43
|
+
|
44
|
+
Check_Type(input, T_STRING);
|
45
|
+
Check_Type(type, T_SYMBOL);
|
46
|
+
type_id = SYM2ID(type);
|
47
|
+
|
48
|
+
if (type_id == rb_intern("png")) {
|
49
|
+
image = gdImageCreateFromPngPtr(RSTRING_LEN(input), RSTRING_PTR(input));
|
50
|
+
}
|
51
|
+
else if (type_id == rb_intern("jpeg")) {
|
52
|
+
image = gdImageCreateFromJpegPtr(RSTRING_LEN(input), RSTRING_PTR(input));
|
53
|
+
}
|
54
|
+
else {
|
55
|
+
rb_raise(rb_eArgError, "type must be a supported image type");
|
56
|
+
}
|
57
|
+
|
58
|
+
if (!image) {
|
59
|
+
rb_raise(rb_eRuntimeError, "Error loading image data");
|
60
|
+
}
|
61
|
+
|
62
|
+
set_image_data(self, image);
|
63
|
+
set_alpha(image);
|
64
|
+
|
65
|
+
retrieve_image_dimensions(self);
|
66
|
+
|
67
|
+
return self;
|
68
|
+
}
|
69
|
+
|
70
|
+
#ifdef HAVE_GDIMAGECREATEFROMFILE
|
71
|
+
VALUE load_from_file(VALUE self, VALUE filename)
|
72
|
+
{
|
73
|
+
gdImagePtr image;
|
74
|
+
|
75
|
+
Check_Type(filename, T_STRING);
|
76
|
+
|
77
|
+
image = gdImageCreateFromFile(StringValueCStr(filename));
|
78
|
+
if (!image) {
|
79
|
+
rb_raise(rb_eRuntimeError, "Error loading image data");
|
80
|
+
}
|
81
|
+
|
82
|
+
set_image_data(self, image);
|
83
|
+
set_alpha(image);
|
84
|
+
|
85
|
+
retrieve_image_dimensions(self);
|
86
|
+
|
87
|
+
return self;
|
88
|
+
}
|
89
|
+
#endif
|
90
|
+
|
91
|
+
VALUE initialize_copy(int argc, VALUE *argv, VALUE self)
|
92
|
+
{
|
93
|
+
gdImagePtr image, original;
|
94
|
+
|
95
|
+
rb_call_super(argc, argv);
|
96
|
+
|
97
|
+
original = get_image_data(self);
|
98
|
+
|
99
|
+
#ifdef HAVE_GDIMAGECLONE
|
100
|
+
image = gdImageClone(original);
|
101
|
+
#else
|
102
|
+
int width = gdImageSX(original), height = gdImageSY(original);
|
103
|
+
|
104
|
+
image = gdImageCreateTrueColor(width, height);
|
105
|
+
set_alpha(image);
|
106
|
+
|
107
|
+
gdImageCopy(image, original, 0, 0, 0, 0, width, height);
|
108
|
+
#endif
|
109
|
+
|
110
|
+
set_image_data(self, image);
|
111
|
+
|
112
|
+
return Qnil;
|
113
|
+
}
|
114
|
+
|
115
|
+
#ifdef HAVE_GDIMAGEFILE
|
116
|
+
VALUE save_to_file(VALUE self, VALUE filename)
|
117
|
+
{
|
118
|
+
gdImagePtr image;
|
119
|
+
int result;
|
120
|
+
|
121
|
+
Check_Type(filename, T_STRING);
|
122
|
+
|
123
|
+
image = get_image_data(self);
|
124
|
+
result = gdImageFile(image, StringValueCStr(filename));
|
125
|
+
|
126
|
+
if (result == GD_FALSE) {
|
127
|
+
rb_raise(rb_eRuntimeError, "Unknown error occurred while trying to save the file; check it is using a known filename");
|
128
|
+
}
|
129
|
+
|
130
|
+
return self;
|
131
|
+
}
|
132
|
+
#endif
|
133
|
+
|
134
|
+
VALUE to_jpeg(int argc, VALUE *argv, VALUE self)
|
135
|
+
{
|
136
|
+
gdImagePtr image;
|
137
|
+
char *image_data;
|
138
|
+
int size, quality;
|
139
|
+
VALUE quality_value;
|
140
|
+
|
141
|
+
rb_scan_args(argc, argv, "01", &quality_value);
|
142
|
+
|
143
|
+
if (NIL_P(quality_value)) {
|
144
|
+
quality = -1;
|
145
|
+
}
|
146
|
+
else {
|
147
|
+
Check_Type(quality_value, T_FIXNUM);
|
148
|
+
quality = FIX2INT(quality_value);
|
149
|
+
}
|
150
|
+
|
151
|
+
if (quality < -1 || quality > 100) {
|
152
|
+
rb_raise(rb_eArgError, "Quality must be between 0 and 100, or -1 for default");
|
153
|
+
}
|
154
|
+
|
155
|
+
image = get_image_data(self);
|
156
|
+
|
157
|
+
image_data = (char *) gdImageJpegPtr(image, &size, quality);
|
158
|
+
if (!image_data) {
|
159
|
+
rb_raise(rb_eRuntimeError, "Unknown error occurred while trying to build a JPEG");
|
160
|
+
}
|
161
|
+
|
162
|
+
VALUE output = rb_str_new(image_data, size);
|
163
|
+
gdFree(image_data);
|
164
|
+
return output;
|
165
|
+
}
|
166
|
+
|
167
|
+
VALUE to_png(int argc, VALUE *argv, VALUE self)
|
168
|
+
{
|
169
|
+
gdImagePtr image;
|
170
|
+
char *image_data;
|
171
|
+
int size;
|
172
|
+
VALUE compression_value;
|
173
|
+
int compression;
|
174
|
+
|
175
|
+
rb_scan_args(argc, argv, "01", &compression_value);
|
176
|
+
|
177
|
+
image = get_image_data(self);
|
178
|
+
|
179
|
+
if (NIL_P(compression_value)) {
|
180
|
+
image_data = (char *) gdImagePngPtr(image, &size);
|
181
|
+
}
|
182
|
+
else {
|
183
|
+
Check_Type(compression_value, T_FIXNUM);
|
184
|
+
compression = FIX2INT(compression_value);
|
185
|
+
|
186
|
+
if (compression < 0 || compression > 9) {
|
187
|
+
rb_raise(rb_eArgError, "Compression must be between 0 and 9");
|
188
|
+
}
|
189
|
+
|
190
|
+
#ifdef HAVE_GDIMAGEPNGPTREX
|
191
|
+
image_data = (char *) gdImagePngPtrEx(image, &size, compression);
|
192
|
+
#else
|
193
|
+
image_data = (char *) gdImagePngPtr(image, &size);
|
194
|
+
#endif
|
195
|
+
}
|
196
|
+
|
197
|
+
if (!image_data) {
|
198
|
+
rb_raise(rb_eRuntimeError, "Unknown error occurred while trying to build a PNG");
|
199
|
+
}
|
200
|
+
|
201
|
+
VALUE output = rb_str_new(image_data, size);
|
202
|
+
gdFree(image_data);
|
203
|
+
return output;
|
204
|
+
}
|
205
|
+
|
206
|
+
VALUE resize_bang(VALUE self, VALUE width_value, VALUE height_value)
|
207
|
+
{
|
208
|
+
gdImagePtr image_in, image_out;
|
209
|
+
int width, height;
|
210
|
+
|
211
|
+
Check_Type(width_value, T_FIXNUM);
|
212
|
+
Check_Type(height_value, T_FIXNUM);
|
213
|
+
|
214
|
+
width = FIX2INT(width_value);
|
215
|
+
height = FIX2INT(height_value);
|
216
|
+
|
217
|
+
if (width < 0 || height < 0) {
|
218
|
+
rb_raise(rb_eArgError, "width and height must both be positive integers");
|
219
|
+
}
|
220
|
+
|
221
|
+
image_in = get_image_data(self);
|
222
|
+
image_out = gdImageCreateTrueColor(width, height);
|
223
|
+
set_alpha(image_out);
|
224
|
+
|
225
|
+
gdImageCopyResampled(
|
226
|
+
image_out, image_in, 0, 0, 0, 0,
|
227
|
+
gdImageSX(image_out), gdImageSY(image_out),
|
228
|
+
gdImageSX(image_in), gdImageSY(image_in)
|
229
|
+
);
|
230
|
+
|
231
|
+
set_image_data(self, image_out);
|
232
|
+
|
233
|
+
retrieve_image_dimensions(self);
|
234
|
+
|
235
|
+
return self;
|
236
|
+
}
|
237
|
+
|
238
|
+
void Init_tinyimg()
|
239
|
+
{
|
240
|
+
VALUE cTinyimg = rb_define_class("Tinyimg", rb_cObject);
|
241
|
+
rb_define_class_under(cTinyimg, "Image", rb_cObject);
|
242
|
+
|
243
|
+
rb_define_method(cTinyimg, "resize!", resize_bang, 2);
|
244
|
+
rb_define_method(cTinyimg, "to_jpeg", to_jpeg, -1);
|
245
|
+
rb_define_method(cTinyimg, "to_png", to_png, -1);
|
246
|
+
rb_define_private_method(cTinyimg, "initialize_copy", initialize_copy, -1);
|
247
|
+
rb_define_private_method(cTinyimg, "load_from_string", load_from_string, 2);
|
248
|
+
rb_define_private_method(cTinyimg, "retrieve_image_dimensions", retrieve_image_dimensions, 0);
|
249
|
+
#ifdef HAVE_GDIMAGECREATEFROMFILE
|
250
|
+
rb_define_private_method(cTinyimg, "load_from_file", load_from_file, 1);
|
251
|
+
#endif
|
252
|
+
#ifdef HAVE_GDIMAGEFILE
|
253
|
+
rb_define_private_method(cTinyimg, "save_to_file", save_to_file, 1);
|
254
|
+
#endif
|
255
|
+
}
|
data/lib/tinyimg.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
require_relative '../ext/tinyimg/tinyimg'
|
2
|
+
|
3
|
+
class Tinyimg
|
4
|
+
attr_reader :width, :height
|
5
|
+
|
6
|
+
private_class_method :new
|
7
|
+
|
8
|
+
def self.from_file(filename)
|
9
|
+
new(:filename, filename)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.from_io(io)
|
13
|
+
from_string(io.read)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.from_string(data)
|
17
|
+
new(:string, data)
|
18
|
+
end
|
19
|
+
|
20
|
+
def dimensions
|
21
|
+
[width, height]
|
22
|
+
end
|
23
|
+
|
24
|
+
def resize(width, height)
|
25
|
+
dup.resize!(width, height)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Implemented in C
|
29
|
+
# def resize!(width, height)
|
30
|
+
# end
|
31
|
+
|
32
|
+
def resize_to_fit(new_width, new_height)
|
33
|
+
dup.resize_to_fit!(new_width, new_height)
|
34
|
+
end
|
35
|
+
|
36
|
+
def resize_to_fit!(new_width, new_height)
|
37
|
+
resize_to_fit_or_fill!(new_width, new_height) do |old_ratio, new_ratio|
|
38
|
+
old_ratio > new_ratio
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def resize_to_fill(new_width, new_height)
|
43
|
+
dup.resize_to_fill!(new_width, new_height)
|
44
|
+
end
|
45
|
+
|
46
|
+
def resize_to_fill!(new_width, new_height)
|
47
|
+
resize_to_fit_or_fill!(new_width, new_height) do |old_ratio, new_ratio|
|
48
|
+
old_ratio < new_ratio
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def save(filename)
|
53
|
+
if respond_to?(:save_to_file)
|
54
|
+
save_to_file(filename)
|
55
|
+
else
|
56
|
+
data = case determine_by_extension(filename)
|
57
|
+
when :jpeg then to_jpeg
|
58
|
+
when :png then to_png
|
59
|
+
end
|
60
|
+
|
61
|
+
File.write(filename, data)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Implemented in C
|
66
|
+
# def to_jpeg(quality = DEFAULT_JPEG_QUALITY)
|
67
|
+
# end
|
68
|
+
|
69
|
+
# Implemented in C
|
70
|
+
# def to_png(compression = nil)
|
71
|
+
# end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def initialize(mode, data, type = nil)
|
76
|
+
case mode
|
77
|
+
when :string
|
78
|
+
load_from_string(data, determine_type(data))
|
79
|
+
when :filename
|
80
|
+
if respond_to?(:load_from_file)
|
81
|
+
load_from_file(data)
|
82
|
+
else
|
83
|
+
content = File.read(data)
|
84
|
+
load_from_string(content, determine_type(content))
|
85
|
+
end
|
86
|
+
else
|
87
|
+
raise
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def resize_to_fit_or_fill!(new_width, new_height)
|
92
|
+
old_ratio = width / height.to_f
|
93
|
+
new_ratio = new_width / new_height.to_f
|
94
|
+
|
95
|
+
if yield(old_ratio, new_ratio)
|
96
|
+
resize_width = new_width
|
97
|
+
resize_height = (height * (new_width / width.to_f)).to_i
|
98
|
+
else
|
99
|
+
resize_width = (width * (new_height / height.to_f)).to_i
|
100
|
+
resize_height = new_height
|
101
|
+
end
|
102
|
+
|
103
|
+
resize!(resize_width, resize_height)
|
104
|
+
end
|
105
|
+
|
106
|
+
def determine_type(data)
|
107
|
+
if data[0, 3].unpack("C*") == [255, 216, 255]
|
108
|
+
:jpeg
|
109
|
+
elsif data[0, 8].unpack("C*") == [137, 80, 78, 71, 13, 10, 26, 10]
|
110
|
+
:png
|
111
|
+
else
|
112
|
+
raise ArgumentError, "Only JPEG and PNG files are supported"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def determine_by_extension(filename)
|
117
|
+
case filename.split(".").last.to_s.downcase
|
118
|
+
when 'jpeg', 'jpg' then :jpeg
|
119
|
+
when 'png' then :png
|
120
|
+
else
|
121
|
+
raise ArgumentError, "Cannot determine image type based on the filename"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Implemented in C
|
126
|
+
# def load_from_string(data, type)
|
127
|
+
# end
|
128
|
+
|
129
|
+
# Implemented in C
|
130
|
+
# Only available with libgd 2.1.1+
|
131
|
+
# def load_from_file(filename)
|
132
|
+
# end
|
133
|
+
|
134
|
+
# Implemented in C
|
135
|
+
# Only available with libgd 2.1.1+
|
136
|
+
# def save_to_file(filename)
|
137
|
+
# end
|
138
|
+
end
|
Binary file
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'tinyimg'
|
2
|
+
|
3
|
+
RSpec.describe Tinyimg do
|
4
|
+
let(:samples_directory) { "#{File.dirname(__FILE__)}/samples" }
|
5
|
+
let(:sample_filename) { "#{samples_directory}/duck.png" }
|
6
|
+
let(:sample) { Tinyimg.from_file(sample_filename) }
|
7
|
+
|
8
|
+
let(:tmp_directory) { "#{File.dirname(__FILE__)}/tmp" }
|
9
|
+
let(:tmp_jpg_filename) { "#{tmp_directory}/test.jpg" }
|
10
|
+
let(:tmp_png_filename) { "#{tmp_directory}/test.png" }
|
11
|
+
|
12
|
+
describe "#from_file, #width, #height and #dimensions" do
|
13
|
+
it "loads from a file and returns the width and height of the image" do
|
14
|
+
expect(sample.width).to eq 200
|
15
|
+
expect(sample.height).to eq 153
|
16
|
+
expect(sample.dimensions).to eq [200, 153]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#from_string" do
|
21
|
+
it "loads a PNG" do
|
22
|
+
image = Tinyimg.from_string(IO.read(sample_filename))
|
23
|
+
expect(image.dimensions).to eq [200, 153]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#from_io" do
|
28
|
+
it "loads a PNG" do
|
29
|
+
File.open(sample_filename, "r") do |file|
|
30
|
+
image = Tinyimg.from_io(file)
|
31
|
+
expect(image.dimensions).to eq [200, 153]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#resize" do
|
37
|
+
it "resizes the image as requested, creating a new image" do
|
38
|
+
result = sample.resize(100, 100)
|
39
|
+
expect(result).to_not eql sample
|
40
|
+
expect(sample.dimensions).to eq [200, 153]
|
41
|
+
expect(result.dimensions).to eq [100, 100]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#resize!" do
|
46
|
+
it "resizes the image as requested" do
|
47
|
+
result = sample.resize!(100, 100)
|
48
|
+
expect(result).to eql sample
|
49
|
+
expect(sample.dimensions).to eq [100, 100]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#resize_to_fit!" do
|
54
|
+
it "calculates the image dimensions so it fits the width and resizes" do
|
55
|
+
sample.resize_to_fit!(100, 100)
|
56
|
+
expect(sample.dimensions).to eq [100, 76]
|
57
|
+
end
|
58
|
+
|
59
|
+
it "calculates the image dimensions so it fits the height and resizes" do
|
60
|
+
sample.resize_to_fit!(1000, 100)
|
61
|
+
expect(sample.dimensions).to eq [130, 100]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#resize_to_fill!" do
|
66
|
+
it "calculates the image dimensions so it fills the width and resizes" do
|
67
|
+
sample.resize_to_fill!(1000, 100)
|
68
|
+
expect(sample.dimensions).to eq [1000, 765]
|
69
|
+
end
|
70
|
+
|
71
|
+
it "calculates the image dimensions so it fills the height and resizes" do
|
72
|
+
sample.resize_to_fill!(100, 100)
|
73
|
+
expect(sample.dimensions).to eq [130, 100]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#save" do
|
78
|
+
it "saves a JPEG" do
|
79
|
+
begin
|
80
|
+
sample.save(tmp_jpg_filename)
|
81
|
+
expect(File.read(tmp_jpg_filename, 3).unpack("C*")).to eq [255, 216, 255]
|
82
|
+
|
83
|
+
reloaded = Tinyimg.from_file(tmp_jpg_filename)
|
84
|
+
expect(reloaded.dimensions).to eq [200, 153]
|
85
|
+
ensure
|
86
|
+
File.unlink(tmp_jpg_filename)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
it "saves a PNG" do
|
91
|
+
begin
|
92
|
+
sample.save(tmp_png_filename)
|
93
|
+
expect(File.read(tmp_png_filename, 3).unpack("C*")).to eq [137, 80, 78]
|
94
|
+
|
95
|
+
reloaded = Tinyimg.from_file(tmp_png_filename)
|
96
|
+
expect(reloaded.dimensions).to eq [200, 153]
|
97
|
+
ensure
|
98
|
+
File.unlink(tmp_png_filename)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "#to_jpeg" do
|
104
|
+
it "exports a JPEG" do
|
105
|
+
data = sample.to_jpeg
|
106
|
+
expect(data[0, 3].unpack("C*")).to eq [255, 216, 255]
|
107
|
+
|
108
|
+
reloaded = Tinyimg.from_string(data)
|
109
|
+
expect(reloaded.dimensions).to eq [200, 153]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "#to_png" do
|
114
|
+
it "exports a PNG" do
|
115
|
+
data = sample.to_png
|
116
|
+
expect(data[0, 3].unpack("C*")).to eq [137, 80, 78]
|
117
|
+
|
118
|
+
reloaded = Tinyimg.from_string(data)
|
119
|
+
expect(reloaded.dimensions).to eq [200, 153]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/spec/tmp/.keep
ADDED
File without changes
|
data/tinyimg.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Gem::Specification.new do |gem|
|
2
|
+
gem.name = 'tinyimg'
|
3
|
+
gem.version = '0.1.0'
|
4
|
+
gem.summary = "Tiny and fast JPEG/PNG resizer and converter"
|
5
|
+
gem.description = "Convert between JPEG/PNG and resize images, either all in memory or via disk. Only required libgd to function."
|
6
|
+
gem.has_rdoc = false
|
7
|
+
gem.author = "Roger Nesbitt"
|
8
|
+
gem.email = "roger@seriousorange.com"
|
9
|
+
gem.homepage = "http://github.com/mogest/tinyimg"
|
10
|
+
gem.license = 'MIT'
|
11
|
+
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- spec/*`.split("\n")
|
14
|
+
gem.require_paths = ["lib"]
|
15
|
+
gem.extensions = %w(ext/tinyimg/extconf.rb)
|
16
|
+
|
17
|
+
gem.required_ruby_version = '>= 2.0.0'
|
18
|
+
|
19
|
+
gem.add_development_dependency "rspec", "~> 3.0"
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tinyimg
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Roger Nesbitt
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
description: Convert between JPEG/PNG and resize images, either all in memory or via
|
28
|
+
disk. Only required libgd to function.
|
29
|
+
email: roger@seriousorange.com
|
30
|
+
executables: []
|
31
|
+
extensions:
|
32
|
+
- ext/tinyimg/extconf.rb
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- ".gitignore"
|
36
|
+
- Gemfile
|
37
|
+
- Gemfile.lock
|
38
|
+
- LICENSE
|
39
|
+
- README.md
|
40
|
+
- ext/tinyimg/extconf.rb
|
41
|
+
- ext/tinyimg/tinyimg.c
|
42
|
+
- lib/tinyimg.rb
|
43
|
+
- spec/samples/duck.png
|
44
|
+
- spec/tinyimg_spec.rb
|
45
|
+
- spec/tmp/.keep
|
46
|
+
- tinyimg.gemspec
|
47
|
+
homepage: http://github.com/mogest/tinyimg
|
48
|
+
licenses:
|
49
|
+
- MIT
|
50
|
+
metadata: {}
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 2.0.0
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
requirements: []
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 2.2.2
|
68
|
+
signing_key:
|
69
|
+
specification_version: 4
|
70
|
+
summary: Tiny and fast JPEG/PNG resizer and converter
|
71
|
+
test_files:
|
72
|
+
- spec/samples/duck.png
|
73
|
+
- spec/tinyimg_spec.rb
|
74
|
+
- spec/tmp/.keep
|