tinyimg 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.
- 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
|