thwomp 0.0.6
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.
- data/.gitignore +8 -0
- data/README.rdoc +100 -0
- data/Rakefile +2 -0
- data/bin/install_gnash_osx +30 -0
- data/bin/install_gnash_ubuntu +1 -0
- data/lib/thwomp.rb +10 -0
- data/lib/thwomp/animation_preview.rb +58 -0
- data/lib/thwomp/color_counter.rb +83 -0
- data/lib/thwomp/downloader.rb +35 -0
- data/lib/thwomp/raw_renderer.rb +40 -0
- data/lib/thwomp/renderer.rb +98 -0
- data/lib/thwomp/suitable_thumbnail.rb +52 -0
- data/lib/thwomp/thumbnail.rb +54 -0
- data/lib/thwomp/version.rb +3 -0
- data/spec/animation_preview_spec.rb +12 -0
- data/spec/color_counter_spec.rb +18 -0
- data/spec/fixtures/frames.gif +0 -0
- data/spec/renderer_spec.rb +30 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/suitable_thumbnail_spec.rb +11 -0
- data/spec/thumbnail_spec.rb +31 -0
- data/thwomp.gemspec +25 -0
- metadata +163 -0
data/.gitignore
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
= Thwomp
|
2
|
+
SWF renderer for Ruby
|
3
|
+
|
4
|
+
Note: work in progress!
|
5
|
+
|
6
|
+
== Installiing Thwomp
|
7
|
+
|
8
|
+
Requirements:
|
9
|
+
* sdl
|
10
|
+
* boost
|
11
|
+
* antigrain (libagg)
|
12
|
+
* gnash
|
13
|
+
|
14
|
+
=== OSX lazy installer
|
15
|
+
|
16
|
+
Just run install_gnash_osx
|
17
|
+
|
18
|
+
|
19
|
+
=== OSX
|
20
|
+
|
21
|
+
<b>Make sure homebrew is installed:</b>
|
22
|
+
|
23
|
+
/usr/bin/ruby -e "$(curl -fsSL https://raw.github.com/gist/323731)"
|
24
|
+
|
25
|
+
<b>Update your homebrew:</b>
|
26
|
+
|
27
|
+
brew update
|
28
|
+
|
29
|
+
<b>Install SDL dev. package:</b>
|
30
|
+
|
31
|
+
Be warned, if you are running OSX Lion you'll need to set the default compiler to GCC instead of the GCC LVM compiler.
|
32
|
+
export CC=gcc-4.2
|
33
|
+
|
34
|
+
After that:
|
35
|
+
cd /tmp/
|
36
|
+
wget http://www.libsdl.org/release/SDL-1.2.14.tar.gz
|
37
|
+
tar -xzf SDL-1.2.14.tar.gz
|
38
|
+
cd SDL-1.2.14
|
39
|
+
./configure
|
40
|
+
make
|
41
|
+
make install
|
42
|
+
|
43
|
+
<b>Install boost:</b>
|
44
|
+
|
45
|
+
brew install boost
|
46
|
+
|
47
|
+
<b>Install anti grain:</b>
|
48
|
+
|
49
|
+
brew install libagg
|
50
|
+
|
51
|
+
<b>Install gnash:</b>
|
52
|
+
|
53
|
+
Now this is a tricky one. There is a bug in the configure script of gnash, that won't detect the correct version of ffmpeg. It could be possible that the configure script will crash. The default installation of gnash will fallback to gstreamer.
|
54
|
+
|
55
|
+
Anway... this will install gnash-dump which will allow us to dump RAW files from an input SWF file:
|
56
|
+
|
57
|
+
Option 1: Install from source
|
58
|
+
cd /tmp/
|
59
|
+
git clone git://git.sv.gnu.org/gnash.git
|
60
|
+
cd gnash
|
61
|
+
./autogen
|
62
|
+
./configure --prefix=/usr/local/gnash-dump --enable-renderer=agg --enable-gui=dump --enable-media=no --disable-kparts --disable-nsapi --disable-menus make
|
63
|
+
make
|
64
|
+
sudo make install
|
65
|
+
|
66
|
+
Option 2: Install from tarbal from the intertubes:
|
67
|
+
cd /tmp/
|
68
|
+
wget ftp://ftp.mirror.nl/pub/mirror/gnu/gnash/0.8.9/gnash-0.8.9.tar.gz
|
69
|
+
tar -xzf gnash-0.8.9.tar.gz
|
70
|
+
cd gnash-0.8.9
|
71
|
+
./configure --prefix=/usr/local/gnash-dump --enable-renderer=agg --enable-gui=dump --enable-media=no --disable-kparts --disable-nsapi --disable-menus
|
72
|
+
make
|
73
|
+
sudo make install
|
74
|
+
|
75
|
+
Don't forget to add sudo before gnash.
|
76
|
+
|
77
|
+
=== Ubuntu
|
78
|
+
|
79
|
+
TODO
|
80
|
+
|
81
|
+
== Installing the thwomp gem
|
82
|
+
gem install thwomp
|
83
|
+
|
84
|
+
== Usage
|
85
|
+
|
86
|
+
<b>Retrieiving image from frame:</b>
|
87
|
+
renderer = Thwomp::Renderer.new("http://www.example.com/flash.swf")
|
88
|
+
data = renderer.png_data(2)
|
89
|
+
|
90
|
+
<b>Creating a thumbnail from a frame:</b>
|
91
|
+
renderer = Thwomp::Renderer.new("http://www.example.com/flash.swf")
|
92
|
+
data = Thwomp::Thumbnail.new(renderer).png_data(2)
|
93
|
+
|
94
|
+
<b>Finding the most suitble thumbnail for a swf movie:</b>
|
95
|
+
renderer = Thwomp::Renderer.new("http://www.example.com/flash.swf")
|
96
|
+
data = Thwomp::SuitableThumbnail.new(renderer).png_data
|
97
|
+
|
98
|
+
<b>Creating a GIF animation from a swf movie:</b>
|
99
|
+
renderer = Thwomp::Renderer.new("http://www.example.com/flash.swf")
|
100
|
+
data = Thwomp::AnimationPreview.new(renderer).gif_data
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'highline/import'
|
4
|
+
|
5
|
+
if ask('Do you wish to install homebrew? [y/n]') =~ /^[Yy]$/
|
6
|
+
puts 'Installing homebrew'
|
7
|
+
`/usr/bin/ruby -e "$(curl -fsSL https://raw.github.com/gist/323731)"`
|
8
|
+
end
|
9
|
+
|
10
|
+
if ask('Install required libs with homebrew? [y/n]') =~ /^[Yy]$/
|
11
|
+
puts 'Installing required libs with homebrew'
|
12
|
+
`brew update && brew install giflib boost libagg`
|
13
|
+
end
|
14
|
+
|
15
|
+
if ask('Install gnash? This may take a while! [y/n]') =~ /^[Yy]$/
|
16
|
+
|
17
|
+
puts 'Installing gnash'
|
18
|
+
|
19
|
+
`export CC=gcc-4.2
|
20
|
+
cd /tmp/
|
21
|
+
rm -fr gnash
|
22
|
+
git clone git://git.sv.gnu.org/gnash.git
|
23
|
+
cd gnash
|
24
|
+
./autogen
|
25
|
+
./configure --prefix=/usr/local/gnash-dump --enable-renderer=agg --enable-gui=dump --enable-media=no --disable-kparts --disable-nsapi --disable-menus make
|
26
|
+
make
|
27
|
+
sudo make install`
|
28
|
+
end
|
29
|
+
|
30
|
+
puts 'Done'
|
@@ -0,0 +1 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
data/lib/thwomp.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
|
3
|
+
module Thwomp
|
4
|
+
class AnimationPreview < Struct.new(:renderer)
|
5
|
+
|
6
|
+
attr_accessor :renderer, :max_width, :max_height, :max_frames
|
7
|
+
|
8
|
+
DEFAULT_OPTIONS = { :max_width => 128,
|
9
|
+
:max_height => 128,
|
10
|
+
:max_frames => 10 }
|
11
|
+
|
12
|
+
#
|
13
|
+
# Creates an GIF animation from a given SWF
|
14
|
+
#
|
15
|
+
# Options
|
16
|
+
# :max_width Maximum width of GIF
|
17
|
+
# :max_height Maximum height of GIF
|
18
|
+
#
|
19
|
+
def initialize(renderer, options = {})
|
20
|
+
DEFAULT_OPTIONS.merge(options).each { |k,v| send(:"#{k}=", v) if v }
|
21
|
+
@renderer = renderer
|
22
|
+
end
|
23
|
+
|
24
|
+
# returns the gif binary data of generated animation
|
25
|
+
def gif_data
|
26
|
+
File.open(filename, 'rb') { |f| f.read }
|
27
|
+
end
|
28
|
+
|
29
|
+
# returns temp filename of generated animation
|
30
|
+
def filename
|
31
|
+
@filename ||= generate_gif!
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def thumbnail
|
37
|
+
@thumbnail ||= Thumbnail.new(renderer, :max_width => max_width, :max_height => max_height)
|
38
|
+
end
|
39
|
+
|
40
|
+
# returns all filenames of the seperate frames in the flash animation
|
41
|
+
def frame_filenames
|
42
|
+
unless @frame_filenames
|
43
|
+
frames = (0..max_frames).to_a + ['last']
|
44
|
+
renderer.render_batch!(frames)
|
45
|
+
@frame_filenames = frames.map { |frame| thumbnail.filename(frame) }.compact
|
46
|
+
end
|
47
|
+
|
48
|
+
@frame_filenames
|
49
|
+
end
|
50
|
+
|
51
|
+
def generate_gif!
|
52
|
+
filename = "#{Dir.tmpdir}/animation_#{Time.now.to_i}.gif"
|
53
|
+
`convert #{frame_filenames.join(' ')} #{filename}`
|
54
|
+
filename
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'oily_png'
|
2
|
+
|
3
|
+
module Thwomp
|
4
|
+
# Flattens all colors in given image file and calculates the diversity in used colors
|
5
|
+
# We can asume that if the image contains more than 2 colors, we have an interesting image
|
6
|
+
class ColorCounter < Struct.new(:image_filename)
|
7
|
+
|
8
|
+
FLATTEN_FACTOR = 4.0
|
9
|
+
|
10
|
+
# returns the number of different colors
|
11
|
+
def diversity
|
12
|
+
color_count.keys
|
13
|
+
end
|
14
|
+
|
15
|
+
# tests if the image is empty
|
16
|
+
def empty?
|
17
|
+
diversity.size < 2
|
18
|
+
end
|
19
|
+
|
20
|
+
# tests if the image contains an photo
|
21
|
+
def present?
|
22
|
+
!empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def png
|
28
|
+
@png ||= ChunkyPNG::Image.from_file(image_filename)
|
29
|
+
end
|
30
|
+
|
31
|
+
def width
|
32
|
+
@width ||= png.width
|
33
|
+
end
|
34
|
+
|
35
|
+
def height
|
36
|
+
@height ||= png.height
|
37
|
+
end
|
38
|
+
|
39
|
+
# start counting mofo!
|
40
|
+
def color_count
|
41
|
+
unless @color_count
|
42
|
+
@color_count = {}
|
43
|
+
|
44
|
+
height.times do |y|
|
45
|
+
width.times do |x|
|
46
|
+
@color_count[flattened_color_at(x, y)] ||= 0
|
47
|
+
@color_count[flattened_color_at(x, y)] += 1
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
@color_count
|
53
|
+
end
|
54
|
+
|
55
|
+
# simplifies the given color
|
56
|
+
def flattened_color(color)
|
57
|
+
r = ChunkyPNG::Color.r(color)
|
58
|
+
g = ChunkyPNG::Color.g(color)
|
59
|
+
b = ChunkyPNG::Color.b(color)
|
60
|
+
|
61
|
+
ChunkyPNG::Color.rgb(flatten_color(r), flatten_color(g), flatten_color(b))
|
62
|
+
end
|
63
|
+
|
64
|
+
# flattens a color value
|
65
|
+
def flatten_color(color)
|
66
|
+
((((color.to_f / 255.0) * flattened_max_color.to_i).to_i / flattened_max_color) * 255.0).to_i
|
67
|
+
end
|
68
|
+
|
69
|
+
def flattened_max_color
|
70
|
+
255.0 / FLATTEN_FACTOR
|
71
|
+
end
|
72
|
+
|
73
|
+
# returns the color at pixel X,Y
|
74
|
+
def color_at(x,y)
|
75
|
+
png[y, x]
|
76
|
+
end
|
77
|
+
|
78
|
+
def flattened_color_at(x,y)
|
79
|
+
flattened_color color_at(x,y)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Thwomp
|
2
|
+
# Downloads SWF files
|
3
|
+
class Downloader < Struct.new(:swf_url)
|
4
|
+
|
5
|
+
# tests if the given file remote
|
6
|
+
def remote?
|
7
|
+
swf_url =~ /^http(s)?::/
|
8
|
+
end
|
9
|
+
|
10
|
+
# tests if the given file is local
|
11
|
+
def local?
|
12
|
+
!remote?
|
13
|
+
end
|
14
|
+
|
15
|
+
# returns filename of local/remote file
|
16
|
+
def filename
|
17
|
+
local?? swf_url : tmp_file.path
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def download!
|
23
|
+
@download ||= tmp_file.write(response)
|
24
|
+
end
|
25
|
+
|
26
|
+
def tmp_file
|
27
|
+
@tmp_file ||= Tempfile.new('thwomp')
|
28
|
+
end
|
29
|
+
|
30
|
+
def response
|
31
|
+
@response ||= http.get(swf_url)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
|
3
|
+
# TODO maybe nice to use RAW files instead of screenshot files
|
4
|
+
module Thwomp
|
5
|
+
# Renders SWF files
|
6
|
+
class RawRenderer < Renderer
|
7
|
+
|
8
|
+
attr_accessor :max_advances
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
# calculates offset of given frame
|
13
|
+
def frame_offset(frame)
|
14
|
+
frame_size * frame
|
15
|
+
end
|
16
|
+
|
17
|
+
# calculates size of frame
|
18
|
+
def frame_size
|
19
|
+
max_width * max_height * 4
|
20
|
+
end
|
21
|
+
|
22
|
+
# maximum number of captures frames
|
23
|
+
def max_advances
|
24
|
+
@max_advances ||= 100
|
25
|
+
end
|
26
|
+
|
27
|
+
# extracts one frame from dump and returns the raw data
|
28
|
+
def extract_frame_from_raw(frame)
|
29
|
+
#@render_frames ||= `#{gnash_binary} #{downloader.filename} -D #{raw_filename} --max-advances #{max_advances} 1 -j 500 -k 500`
|
30
|
+
render_frame!(frame)
|
31
|
+
|
32
|
+
File.open('rb', raw_filename) do |file|
|
33
|
+
file.seek(frame_offset(frame))
|
34
|
+
file.read(frame_size)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
|
3
|
+
module Thwomp
|
4
|
+
|
5
|
+
# Renders SWF files
|
6
|
+
class Renderer
|
7
|
+
|
8
|
+
attr_accessor :url, :max_width, :max_height, :max_frames
|
9
|
+
|
10
|
+
DEFAULT_OPTIONS = { :max_width => 500,
|
11
|
+
:max_height => 500,
|
12
|
+
:max_frames => 10 }
|
13
|
+
|
14
|
+
# Initializer the Thwomp renderer
|
15
|
+
#
|
16
|
+
# Options:
|
17
|
+
# :max_width Maximum width of renderer
|
18
|
+
# :max_height Maximum height of renderer
|
19
|
+
# :max_frames Maximum number of frames the renderer can handle
|
20
|
+
def initialize(url, options = {})
|
21
|
+
DEFAULT_OPTIONS.merge(options).each { |k,v| send(:"#{k}=", v) if v }
|
22
|
+
@url = url
|
23
|
+
end
|
24
|
+
|
25
|
+
# returns filename of frame
|
26
|
+
def frame_filename(frame)
|
27
|
+
frame_exists?(frame) ? @frame_filename[frame] : nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# returns PNG data from given frame
|
31
|
+
def png_data(frame)
|
32
|
+
frame_exists?(frame) ? frame_data(frame) : nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# renders a batch of frames
|
36
|
+
def render_batch!(frames)
|
37
|
+
@frame_filename ||= {}
|
38
|
+
|
39
|
+
frame_batch = frames.join(',')
|
40
|
+
file_mask = frame_output_filename('%f')
|
41
|
+
|
42
|
+
`#{self.class.gnash_binary} -s1 --screenshot=#{frame_batch} --screenshot-file=#{file_mask} -1 -r1 --timeout 200 #{filename} -j #{max_width} -k #{max_height}`
|
43
|
+
|
44
|
+
frames.each do |frame|
|
45
|
+
@frame_filename[frame] = file_mask.gsub('%f', frame.to_s)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def frame_exists?(frame)
|
50
|
+
render_frame!(frame)
|
51
|
+
File.exists?(@frame_filename[frame])
|
52
|
+
end
|
53
|
+
|
54
|
+
# returns the current set gnash binary
|
55
|
+
def self.gnash_binary
|
56
|
+
@gnash_binary ||= 'gnash-dump'
|
57
|
+
end
|
58
|
+
|
59
|
+
# sets the path to the gnash binary
|
60
|
+
def self.gnash_binary=(binary)
|
61
|
+
@gnash_binary = binary
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def frame_data(frame)
|
67
|
+
File.open(@frame_filename[frame], 'rb') { |f| f.read }
|
68
|
+
end
|
69
|
+
|
70
|
+
# renders all the frames from the SWF file and dump them to a RAW file
|
71
|
+
def render_frame!(frame)
|
72
|
+
@frame_filename ||= {}
|
73
|
+
|
74
|
+
unless @frame_filename[frame]
|
75
|
+
@frame_filename[frame] = frame_output_filename(frame)
|
76
|
+
|
77
|
+
`#{self.class.gnash_binary} -s1 --screenshot=#{frame} --screenshot-file #{@frame_filename[frame]} -1 -r1 --timeout 200 #{filename} -j #{max_width} -k #{max_height}`
|
78
|
+
end
|
79
|
+
|
80
|
+
@frame_filename[frame]
|
81
|
+
end
|
82
|
+
|
83
|
+
def filename
|
84
|
+
downloader.filename
|
85
|
+
end
|
86
|
+
|
87
|
+
# returns the downloader
|
88
|
+
def downloader
|
89
|
+
@downloader ||= Downloader.new(url)
|
90
|
+
end
|
91
|
+
|
92
|
+
# returns temp filename for given frame no.
|
93
|
+
def frame_output_filename(frame)
|
94
|
+
"#{Dir.tmpdir}/frame_#{Time.now.to_i}_#{frame}.png"
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Thwomp
|
2
|
+
|
3
|
+
# Creates thumbnail of SWF movie and finds the most interesting frame in the movie
|
4
|
+
class SuitableThumbnail < Thumbnail
|
5
|
+
|
6
|
+
# returns the png binary data of the generated thumbnail
|
7
|
+
def png_data
|
8
|
+
File.open(filename, 'rb') { |f| f.read }
|
9
|
+
end
|
10
|
+
|
11
|
+
# returns the temp filename of the generated thumbnail
|
12
|
+
def filename
|
13
|
+
@filename ||= generate_thumbnail!
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# loop till we find the most suitable frame
|
19
|
+
def suitable_frame
|
20
|
+
unless @suitable_frame
|
21
|
+
frames = ['last'] + (0..renderer.max_frames).to_a.reverse
|
22
|
+
|
23
|
+
# render a batch of frames to speed up the process
|
24
|
+
renderer.render_batch!(frames)
|
25
|
+
|
26
|
+
frames.each do |frame|
|
27
|
+
@suitable_frame = frame if !@suitable_frame && suitable_frame?(frame)
|
28
|
+
end
|
29
|
+
|
30
|
+
@suitable_frame ||= 'last'
|
31
|
+
end
|
32
|
+
|
33
|
+
@suitable_frame
|
34
|
+
end
|
35
|
+
|
36
|
+
# tests if the given frame no. is suitable for a thumbnail
|
37
|
+
# tests if the frame doesn't contain a too high concentration of one color
|
38
|
+
def suitable_frame?(frame)
|
39
|
+
renderer.frame_exists?(frame) && ColorCounter.new(renderer.frame_filename(frame)).present?
|
40
|
+
end
|
41
|
+
|
42
|
+
def suitable_frame_filename
|
43
|
+
@suitable_frame_filename ||= renderer.frame_filename(suitable_frame)
|
44
|
+
end
|
45
|
+
|
46
|
+
def generate_thumbnail!
|
47
|
+
super(suitable_frame)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
require 'oily_png'
|
3
|
+
|
4
|
+
module Thwomp
|
5
|
+
|
6
|
+
# Creates thumbnail of SWF movies
|
7
|
+
class Thumbnail
|
8
|
+
|
9
|
+
attr_accessor :renderer, :max_width, :max_height
|
10
|
+
|
11
|
+
DEFAULT_OPTIONS = { :max_width => 128,
|
12
|
+
:max_height => 128 }
|
13
|
+
|
14
|
+
#
|
15
|
+
# Intializes thumbnail renderer
|
16
|
+
#
|
17
|
+
# Options
|
18
|
+
# :max_width Maximum width of thumbnail
|
19
|
+
# :max_height Maximum height of thumbnail
|
20
|
+
#
|
21
|
+
def initialize(renderer, options = {})
|
22
|
+
DEFAULT_OPTIONS.merge(options).each { |k,v| send(:"#{k}=", v) if v }
|
23
|
+
@renderer = renderer
|
24
|
+
end
|
25
|
+
|
26
|
+
# returns the png binary data of the generated thumbnail
|
27
|
+
def png_data(frame)
|
28
|
+
File.open(filename(frame), 'rb') { |f| f.read } if renderer.frame_exists?(frame)
|
29
|
+
end
|
30
|
+
|
31
|
+
# returns the temp filename of the generated thumbnail
|
32
|
+
def filename(frame)
|
33
|
+
@filename ||= {}
|
34
|
+
@filename[frame] ||= generate_thumbnail!(frame) if renderer.frame_exists?(frame)
|
35
|
+
end
|
36
|
+
|
37
|
+
def render_batch!(frames)
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def generate_thumbnail!(frame)
|
44
|
+
filename = "#{Dir.tmpdir}/thumbnail_#{Time.now.to_i}_#{frame}.png"
|
45
|
+
|
46
|
+
thumbnail = ChunkyPNG::Image.from_file(renderer.frame_filename(frame))
|
47
|
+
thumbnail.resample_bilinear!(max_width, max_height)
|
48
|
+
thumbnail.save(filename)
|
49
|
+
|
50
|
+
filename
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Thwomp::AnimationPreview do
|
4
|
+
|
5
|
+
let(:renderer) { Thwomp::Renderer.new("#{File.dirname(__FILE__)}/fixtures/test.swf") }
|
6
|
+
|
7
|
+
it 'generates a gif animation' do
|
8
|
+
animation = Thwomp::AnimationPreview.new(renderer, :max_width => 60, :max_height => 60)
|
9
|
+
animation.gif_data.should == get_contents("#{File.dirname(__FILE__)}/fixtures/frames.gif")
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'thwomp'
|
3
|
+
|
4
|
+
describe Thwomp::ColorCounter do
|
5
|
+
|
6
|
+
it 'sees that the image is significant' do
|
7
|
+
subject = Thwomp::ColorCounter.new("#{File.dirname(__FILE__)}/fixtures/significant.png")
|
8
|
+
subject.empty?.should == false
|
9
|
+
subject.present?.should == true
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'sees that image is insignificant' do
|
13
|
+
subject = Thwomp::ColorCounter.new("#{File.dirname(__FILE__)}/fixtures/insignificant.png")
|
14
|
+
subject.empty?.should == true
|
15
|
+
subject.present?.should == false
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
Binary file
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'thwomp'
|
3
|
+
|
4
|
+
describe Thwomp::Renderer do
|
5
|
+
describe 'extracting frames from flash' do
|
6
|
+
|
7
|
+
subject { Thwomp::Renderer.new("#{File.dirname(__FILE__)}/fixtures/test.swf") }
|
8
|
+
|
9
|
+
it 'extracts frame 1' do
|
10
|
+
subject.png_data(0).should == get_contents("#{File.dirname(__FILE__)}/fixtures/frame0.png")
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'extracts frame 2' do
|
14
|
+
subject.png_data(1).should == get_contents("#{File.dirname(__FILE__)}/fixtures/frame1.png")
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'extracts frame 3' do
|
18
|
+
subject.png_data(2).should == get_contents("#{File.dirname(__FILE__)}/fixtures/frame2.png")
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'extracts frame 4' do
|
22
|
+
subject.png_data(3).should == get_contents("#{File.dirname(__FILE__)}/fixtures/frame3.png")
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'extracts frame 5' do
|
26
|
+
subject.png_data(4).should == get_contents("#{File.dirname(__FILE__)}/fixtures/frame4.png")
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
require 'rubygems'
|
4
|
+
require 'yaml'
|
5
|
+
require 'rspec'
|
6
|
+
require 'thwomp'
|
7
|
+
|
8
|
+
def get_contents(file)
|
9
|
+
File.open(file, 'rb') { |f| f.read }
|
10
|
+
end
|
11
|
+
|
12
|
+
Thwomp::Renderer.gnash_binary = '/usr/local/gnash-dump/bin/dump-gnash'
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'thwomp'
|
3
|
+
|
4
|
+
describe Thwomp::SuitableThumbnail do
|
5
|
+
it 'finds a suitable preview image' do
|
6
|
+
renderer = Thwomp::Renderer.new("#{File.dirname(__FILE__)}/fixtures/test.swf")
|
7
|
+
subject = Thwomp::SuitableThumbnail.new(renderer, :max_width => 128, :max_height => 128)
|
8
|
+
|
9
|
+
subject.png_data.should == get_contents("#{File.dirname(__FILE__)}/fixtures/frame5_thumbnail.png")
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'thwomp'
|
3
|
+
|
4
|
+
describe Thwomp::Thumbnail do
|
5
|
+
describe 'creating thumbnails from frames' do
|
6
|
+
|
7
|
+
let(:renderer) { Thwomp::Renderer.new("#{File.dirname(__FILE__)}/fixtures/test.swf") }
|
8
|
+
subject { Thwomp::Thumbnail.new(renderer, :max_width => 128, :max_height => 128) }
|
9
|
+
|
10
|
+
it 'creates thumbnail from frame 1' do
|
11
|
+
subject.png_data(0).should == get_contents("#{File.dirname(__FILE__)}/fixtures/frame0_thumbnail.png")
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'creates thumbnail from frame 2' do
|
15
|
+
subject.png_data(1).should == get_contents("#{File.dirname(__FILE__)}/fixtures/frame1_thumbnail.png")
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'creates thumbnail from frame 3' do
|
19
|
+
subject.png_data(2).should == get_contents("#{File.dirname(__FILE__)}/fixtures/frame2_thumbnail.png")
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'creates thumbnail from frame 4' do
|
23
|
+
subject.png_data(3).should == get_contents("#{File.dirname(__FILE__)}/fixtures/frame3_thumbnail.png")
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'creates thumbnail from frame 5' do
|
27
|
+
subject.png_data(4).should == get_contents("#{File.dirname(__FILE__)}/fixtures/frame4_thumbnail.png")
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
data/thwomp.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "thwomp/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "thwomp"
|
7
|
+
s.version = Thwomp::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Altovista"]
|
10
|
+
s.email = ["webmaster@altovista.nl"]
|
11
|
+
s.homepage = "http://www.altovista.nl"
|
12
|
+
s.summary = %q{Create thumbnails or gif animations from SWF files}
|
13
|
+
s.description = %q{Creates thumbnails or gif animations using Gnash as SWF renderer}
|
14
|
+
|
15
|
+
s.rubyforge_project = "thwomp"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_development_dependency 'rspec'
|
23
|
+
s.add_dependency 'oily_png'
|
24
|
+
s.add_dependency 'highline'
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: thwomp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 19
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 6
|
10
|
+
version: 0.0.6
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Altovista
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-08-11 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rspec
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: oily_png
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: highline
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :runtime
|
62
|
+
version_requirements: *id003
|
63
|
+
description: Creates thumbnails or gif animations using Gnash as SWF renderer
|
64
|
+
email:
|
65
|
+
- webmaster@altovista.nl
|
66
|
+
executables:
|
67
|
+
- install_gnash_osx
|
68
|
+
- install_gnash_ubuntu
|
69
|
+
extensions: []
|
70
|
+
|
71
|
+
extra_rdoc_files: []
|
72
|
+
|
73
|
+
files:
|
74
|
+
- .gitignore
|
75
|
+
- README.rdoc
|
76
|
+
- Rakefile
|
77
|
+
- bin/install_gnash_osx
|
78
|
+
- bin/install_gnash_ubuntu
|
79
|
+
- lib/thwomp.rb
|
80
|
+
- lib/thwomp/animation_preview.rb
|
81
|
+
- lib/thwomp/color_counter.rb
|
82
|
+
- lib/thwomp/downloader.rb
|
83
|
+
- lib/thwomp/raw_renderer.rb
|
84
|
+
- lib/thwomp/renderer.rb
|
85
|
+
- lib/thwomp/suitable_thumbnail.rb
|
86
|
+
- lib/thwomp/thumbnail.rb
|
87
|
+
- lib/thwomp/version.rb
|
88
|
+
- spec/animation_preview_spec.rb
|
89
|
+
- spec/color_counter_spec.rb
|
90
|
+
- spec/fixtures/frame0.png
|
91
|
+
- spec/fixtures/frame0_thumbnail.png
|
92
|
+
- spec/fixtures/frame1.png
|
93
|
+
- spec/fixtures/frame1_thumbnail.png
|
94
|
+
- spec/fixtures/frame2.png
|
95
|
+
- spec/fixtures/frame2_thumbnail.png
|
96
|
+
- spec/fixtures/frame3.png
|
97
|
+
- spec/fixtures/frame3_thumbnail.png
|
98
|
+
- spec/fixtures/frame4.png
|
99
|
+
- spec/fixtures/frame4_thumbnail.png
|
100
|
+
- spec/fixtures/frame5_thumbnail.png
|
101
|
+
- spec/fixtures/frames.gif
|
102
|
+
- spec/fixtures/insignificant.png
|
103
|
+
- spec/fixtures/significant.png
|
104
|
+
- spec/renderer_spec.rb
|
105
|
+
- spec/spec_helper.rb
|
106
|
+
- spec/suitable_thumbnail_spec.rb
|
107
|
+
- spec/thumbnail_spec.rb
|
108
|
+
- thwomp.gemspec
|
109
|
+
has_rdoc: true
|
110
|
+
homepage: http://www.altovista.nl
|
111
|
+
licenses: []
|
112
|
+
|
113
|
+
post_install_message:
|
114
|
+
rdoc_options: []
|
115
|
+
|
116
|
+
require_paths:
|
117
|
+
- lib
|
118
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
hash: 3
|
124
|
+
segments:
|
125
|
+
- 0
|
126
|
+
version: "0"
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
none: false
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
hash: 3
|
133
|
+
segments:
|
134
|
+
- 0
|
135
|
+
version: "0"
|
136
|
+
requirements: []
|
137
|
+
|
138
|
+
rubyforge_project: thwomp
|
139
|
+
rubygems_version: 1.6.2
|
140
|
+
signing_key:
|
141
|
+
specification_version: 3
|
142
|
+
summary: Create thumbnails or gif animations from SWF files
|
143
|
+
test_files:
|
144
|
+
- spec/animation_preview_spec.rb
|
145
|
+
- spec/color_counter_spec.rb
|
146
|
+
- spec/fixtures/frame0.png
|
147
|
+
- spec/fixtures/frame0_thumbnail.png
|
148
|
+
- spec/fixtures/frame1.png
|
149
|
+
- spec/fixtures/frame1_thumbnail.png
|
150
|
+
- spec/fixtures/frame2.png
|
151
|
+
- spec/fixtures/frame2_thumbnail.png
|
152
|
+
- spec/fixtures/frame3.png
|
153
|
+
- spec/fixtures/frame3_thumbnail.png
|
154
|
+
- spec/fixtures/frame4.png
|
155
|
+
- spec/fixtures/frame4_thumbnail.png
|
156
|
+
- spec/fixtures/frame5_thumbnail.png
|
157
|
+
- spec/fixtures/frames.gif
|
158
|
+
- spec/fixtures/insignificant.png
|
159
|
+
- spec/fixtures/significant.png
|
160
|
+
- spec/renderer_spec.rb
|
161
|
+
- spec/spec_helper.rb
|
162
|
+
- spec/suitable_thumbnail_spec.rb
|
163
|
+
- spec/thumbnail_spec.rb
|