miloops-attachment_fu 3.2.5
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/CHANGELOG +63 -0
- data/LICENSE +20 -0
- data/README +253 -0
- data/Rakefile +22 -0
- data/amazon_s3.yml.tpl +17 -0
- data/install.rb +7 -0
- data/lib/geometry.rb +96 -0
- data/lib/pothoven-attachment_fu.rb +23 -0
- data/lib/technoweenie/attachment_fu.rb +578 -0
- data/lib/technoweenie/attachment_fu/backends/cloud_file_backend.rb +211 -0
- data/lib/technoweenie/attachment_fu/backends/db_file_backend.rb +39 -0
- data/lib/technoweenie/attachment_fu/backends/file_system_backend.rb +126 -0
- data/lib/technoweenie/attachment_fu/backends/s3_backend.rb +394 -0
- data/lib/technoweenie/attachment_fu/processors/core_image_processor.rb +66 -0
- data/lib/technoweenie/attachment_fu/processors/gd2_processor.rb +59 -0
- data/lib/technoweenie/attachment_fu/processors/image_science_processor.rb +80 -0
- data/lib/technoweenie/attachment_fu/processors/mini_magick_processor.rb +142 -0
- data/lib/technoweenie/attachment_fu/processors/rmagick_processor.rb +66 -0
- data/rackspace_cloudfiles.yml.tpl +14 -0
- data/test/backends/db_file_test.rb +16 -0
- data/test/backends/file_system_test.rb +143 -0
- data/test/backends/remote/cloudfiles_test.rb +102 -0
- data/test/backends/remote/s3_test.rb +119 -0
- data/test/base_attachment_tests.rb +77 -0
- data/test/basic_test.rb +120 -0
- data/test/database.yml +18 -0
- data/test/extra_attachment_test.rb +67 -0
- data/test/fixtures/attachment.rb +304 -0
- data/test/fixtures/files/fake/rails.png +0 -0
- data/test/fixtures/files/foo.txt +1 -0
- data/test/fixtures/files/rails.jpg +0 -0
- data/test/fixtures/files/rails.png +0 -0
- data/test/geometry_test.rb +114 -0
- data/test/processors/core_image_test.rb +58 -0
- data/test/processors/gd2_test.rb +51 -0
- data/test/processors/image_science_test.rb +54 -0
- data/test/processors/mini_magick_test.rb +122 -0
- data/test/processors/rmagick_test.rb +272 -0
- data/test/schema.rb +136 -0
- data/test/test_helper.rb +180 -0
- data/test/validation_test.rb +55 -0
- data/vendor/red_artisan/core_image/filters/color.rb +27 -0
- data/vendor/red_artisan/core_image/filters/effects.rb +31 -0
- data/vendor/red_artisan/core_image/filters/perspective.rb +25 -0
- data/vendor/red_artisan/core_image/filters/quality.rb +25 -0
- data/vendor/red_artisan/core_image/filters/scale.rb +47 -0
- data/vendor/red_artisan/core_image/filters/watermark.rb +32 -0
- data/vendor/red_artisan/core_image/processor.rb +123 -0
- metadata +98 -0
data/test/schema.rb
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 0) do
|
2
|
+
create_table :attachments, :force => true do |t|
|
3
|
+
t.column :db_file_id, :integer
|
4
|
+
t.column :parent_id, :integer
|
5
|
+
t.column :imageable_id, :integer
|
6
|
+
t.column :imageable_type, :string, :limit => 255
|
7
|
+
t.column :thumbnail, :string
|
8
|
+
t.column :filename, :string, :limit => 255
|
9
|
+
t.column :content_type, :string, :limit => 255
|
10
|
+
t.column :size, :integer
|
11
|
+
t.column :width, :integer
|
12
|
+
t.column :height, :integer
|
13
|
+
t.column :aspect_ratio, :float
|
14
|
+
end
|
15
|
+
|
16
|
+
create_table :file_attachments, :force => true do |t|
|
17
|
+
t.column :parent_id, :integer
|
18
|
+
t.column :thumbnail, :string
|
19
|
+
t.column :filename, :string, :limit => 255
|
20
|
+
t.column :content_type, :string, :limit => 255
|
21
|
+
t.column :size, :integer
|
22
|
+
t.column :width, :integer
|
23
|
+
t.column :height, :integer
|
24
|
+
t.column :type, :string
|
25
|
+
t.column :aspect_ratio, :float
|
26
|
+
end
|
27
|
+
|
28
|
+
create_table :file_attachments_with_string_id, :id => false, :force => true do |t|
|
29
|
+
t.column :id, :string
|
30
|
+
t.column :parent_id, :string
|
31
|
+
t.column :thumbnail, :string
|
32
|
+
t.column :filename, :string, :limit => 255
|
33
|
+
t.column :content_type, :string, :limit => 255
|
34
|
+
t.column :size, :integer
|
35
|
+
t.column :width, :integer
|
36
|
+
t.column :height, :integer
|
37
|
+
t.column :type, :string
|
38
|
+
t.column :aspect_ratio, :float
|
39
|
+
end
|
40
|
+
|
41
|
+
create_table :gd2_attachments, :force => true do |t|
|
42
|
+
t.column :parent_id, :integer
|
43
|
+
t.column :thumbnail, :string
|
44
|
+
t.column :filename, :string, :limit => 255
|
45
|
+
t.column :content_type, :string, :limit => 255
|
46
|
+
t.column :size, :integer
|
47
|
+
t.column :width, :integer
|
48
|
+
t.column :height, :integer
|
49
|
+
t.column :type, :string
|
50
|
+
end
|
51
|
+
|
52
|
+
create_table :image_science_attachments, :force => true do |t|
|
53
|
+
t.column :parent_id, :integer
|
54
|
+
t.column :thumbnail, :string
|
55
|
+
t.column :filename, :string, :limit => 255
|
56
|
+
t.column :content_type, :string, :limit => 255
|
57
|
+
t.column :size, :integer
|
58
|
+
t.column :width, :integer
|
59
|
+
t.column :height, :integer
|
60
|
+
t.column :type, :string
|
61
|
+
end
|
62
|
+
|
63
|
+
create_table :core_image_attachments, :force => true do |t|
|
64
|
+
t.column :parent_id, :integer
|
65
|
+
t.column :thumbnail, :string
|
66
|
+
t.column :filename, :string, :limit => 255
|
67
|
+
t.column :content_type, :string, :limit => 255
|
68
|
+
t.column :size, :integer
|
69
|
+
t.column :width, :integer
|
70
|
+
t.column :height, :integer
|
71
|
+
t.column :type, :string
|
72
|
+
end
|
73
|
+
|
74
|
+
create_table :mini_magick_attachments, :force => true do |t|
|
75
|
+
t.column :parent_id, :integer
|
76
|
+
t.column :thumbnail, :string
|
77
|
+
t.column :filename, :string, :limit => 255
|
78
|
+
t.column :content_type, :string, :limit => 255
|
79
|
+
t.column :size, :integer
|
80
|
+
t.column :width, :integer
|
81
|
+
t.column :height, :integer
|
82
|
+
t.column :type, :string
|
83
|
+
end
|
84
|
+
|
85
|
+
create_table :mini_magick_attachments, :force => true do |t|
|
86
|
+
t.column :parent_id, :integer
|
87
|
+
t.column :thumbnail, :string
|
88
|
+
t.column :filename, :string, :limit => 255
|
89
|
+
t.column :content_type, :string, :limit => 255
|
90
|
+
t.column :size, :integer
|
91
|
+
t.column :width, :integer
|
92
|
+
t.column :height, :integer
|
93
|
+
t.column :type, :string
|
94
|
+
end
|
95
|
+
|
96
|
+
create_table :orphan_attachments, :force => true do |t|
|
97
|
+
t.column :db_file_id, :integer
|
98
|
+
t.column :filename, :string, :limit => 255
|
99
|
+
t.column :content_type, :string, :limit => 255
|
100
|
+
t.column :size, :integer
|
101
|
+
end
|
102
|
+
|
103
|
+
create_table :minimal_attachments, :force => true do |t|
|
104
|
+
t.column :size, :integer
|
105
|
+
t.column :content_type, :string, :limit => 255
|
106
|
+
end
|
107
|
+
|
108
|
+
create_table :db_files, :force => true do |t|
|
109
|
+
t.column :data, :binary
|
110
|
+
end
|
111
|
+
|
112
|
+
create_table :s3_attachments, :force => true do |t|
|
113
|
+
t.column :parent_id, :integer
|
114
|
+
t.column :thumbnail, :string
|
115
|
+
t.column :filename, :string, :limit => 255
|
116
|
+
t.column :content_type, :string, :limit => 255
|
117
|
+
t.column :size, :integer
|
118
|
+
t.column :width, :integer
|
119
|
+
t.column :height, :integer
|
120
|
+
t.column :type, :string
|
121
|
+
t.column :aspect_ratio, :float
|
122
|
+
end
|
123
|
+
|
124
|
+
create_table :cloud_files_attachments, :force => true do |t|
|
125
|
+
t.column :parent_id, :integer
|
126
|
+
t.column :thumbnail, :string
|
127
|
+
t.column :filename, :string, :limit => 255
|
128
|
+
t.column :content_type, :string, :limit => 255
|
129
|
+
t.column :size, :integer
|
130
|
+
t.column :width, :integer
|
131
|
+
t.column :height, :integer
|
132
|
+
t.column :type, :string
|
133
|
+
t.column :aspect_ratio, :float
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
|
2
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
3
|
+
|
4
|
+
ENV['RAILS_ENV'] = 'test'
|
5
|
+
ENV['RAILS_ROOT'] ||= File.dirname(__FILE__) + '/../../../..'
|
6
|
+
|
7
|
+
require 'test/unit'
|
8
|
+
require File.expand_path(File.join(ENV['RAILS_ROOT'], 'config/environment.rb'))
|
9
|
+
|
10
|
+
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
11
|
+
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
|
12
|
+
|
13
|
+
db_adapter = ENV['DB']
|
14
|
+
|
15
|
+
# no db passed, try one of these fine config-free DBs before bombing.
|
16
|
+
db_adapter ||=
|
17
|
+
begin
|
18
|
+
require 'rubygems'
|
19
|
+
require 'sqlite'
|
20
|
+
'sqlite'
|
21
|
+
rescue MissingSourceFile
|
22
|
+
begin
|
23
|
+
require 'sqlite3'
|
24
|
+
'sqlite3'
|
25
|
+
rescue MissingSourceFile
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
if db_adapter.nil?
|
30
|
+
raise "No DB Adapter selected. Pass the DB= option to pick one, or install Sqlite or Sqlite3."
|
31
|
+
end
|
32
|
+
|
33
|
+
ActiveRecord::Base.establish_connection(config[db_adapter])
|
34
|
+
|
35
|
+
load(File.dirname(__FILE__) + "/schema.rb")
|
36
|
+
|
37
|
+
FIXTURE_PATH = File.dirname(__FILE__) + "/fixtures"
|
38
|
+
$LOAD_PATH.unshift(FIXTURE_PATH)
|
39
|
+
|
40
|
+
class Test::Unit::TestCase #:nodoc:
|
41
|
+
include ActionDispatch::TestProcess
|
42
|
+
def create_fixtures(*table_names)
|
43
|
+
if block_given?
|
44
|
+
Fixtures.create_fixtures(FIXTURE_PATH, table_names) { yield }
|
45
|
+
else
|
46
|
+
Fixtures.create_fixtures(FIXTURE_PATH, table_names)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def setup
|
51
|
+
Attachment.saves = 0
|
52
|
+
DbFile.transaction { [Attachment, FileAttachment, OrphanAttachment, MinimalAttachment, DbFile].each { |klass| klass.delete_all } }
|
53
|
+
attachment_model self.class.attachment_model
|
54
|
+
end
|
55
|
+
|
56
|
+
def teardown
|
57
|
+
FileUtils.rm_rf File.join(File.dirname(__FILE__), 'files')
|
58
|
+
# Files generated by random_tempfile_filename
|
59
|
+
FileUtils.rm_rf Dir['[0-9]*.{png,jpg}']
|
60
|
+
end
|
61
|
+
|
62
|
+
#self.use_transactional_fixtures = true
|
63
|
+
#self.use_instantiated_fixtures = false
|
64
|
+
|
65
|
+
def self.attachment_model(klass = nil)
|
66
|
+
@attachment_model = klass if klass
|
67
|
+
@attachment_model
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.test_against_class(test_method, klass, subclass = false)
|
71
|
+
define_method("#{test_method}_on_#{:sub if subclass}class") do
|
72
|
+
klass = Class.new(klass) if subclass
|
73
|
+
attachment_model klass
|
74
|
+
send test_method, klass
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.test_against_subclass(test_method, klass)
|
79
|
+
test_against_class test_method, klass, true
|
80
|
+
end
|
81
|
+
|
82
|
+
protected
|
83
|
+
def upload_file(options = {})
|
84
|
+
use_temp_file options[:filename] do |file|
|
85
|
+
opts = { :uploaded_data => fixture_file_upload(file, options[:content_type] || 'image/png') }
|
86
|
+
opts.update(options.reject { |k, v| ![:imageable_type, :imageable_id].include?(k) })
|
87
|
+
att = attachment_model.create opts
|
88
|
+
att.reload unless att.new_record?
|
89
|
+
return att
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def upload_merb_file(options = {})
|
94
|
+
use_temp_file options[:filename] do |file|
|
95
|
+
att = attachment_model.create :uploaded_data => {"size" => file.size, "content_type" => options[:content_type] || 'image/png', "filename" => file, 'tempfile' => fixture_file_upload(file, options[:content_type] || 'image/png')}
|
96
|
+
att.reload unless att.new_record?
|
97
|
+
return att
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def use_temp_file(fixture_filename)
|
102
|
+
temp_path = File.join('/tmp', File.basename(fixture_filename))
|
103
|
+
temp_dir = File.join(FIXTURE_PATH, 'tmp')
|
104
|
+
use_file = File.join(FIXTURE_PATH, temp_path)
|
105
|
+
FileUtils.mkdir_p temp_dir
|
106
|
+
FileUtils.cp File.join(FIXTURE_PATH, fixture_filename), use_file
|
107
|
+
yield use_file
|
108
|
+
ensure
|
109
|
+
FileUtils.rm_rf temp_dir
|
110
|
+
end
|
111
|
+
|
112
|
+
def assert_created(num = 1)
|
113
|
+
assert_difference attachment_model.base_class, :count, num do
|
114
|
+
if attachment_model.included_modules.include? DbFile
|
115
|
+
assert_difference DbFile, :count, num do
|
116
|
+
yield
|
117
|
+
end
|
118
|
+
else
|
119
|
+
yield
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def assert_valid(record)
|
125
|
+
assert record.valid?, record.errors.full_messages.join("\n")
|
126
|
+
end
|
127
|
+
|
128
|
+
def assert_file_jpeg_quality(model, thumbnail, expected)
|
129
|
+
filename = if model.respond_to?(:full_filename)
|
130
|
+
model.full_filename(thumbnail)
|
131
|
+
else
|
132
|
+
thumb = thumbnail ? model.thumbnails.find(:first, :conditions => { :thumbnail => thumbnail.to_s }, :include => :db_file) : model
|
133
|
+
unless thumb && thumb.db_file && thumb.db_file.data && thumb.db_file.data.size > 0
|
134
|
+
STDERR.puts "Cannot find DB file data for thumbnail #{thumbnail.inspect} -> Aborting JPEG quality check."
|
135
|
+
return
|
136
|
+
end
|
137
|
+
result = Tempfile.new('dbfile_dump').path
|
138
|
+
File.open(result, 'wb') { |f| f.write(thumb.db_file.data) }
|
139
|
+
result
|
140
|
+
end
|
141
|
+
quality = %x(identify -format '%Q' "#{filename}" 2> /dev/null)
|
142
|
+
if $?.success?
|
143
|
+
assert_equal expected, quality.to_i, "Produced JPEG quality (thumbnail: #{thumbnail.inspect}) is incorrect."
|
144
|
+
else
|
145
|
+
STDERR.puts "ImageMagick's identify not found / not in PATH: can't quickly check produced image quality."
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def assert_not_created
|
150
|
+
assert_created(0) { yield }
|
151
|
+
end
|
152
|
+
|
153
|
+
def should_reject_by_size_with(klass)
|
154
|
+
attachment_model klass
|
155
|
+
assert_not_created do
|
156
|
+
attachment = upload_file :filename => '/files/rails.png'
|
157
|
+
assert attachment.new_record?
|
158
|
+
assert attachment.errors.on(:size)
|
159
|
+
assert_nil attachment.db_file if attachment.respond_to?(:db_file)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def assert_difference(object, method = nil, difference = 1)
|
164
|
+
initial_value = object.send(method)
|
165
|
+
yield
|
166
|
+
assert_equal initial_value + difference, object.send(method)
|
167
|
+
end
|
168
|
+
|
169
|
+
def assert_no_difference(object, method, &block)
|
170
|
+
assert_difference object, method, 0, &block
|
171
|
+
end
|
172
|
+
|
173
|
+
def attachment_model(klass = nil)
|
174
|
+
@attachment_model = klass if klass
|
175
|
+
@attachment_model
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
require File.join(File.dirname(__FILE__), 'fixtures/attachment')
|
180
|
+
require File.join(File.dirname(__FILE__), 'base_attachment_tests')
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
|
2
|
+
|
3
|
+
class ValidationTest < Test::Unit::TestCase
|
4
|
+
def test_should_invalidate_big_files
|
5
|
+
@attachment = SmallAttachment.new
|
6
|
+
assert !@attachment.valid?
|
7
|
+
assert @attachment.errors[:size]
|
8
|
+
|
9
|
+
@attachment.size = 2000
|
10
|
+
assert !@attachment.valid?
|
11
|
+
assert @attachment.errors[:size], @attachment.errors.full_messages.to_sentence
|
12
|
+
|
13
|
+
@attachment.size = 1000
|
14
|
+
assert !@attachment.valid?
|
15
|
+
assert @attachment.errors[:size].empty?
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_should_invalidate_small_files
|
19
|
+
@attachment = BigAttachment.new
|
20
|
+
assert !@attachment.valid?
|
21
|
+
assert @attachment.errors[:size]
|
22
|
+
|
23
|
+
@attachment.size = 2000
|
24
|
+
assert !@attachment.valid?
|
25
|
+
assert @attachment.errors[:size], @attachment.errors.full_messages.to_sentence
|
26
|
+
|
27
|
+
@attachment.size = 1.megabyte
|
28
|
+
assert !@attachment.valid?
|
29
|
+
assert @attachment.errors[:size].empty?
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_should_validate_content_type
|
33
|
+
@attachment = PdfAttachment.new
|
34
|
+
assert !@attachment.valid?
|
35
|
+
assert @attachment.errors[:content_type]
|
36
|
+
|
37
|
+
@attachment.content_type = 'foo'
|
38
|
+
assert !@attachment.valid?
|
39
|
+
assert @attachment.errors[:content_type]
|
40
|
+
|
41
|
+
@attachment.content_type = 'pdf'
|
42
|
+
assert !@attachment.valid?
|
43
|
+
assert @attachment.errors[:content_type].empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_should_require_filename
|
47
|
+
@attachment = Attachment.new
|
48
|
+
assert !@attachment.valid?
|
49
|
+
assert @attachment.errors[:filename]
|
50
|
+
|
51
|
+
@attachment.filename = 'foo'
|
52
|
+
assert !@attachment.valid?
|
53
|
+
assert @attachment.errors[:filename].empty?
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module RedArtisan
|
2
|
+
module CoreImage
|
3
|
+
module Filters
|
4
|
+
module Color
|
5
|
+
|
6
|
+
def greyscale(color = nil, intensity = 1.00)
|
7
|
+
create_core_image_context(@original.extent.size.width, @original.extent.size.height)
|
8
|
+
|
9
|
+
color = OSX::CIColor.colorWithString("1.0 1.0 1.0 1.0") unless color
|
10
|
+
|
11
|
+
@original.color_monochrome :inputColor => color, :inputIntensity => intensity do |greyscale|
|
12
|
+
@target = greyscale
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def sepia(intensity = 1.00)
|
17
|
+
create_core_image_context(@original.extent.size.width, @original.extent.size.height)
|
18
|
+
|
19
|
+
@original.sepia_tone :inputIntensity => intensity do |sepia|
|
20
|
+
@target = sepia
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module RedArtisan
|
2
|
+
module CoreImage
|
3
|
+
module Filters
|
4
|
+
module Effects
|
5
|
+
|
6
|
+
def spotlight(position, points_at, brightness, concentration, color)
|
7
|
+
create_core_image_context(@original.extent.size.width, @original.extent.size.height)
|
8
|
+
|
9
|
+
@original.spot_light :inputLightPosition => vector3(*position), :inputLightPointsAt => vector3(*points_at),
|
10
|
+
:inputBrightness => brightness, :inputConcentration => concentration, :inputColor => color do |spot|
|
11
|
+
@target = spot
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def edges(intensity = 1.00)
|
16
|
+
create_core_image_context(@original.extent.size.width, @original.extent.size.height)
|
17
|
+
|
18
|
+
@original.edges :inputIntensity => intensity do |edged|
|
19
|
+
@target = edged
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def vector3(x, y, w)
|
26
|
+
OSX::CIVector.vectorWithX_Y_Z(x, y, w)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RedArtisan
|
2
|
+
module CoreImage
|
3
|
+
module Filters
|
4
|
+
module Perspective
|
5
|
+
|
6
|
+
def perspective(top_left, top_right, bottom_left, bottom_right)
|
7
|
+
create_core_image_context(@original.extent.size.width, @original.extent.size.height)
|
8
|
+
|
9
|
+
@original.perspective_transform :inputTopLeft => top_left, :inputTopRight => top_right, :inputBottomLeft => bottom_left, :inputBottomRight => bottom_right do |transformed|
|
10
|
+
@target = transformed
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def perspective_tiled(top_left, top_right, bottom_left, bottom_right)
|
15
|
+
create_core_image_context(@original.extent.size.width, @original.extent.size.height)
|
16
|
+
|
17
|
+
@original.perspective_tile :inputTopLeft => top_left, :inputTopRight => top_right, :inputBottomLeft => bottom_left, :inputBottomRight => bottom_right do |tiled|
|
18
|
+
@target = tiled
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|