sdsykes-fastimage 0.1.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,27 +0,0 @@
1
- FastImage Resize is an extremely light solution for resizing images in ruby by using libgd
2
-
3
- === Examples
4
-
5
- require 'fastimage_resize'
6
-
7
- FastImage.resize("http://stephensykes.com/images/ss.com_x.gif", "my.gif", 100, 20)
8
- => 1
9
-
10
- === Requirements
11
-
12
- RubyInline
13
-
14
- sudo gem install RubyInline
15
-
16
- FastImage
17
-
18
- sudo gem install sdsykes-fastimage -s http://gems.github.com
19
-
20
- Libgd
21
-
22
- See http://www.libgd.org/
23
- Libgd is commonly available on most unix platforms, including OSX.
24
-
25
- === References
26
-
27
- * http://blog.new-bamboo.co.uk/2007/12/3/super-f-simple-resizing
data/README.textile CHANGED
@@ -1,42 +1,38 @@
1
- h1. FastImage Resize
1
+ h1. FastImage
2
2
 
3
- h4. FastImage Resize is an extremely light solution for resizing images in ruby by using libgd
3
+ h4. FastImage finds the size or type of an image given its uri by fetching as little as needed
4
4
 
5
- h2. Examples
5
+ h2. The problem
6
6
 
7
- <pre>
8
- <code>
9
- require 'fastimage_resize'
7
+ Your app needs to find the size or type of an image. But the image is not locally stored - it's on another server, or in the cloud - at Amazon S3 for example.
10
8
 
11
- FastImage.resize("http://stephensykes.com/images/ss.com_x.gif", "my.gif", 100, 20)
12
- => 1
13
- </pre>
14
- </code>
9
+ You don't want to download the entire image, which could be many tens of kilobytes, or even megabytes just to get this information. For most image types, the size of the image is simply stored at the start of the file. For JPEG files it's a little bit more complex, but even so you do not need to fetch most of the image to find the size.
15
10
 
16
- h2. Requirements
11
+ FastImage does this minimal fetch for image types GIF, JPEG, PNG and BMP.
17
12
 
18
- * RubyInline
13
+ You only need supply the uri, and FastImage will do the rest.
19
14
 
20
- <pre>
21
- <code>
22
- sudo gem install RubyInline
23
- </pre>
24
- </code>
25
-
26
- * FastImage
15
+ h2. Examples
27
16
 
28
17
  <pre>
29
18
  <code>
30
- sudo gem install sdsykes-fastimage -s http://gems.github.com
31
- </pre>
19
+ require 'fastimage'
20
+
21
+ FastImage.size("http://stephensykes.com/images/ss.com_x.gif")
22
+ => [266, 56] # width, height
23
+ FastImage.type("http://stephensykes.com/images/pngimage")
24
+ => :png
32
25
  </code>
26
+ </pre>
33
27
 
34
- * Libgd
28
+ h2. Installation
35
29
 
36
- See "http://www.libgd.org/":http://www.libgd.org/
30
+ h4. Gem
31
+
32
+ sudo gem install sdsykes-fastimage -s http://gems.github.com
37
33
 
38
- Libgd is commonly available on most unix platforms, including OSX.
34
+ h2. Documentation
39
35
 
40
- h2. References
36
+ "http://rdoc.info/projects/sdsykes/fastimage":http://rdoc.info/projects/sdsykes/fastimage
41
37
 
42
- * "http://blog.new-bamboo.co.uk/2007/12/3/super-f-simple-resizing":http://blog.new-bamboo.co.uk/2007/12/3/super-f-simple-resizing
38
+ (c) 2009 Stephen Sykes (sdsykes)
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 0
3
- :major: 0
4
- :minor: 1
2
+ :patch: 1
3
+ :major: 1
4
+ :minor: 0
data/lib/fastimage.rb ADDED
@@ -0,0 +1,252 @@
1
+ # FastImage finds the size or type of an image given its uri.
2
+ # It is careful to only fetch and parse as much of the image as is needed to determine the result.
3
+ # It does this by using a feature of Net::HTTP that yields strings from the resource being fetched
4
+ # as soon as the packets arrive.
5
+ #
6
+ # No external libraries such as ImageMagick are used here, this is a very lightweight solution to
7
+ # finding image information.
8
+ #
9
+ # FastImage knows about GIF, JPEG, BMP and PNG files.
10
+ #
11
+ # === Examples
12
+ # require 'fastimage'
13
+ #
14
+ # FastImage.size("http://stephensykes.com/images/ss.com_x.gif")
15
+ # => [266, 56]
16
+ # FastImage.type("http://stephensykes.com/images/pngimage")
17
+ # => :png
18
+ #
19
+ # === References
20
+ # * http://snippets.dzone.com/posts/show/805
21
+ # * http://www.anttikupila.com/flash/getting-jpg-dimensions-with-as3-without-loading-the-entire-file/
22
+ # * http://pennysmalls.com/2008/08/19/find-jpeg-dimensions-fast-in-ruby/
23
+ #
24
+ require 'net/https'
25
+
26
+ class FastImage
27
+ attr_reader :size, :type
28
+
29
+ class FastImageException < StandardError # :nodoc:
30
+ end
31
+ class MoreCharsNeeded < FastImageException # :nodoc:
32
+ end
33
+ class UnknownImageType < FastImageException # :nodoc:
34
+ end
35
+ class ImageFetchFailure < FastImageException # :nodoc:
36
+ end
37
+ class SizeNotFound < FastImageException # :nodoc:
38
+ end
39
+
40
+ DefaultTimeout = 2
41
+
42
+ # Returns an array containing the width and height of the image.
43
+ # It will return nil if the image could not be fetched, or if the image type was not recognised.
44
+ #
45
+ # By default there is a timeout of 2 seconds for opening and reading from the remote server.
46
+ # This can be changed by passing a :timeout => number_of_seconds in the options.
47
+ #
48
+ # If you wish FastImage to raise if it cannot size the image for any reason, then pass
49
+ # :raise_on_failure => true in the options.
50
+ #
51
+ # FastImage knows about GIF, JPEG, BMP and PNG files.
52
+ #
53
+ # === Example
54
+ #
55
+ # require 'fastimage'
56
+ #
57
+ # FastImage.size("http://stephensykes.com/images/ss.com_x.gif")
58
+ # => [266, 56]
59
+ # FastImage.type("http://stephensykes.com/images/pngimage")
60
+ # => [16, 16]
61
+ # FastImage.size("http://farm4.static.flickr.com/3023/3047236863_9dce98b836.jpg")
62
+ # => [500, 375]
63
+ # FastImage.size("http://www-ece.rice.edu/~wakin/images/lena512.bmp")
64
+ # => [512, 512]
65
+ # FastImage.size("http://pennysmalls.com/does_not_exist")
66
+ # => nil
67
+ # FastImage.size("http://pennysmalls.com/does_not_exist", :raise_on_failure=>true)
68
+ # => raises FastImage::ImageFetchFailure
69
+ # FastImage.size("http://stephensykes.com/favicon.ico", :raise_on_failure=>true)
70
+ # => raises FastImage::UnknownImageType
71
+ # FastImage.size("http://stephensykes.com/favicon.ico", :raise_on_failure=>true, :timeout=>0.01)
72
+ # => raises FastImage::ImageFetchFailure
73
+ # FastImage.size("http://stephensykes.com/images/faulty.jpg", :raise_on_failure=>true)
74
+ # => raises FastImage::SizeNotFound
75
+ #
76
+ # === Supported options
77
+ # [:timeout]
78
+ # Overrides the default timeout of 2 seconds. Applies both to reading from and opening the http connection.
79
+ # [:raise_on_failure]
80
+ # If set to true causes an exception to be raised if the image size cannot be found for any reason.
81
+ #
82
+ def self.size(uri, options={})
83
+ new(uri, options).size
84
+ end
85
+
86
+ # Returns an symbol indicating the image type fetched from a uri.
87
+ # It will return nil if the image could not be fetched, or if the image type was not recognised.
88
+ #
89
+ # By default there is a timeout of 2 seconds for opening and reading from the remote server.
90
+ # This can be changed by passing a :timeout => number_of_seconds in the options.
91
+ #
92
+ # If you wish FastImage to raise if it cannot find the type of the image for any reason, then pass
93
+ # :raise_on_failure => true in the options.
94
+ #
95
+ # === Example
96
+ #
97
+ # require 'fastimage'
98
+ #
99
+ # FastImage.type("http://stephensykes.com/images/ss.com_x.gif")
100
+ # => :gif
101
+ # FastImage.type("http://stephensykes.com/images/pngimage")
102
+ # => :png
103
+ # FastImage.type("http://farm4.static.flickr.com/3023/3047236863_9dce98b836.jpg")
104
+ # => :jpg
105
+ # FastImage.type("http://www-ece.rice.edu/~wakin/images/lena512.bmp")
106
+ # => :bmp
107
+ # FastImage.type("http://pennysmalls.com/does_not_exist")
108
+ # => nil
109
+ #
110
+ # === Supported options
111
+ # [:timeout]
112
+ # Overrides the default timeout of 2 seconds. Applies both to reading from and opening the http connection.
113
+ # [:raise_on_failure]
114
+ # If set to true causes an exception to be raised if the image type cannot be found for any reason.
115
+ #
116
+ def self.type(uri, options={})
117
+ new(uri, options.merge(:type_only=>true)).type
118
+ end
119
+
120
+ def initialize(uri, options={})
121
+ @type_only = options[:type_only]
122
+ setup_http(uri, options)
123
+ @http.request_get(@http_get_path) do |res|
124
+ raise ImageFetchFailure unless res.is_a?(Net::HTTPSuccess)
125
+ fetch_size_from_response(res)
126
+ end
127
+ raise SizeNotFound if options[:raise_on_failure] && !@size
128
+ rescue Timeout::Error, SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ECONNRESET, ImageFetchFailure
129
+ raise ImageFetchFailure if options[:raise_on_failure]
130
+ rescue UnknownImageType
131
+ raise UnknownImageType if options[:raise_on_failure]
132
+ end
133
+
134
+ private
135
+
136
+ def setup_http(uri, options)
137
+ u = URI.parse(uri)
138
+ @http = Net::HTTP.new(u.host, u.port)
139
+ @http.use_ssl = (u.scheme == "https")
140
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
141
+ @http.open_timeout = options[:timeout] || DefaultTimeout
142
+ @http.read_timeout = options[:timeout] || DefaultTimeout
143
+ @http_get_path = u.request_uri
144
+ end
145
+
146
+ def fetch_type_from_response(res)
147
+ fetch_from_response(res, :type){parse_type}
148
+ end
149
+
150
+ def fetch_size_from_response(res)
151
+ fetch_from_response(res, :size){parse_size}
152
+ end
153
+
154
+ def fetch_from_response(res, item)
155
+ @unused_str = ""
156
+ res.read_body do |str|
157
+ @str = @unused_str + str
158
+ @strpos = 0
159
+ begin
160
+ result = yield
161
+ if result
162
+ instance_variable_set("@#{item}", result)
163
+ break
164
+ end
165
+ rescue MoreCharsNeeded
166
+ end
167
+ end
168
+ end
169
+
170
+ def parse_size
171
+ @type = parse_type unless @type
172
+ send("parse_size_for_#{@type}")
173
+ end
174
+
175
+ def get_chars(n)
176
+ if @strpos + n - 1 >= @str.size
177
+ @unused_str = @str[@strpos..-1]
178
+ raise MoreCharsNeeded
179
+ else
180
+ result = @str[@strpos..(@strpos + n - 1)]
181
+ @strpos += n
182
+ result
183
+ end
184
+ end
185
+
186
+ def get_byte
187
+ get_chars(1).unpack("C")[0]
188
+ end
189
+
190
+ def read_int(str)
191
+ size_bytes = str.unpack("CC")
192
+ (size_bytes[0] << 8) + size_bytes[1]
193
+ end
194
+
195
+ def parse_type
196
+ case get_chars(2)
197
+ when "BM"
198
+ :bmp
199
+ when "GI"
200
+ :gif
201
+ when 0xff.chr + 0xd8.chr
202
+ :jpg
203
+ when 0x89.chr + "P"
204
+ :png
205
+ else
206
+ raise UnknownImageType
207
+ end
208
+ end
209
+
210
+ def parse_size_for_gif
211
+ get_chars(9)[4..8].unpack('SS')
212
+ end
213
+
214
+ def parse_size_for_png
215
+ get_chars(23)[14..22].unpack('NN')
216
+ end
217
+
218
+ def parse_size_for_jpg
219
+ loop do
220
+ @state = case @state
221
+ when nil
222
+ get_chars(2)
223
+ :started
224
+ when :started
225
+ get_byte == 0xFF ? :sof : :started
226
+ when :sof
227
+ c = get_byte
228
+ if (0xe0..0xef).include?(c)
229
+ :skipframe
230
+ elsif [0xC0..0xC3, 0xC5..0xC7, 0xC9..0xCB, 0xCD..0xCF].detect {|r| r.include? c}
231
+ :readsize
232
+ else
233
+ :skipframe
234
+ end
235
+ when :skipframe
236
+ @skip_chars = read_int(get_chars(2)) - 2
237
+ :do_skip
238
+ when :do_skip
239
+ get_chars(@skip_chars)
240
+ :started
241
+ when :readsize
242
+ s = get_chars(7)
243
+ return [read_int(s[5..6]), read_int(s[3..4])]
244
+ end
245
+ end
246
+ end
247
+
248
+ def parse_size_for_bmp
249
+ d = get_chars(27)[12..26]
250
+ d[0] == 40 ? d[4..-1].unpack('LL') : d[4..8].unpack('SS')
251
+ end
252
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sdsykes-fastimage
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Sykes
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-07-08 00:00:00 -07:00
12
+ date: 2009-06-11 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -27,8 +27,7 @@ files:
27
27
  - README
28
28
  - README.textile
29
29
  - VERSION.yml
30
- - lib/fastimage_resize.rb
31
- - test/test.rb
30
+ - lib/fastimage.rb
32
31
  has_rdoc: true
33
32
  homepage: http://github.com/sdsykes/fastimage
34
33
  post_install_message:
@@ -1,137 +0,0 @@
1
- # FastImage Resize is an extremely light solution for resizing images in ruby by using libgd
2
- #
3
- # === Examples
4
- #
5
- # require 'fastimage_resize'
6
- #
7
- # FastImage.resize("http://stephensykes.com/images/ss.com_x.gif", "my.gif", 100, 20)
8
- # => 1
9
- #
10
- # === Requirements
11
- #
12
- # RubyInline
13
- #
14
- # sudo gem install RubyInline
15
- #
16
- # FastImage
17
- #
18
- # sudo gem install sdsykes-fastimage -s http://gems.github.com
19
- #
20
- # Libgd
21
- #
22
- # See http://www.libgd.org/
23
- # Libgd is commonly available on most unix platforms, including OSX.
24
- #
25
- # === References
26
- #
27
- # * http://blog.new-bamboo.co.uk/2007/12/3/super-f-simple-resizing
28
-
29
- require 'inline'
30
- require 'open-uri'
31
- require 'tempfile'
32
- require 'fastimage'
33
-
34
- class FastImage
35
- SUPPORTED_FORMATS = [:jpg, :png, :gif]
36
-
37
- class FormatNotSupported < FastImageException # :nodoc:
38
- end
39
-
40
- def self.resize(uri_in, file_out, w, h, options={})
41
- jpeg_quality = options[:jpeg_quality] || -1
42
-
43
- u = URI.parse(uri_in)
44
- if u.scheme == "http" || u.scheme == "https" || u.scheme == "ftp"
45
- f = Tempfile.new(name)
46
- f.write(open(u).read)
47
- f.close
48
- resize_local(f.path, file_out, w, h, jpeg_quality)
49
- File.unlink(f.path)
50
- else
51
- resize_local(uri_in, file_out, w, h, jpeg_quality)
52
- end
53
- rescue OpenURI::HTTPError, SocketError
54
- raise ImageFetchFailure
55
- end
56
-
57
- def self.resize_local(file_in, file_out, w, h, jpeg_quality)
58
- fi = new(file_in, :raise_on_failure=>true, :type_only=>true)
59
- type_index = SUPPORTED_FORMATS.index(fi.type)
60
- raise FormatNotSupported unless type_index
61
- fi.resize_image(file_in, file_out, w, h, type_index, jpeg_quality)
62
- end
63
-
64
- def resize_image(filename_in, filename_out, w, h, image_type, jpeg_quality); end
65
-
66
- inline do |builder|
67
- builder.include '"gd.h"'
68
- builder.add_link_flags "-lgd"
69
-
70
- builder.c <<-"END"
71
- void resize_image(char *filename_in, char *filename_out, int w, int h, int image_type, int jpeg_quality) {
72
- gdImagePtr im_in, im_out;
73
- FILE *in, *out;
74
- int trans, trans_r, trans_g, trans_b;
75
- int x, y, f = 0;
76
-
77
- in = fopen(filename_in, "rb");
78
- if (!in) return;
79
-
80
- im_out = gdImageCreateTrueColor(w, h); /* must be truecolor */
81
- switch(image_type) {
82
- case 0: im_in = gdImageCreateFromJpeg(in);
83
- break;
84
- case 1: im_in = gdImageCreateFromPng(in);
85
- gdImageAlphaBlending(im_out, 0); /* handle transparency correctly */
86
- gdImageSaveAlpha(im_out, 1);
87
- break;
88
- case 2: im_in = gdImageCreateFromGif(in);
89
- trans = gdImageGetTransparent(im_in);
90
- /* find a transparent pixel, then turn off transparency
91
- so that it copies correctly */
92
- if (trans >= 0) {
93
- for (x=0; x<gdImageSX(im_in); x++) {
94
- for (y=0; y<gdImageSY(im_in); y++) {
95
- if (gdImageGetPixel(im_in, x, y) == trans) {
96
- f = 1;
97
- break;
98
- }
99
- }
100
- if (f) break;
101
- }
102
- gdImageColorTransparent(im_in, -1);
103
- if (!f) trans = -1; /* no transparent pixel found */
104
- }
105
- break;
106
- }
107
-
108
- fclose(in);
109
-
110
- /* Now copy the original */
111
- gdImageCopyResampled(im_out, im_in, 0, 0, 0, 0,
112
- gdImageSX(im_out), gdImageSY(im_out),
113
- gdImageSX(im_in), gdImageSY(im_in));
114
-
115
- out = fopen(filename_out, "wb");
116
- if (out) {
117
- switch(image_type) {
118
- case 0: gdImageJpeg(im_out, out, jpeg_quality);
119
- break;
120
- case 1: gdImagePng(im_out, out);
121
- break;
122
- case 2: gdImageTrueColorToPalette(im_out, 0, 256);
123
- if (trans >= 0) {
124
- trans = gdImageGetPixel(im_out, x, y); /* get the color index of our transparent pixel */
125
- gdImageColorTransparent(im_out, trans); /* may not always work as hoped */
126
- }
127
- gdImageGif(im_out, out);
128
- break;
129
- }
130
- fclose(out);
131
- }
132
- gdImageDestroy(im_in);
133
- gdImageDestroy(im_out);
134
- }
135
- END
136
- end
137
- end
data/test/test.rb DELETED
@@ -1 +0,0 @@
1
- # todo