image_voodoo 0.8.8 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +4 -0
- data/.rubocop.yml +165 -0
- data/.travis.yml +16 -1
- data/Gemfile +5 -0
- data/Jars.lock +3 -0
- data/Rakefile +14 -4
- data/bin/image_voodoo +73 -84
- data/image_voodoo.gemspec +14 -7
- data/lib/image_science.rb +3 -1
- data/lib/image_voodoo/awt/core_ext/buffered_image.rb +15 -0
- data/lib/image_voodoo/awt/core_ext/graphics2d.rb +15 -0
- data/lib/image_voodoo/awt/shapes.rb +41 -3
- data/lib/image_voodoo/awt.rb +162 -152
- data/lib/image_voodoo/gae.rb +12 -6
- data/lib/image_voodoo/metadata.rb +138 -90
- data/lib/image_voodoo/needs_head.rb +3 -0
- data/lib/image_voodoo/version.rb +3 -1
- data/lib/image_voodoo.rb +47 -96
- data/samples/bench.rb +30 -36
- data/samples/file_greyscale.rb +2 -0
- data/samples/file_thumbnail.rb +2 -0
- data/samples/file_view.rb +4 -1
- data/samples/{in-memory.rb → in_memory.rb} +2 -0
- data/samples/lossy.rb +2 -0
- data/test/test_image_science.rb +34 -74
- data/test/test_image_voodoo.rb +84 -0
- data/test/test_metadata.rb +31 -16
- data/test/test_shapes.rb +23 -0
- data/tools/gen.rb +34 -27
- metadata +87 -10
- data/vendor/metadata-extractor-2.7.0.jar +0 -0
- data/vendor/xmpcore-5.1.2.jar +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 432be5fdc3ce35d25ccec454aec40066f07f512fdf0d45891ecc49ff28e48368
|
4
|
+
data.tar.gz: cacc16a2c686344983bab91d9467ff8a80957f22db50ab037f54553bd1004823
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 421f317327a6d6ecb7c799cd1d4bee0a8a0193ce5f9395c55c2b642387d4fb8b6f62533255b565a5698d379c5ac91d19b0c0fc9fa7776116efee90c537ecf7dd
|
7
|
+
data.tar.gz: 2fe8bfed88aaa7645ed9eb240a652a0c47c095e72043f62dddfe5effcf415c3d833a2b5f6991ad852bb9a9bd06e4bd3363742807229c7efb0b227ab0b1d123af
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
# Experiment to tweak source based on what rubocop wanted me to. I basically
|
2
|
+
# gave up and made the following exceptions. Personally, I find the tool did
|
3
|
+
# find many interesting/questionable code Qs in the project but it is soooo
|
4
|
+
# opinionated in pretty unimportant ways (like who cares how I write my
|
5
|
+
# comments, or why can't I use kind_of? or why give me a fail for a less
|
6
|
+
# readable but more performant pattern).
|
7
|
+
#
|
8
|
+
# I can obviously tweak rules (like below) but I keep hearing of people pulling
|
9
|
+
# their hair out over all the unimportant rules; I question why this project is
|
10
|
+
# "all-in" on it's rules versus leaving most of the style rules as some preset
|
11
|
+
# profiles people can opt in to.
|
12
|
+
#
|
13
|
+
# So I am committing this and I will try and run this every now and then to
|
14
|
+
# see what it thinks about what I wrote but you will not see this as a
|
15
|
+
# requirement for commits since I think by the time I configured it to my
|
16
|
+
# liking I could have finished implementing this library. Perhaps if I
|
17
|
+
# continue using this here and there I will end up with a profile I find
|
18
|
+
# acceptable? I did find value but all the piddly shit just made me angry.
|
19
|
+
# I will reflect on it some more...
|
20
|
+
|
21
|
+
# This is a Java AWT call. It is not what the tool thinks it is...which
|
22
|
+
# might be an issue for users interacting with Java from JRuby.
|
23
|
+
Lint/UselessSetterCall:
|
24
|
+
Exclude:
|
25
|
+
- 'lib/image_voodoo/awt.rb'
|
26
|
+
|
27
|
+
# This is just impossible for them to call properly. I am doing something
|
28
|
+
# like rotate_impl and it is a bunch of reasonably simple math. If you look
|
29
|
+
# for this transformation in a text book it will look like this code. Putting
|
30
|
+
# a bunch of math within different methods would get the score down but
|
31
|
+
# it would not help readability. (addendum: I did try and break it apart more
|
32
|
+
# and never reached 15 but got closer. I felt this rule is neat but the
|
33
|
+
# score really depends on what it is.
|
34
|
+
Metrics/AbcSize:
|
35
|
+
Max: 16.5
|
36
|
+
|
37
|
+
# This is triggered for correct_orientation_impl. I could put these transforms
|
38
|
+
# in a table and then execute lambdas for each orientation but this is much
|
39
|
+
# more clear.
|
40
|
+
Metrics/CyclomaticComplexity:
|
41
|
+
Max: 10
|
42
|
+
|
43
|
+
# A bunch of fields in a hash are basically generated data. Correcting them
|
44
|
+
# for column seems much too pedantic. I guess exclude is the right thing for
|
45
|
+
# an unusual file?
|
46
|
+
Layout/LineLength:
|
47
|
+
Max: 132
|
48
|
+
Exclude:
|
49
|
+
- 'lib/image_voodoo/metadata.rb'
|
50
|
+
|
51
|
+
# Metadata classes have data in them. awt.rb is big and perhaps could be
|
52
|
+
# shrunk but it is not a hard file to navigate and the cleaving points are
|
53
|
+
# not super obvious. These sorts of rules feel very arbitrary and if I have
|
54
|
+
# n highly related things and they do not fit into a more restrictive
|
55
|
+
# taxonomy why would the file being longer matter? I do understand the
|
56
|
+
# motivation here but as a default rule this feels wrong to me.
|
57
|
+
Metrics/ClassLength:
|
58
|
+
Max: 250
|
59
|
+
|
60
|
+
# I do not find this very useful. There is no performance difference and
|
61
|
+
# sometimes I want to highlight this string does not involve interpolation.
|
62
|
+
# Other times it is not worth pointing out.
|
63
|
+
Style/StringLiterals:
|
64
|
+
Enabled: false
|
65
|
+
|
66
|
+
# require 'english' is a step too far for $! which is so baked into my
|
67
|
+
# head I do not want to change :)
|
68
|
+
Style/SpecialGlobalVars:
|
69
|
+
Enabled: false
|
70
|
+
|
71
|
+
# I am grouping math and using lack of whitespace for separation.
|
72
|
+
Layout/SpaceAroundOperators:
|
73
|
+
Enabled: false
|
74
|
+
|
75
|
+
# I prefer tight assignment for opt args.
|
76
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
77
|
+
Enabled: false
|
78
|
+
|
79
|
+
# Java methods which override or implement Java method names cannot be switched
|
80
|
+
# to snake case. Do we really need this as a rule anyways? I have never seen
|
81
|
+
# a Rubyist do this as a preferred style?
|
82
|
+
Naming/MethodName:
|
83
|
+
Enabled: false
|
84
|
+
|
85
|
+
# bin/image_voodoo main options block.
|
86
|
+
Metrics/BlockLength:
|
87
|
+
Exclude:
|
88
|
+
- 'bin/image_voodoo'
|
89
|
+
|
90
|
+
# Hash rocket looks much more natural in a rakefile for its deps.
|
91
|
+
Style/HashSyntax:
|
92
|
+
Exclude:
|
93
|
+
- 'Rakefile'
|
94
|
+
|
95
|
+
# Forget it. I do parallel assignment and you will have to peel it out of
|
96
|
+
# my cold dead hands.
|
97
|
+
Style/ParallelAssignment:
|
98
|
+
Enabled: false
|
99
|
+
|
100
|
+
# FIXME: consider keywords for shapes.
|
101
|
+
# I might switch these to keyword args if I ever revisit shapes support.
|
102
|
+
# In general lots of params do suck and are hard to remember. This library
|
103
|
+
# still is supposed to work in 1.8 but I can probably soon major rev this
|
104
|
+
# and switch over to keywords.
|
105
|
+
Metrics/ParameterLists:
|
106
|
+
Exclude:
|
107
|
+
- 'lib/image_voodoo/awt/shapes.rb'
|
108
|
+
|
109
|
+
# This is complaining that 'x' and 'y' are bad names because they are less
|
110
|
+
# than 3 chars. Silly rule even if I appreciate what it is trying to
|
111
|
+
# accomplish. Interestingly, in image_voodoo I do use h for height and
|
112
|
+
# w for width. Those probably could be written out but they are so common
|
113
|
+
# I am basically opting for short-hand. This is something I rarely do except
|
114
|
+
# domain-driven code like this (x,y,h,w).
|
115
|
+
Naming/MethodParameterName:
|
116
|
+
Enabled: false
|
117
|
+
|
118
|
+
# This represents one of those frustrating dogmatic rules where if I followed
|
119
|
+
# this advice I would then violate the column length rule. I specifically
|
120
|
+
# use traditional if indented block in cases where if_mod doesn't fit.
|
121
|
+
# Gramatically, if_mod can be across two lines but I hate how that looks. The
|
122
|
+
# second solution of logical &&/|| change for a raise is alien to for all but
|
123
|
+
# assignment.
|
124
|
+
Style/IfUnlessModifier:
|
125
|
+
Enabled: false
|
126
|
+
|
127
|
+
# What the hell. Really telling people that encoding: utf-8 should not happen
|
128
|
+
# because there are no multi-byte utf-8 characters in it? Why add this noise?
|
129
|
+
Style/Encoding:
|
130
|
+
Enabled: false
|
131
|
+
|
132
|
+
# FIXME:
|
133
|
+
# This is an unknown error which I should figure out. It must be some
|
134
|
+
# StandardError (which is what nothing explicit means) but I should actually
|
135
|
+
# be more explicit here.
|
136
|
+
Style/RescueStandardError:
|
137
|
+
Exclude:
|
138
|
+
- 'lib/image_voodoo.rb'
|
139
|
+
|
140
|
+
|
141
|
+
# This was complaining metadata_impl should use @metadata_impl instead of
|
142
|
+
# @metadata. I disagree and am unsure why I need to follow this programs
|
143
|
+
# opinion on what a good name is? What is commentable is if I change the
|
144
|
+
# variable name to @gorgon it still tells me I need to use @metadata_impl.
|
145
|
+
# So if I conditionally add any state in any method it will tell me I am
|
146
|
+
# memoizing and have to use same ivar as the name of the method? Is it more
|
147
|
+
# sophisticated than that? Should I have spent 2-3 minutes trying to figure
|
148
|
+
# out how this rule was implemented? :)
|
149
|
+
Naming/MemoizedInstanceVariableName:
|
150
|
+
Enabled: false
|
151
|
+
|
152
|
+
# I have two modes of use for private. If it is a small number of individual
|
153
|
+
# methods I am explicit and inline the private. If it is a large number of
|
154
|
+
# methods especially when the name is fairly obvious that those will be private
|
155
|
+
# (like render_impl) then I use block private. Not sure telling me I can only
|
156
|
+
# use a single style is very useful.
|
157
|
+
Style/AccessModifierDeclarations:
|
158
|
+
Enabled: false
|
159
|
+
|
160
|
+
# I feel for the tiny amount of generation here a better name would not help
|
161
|
+
# me understand it better. This is one of those naming rules I feel is
|
162
|
+
# probably helpful a minority of the time. Also I have never been confused
|
163
|
+
# by what a heredoc text represents because of my heredoc terminator?
|
164
|
+
Naming/HeredocDelimiterNaming:
|
165
|
+
Enabled: false
|
data/.travis.yml
CHANGED
@@ -1,2 +1,17 @@
|
|
1
|
+
language: ruby
|
2
|
+
|
3
|
+
sudo: false
|
4
|
+
|
1
5
|
rvm:
|
2
|
-
- jruby-
|
6
|
+
- jruby-head
|
7
|
+
|
8
|
+
before_install:
|
9
|
+
- mkdir rvm_tmp
|
10
|
+
- cd rvm_tmp
|
11
|
+
|
12
|
+
before_script:
|
13
|
+
- cd ..
|
14
|
+
- git clone --depth 1 https://github.com/drewnoakes/metadata-extractor-images.git ../metadata-extractor-images
|
15
|
+
|
16
|
+
install: JRUBY_OPTS=--dev jruby --dev -S bundle install --jobs=3 --retry=3
|
17
|
+
|
data/Gemfile
ADDED
data/Jars.lock
ADDED
data/Rakefile
CHANGED
@@ -1,13 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'bundler'
|
2
4
|
require 'rake/testtask'
|
5
|
+
require 'jars/installer'
|
3
6
|
Bundler::GemHelper.install_tasks
|
7
|
+
Bundler.setup
|
8
|
+
|
9
|
+
task :default => %i[vendor_jars test]
|
4
10
|
|
5
|
-
|
11
|
+
desc 'Vendor Jars'
|
12
|
+
task :vendor_jars do
|
13
|
+
test_file = File.expand_path File.join('lib', 'image_voodoo_jars.rb'), __dir__
|
14
|
+
Jars::Installer.vendor_jars! unless File.exist? test_file
|
15
|
+
end
|
6
16
|
|
7
|
-
desc
|
8
|
-
task :test do
|
17
|
+
desc 'Run tests'
|
18
|
+
task :test => :vendor_jars do
|
9
19
|
Rake::TestTask.new do |t|
|
10
|
-
t.libs <<
|
20
|
+
t.libs << 'lib:vendor'
|
11
21
|
t.test_files = FileList['test/test*.rb']
|
12
22
|
end
|
13
23
|
end
|
data/bin/image_voodoo
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
#!/usr/bin/env ruby
|
2
4
|
|
3
5
|
require 'optparse'
|
@@ -7,101 +9,91 @@ actions = []
|
|
7
9
|
images = []
|
8
10
|
original_image = nil
|
9
11
|
|
10
|
-
opts = OptionParser.new do |
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
opts.on("-b", "--brightness SCALE,OFFSET", "Adjust brightness") do |args|
|
39
|
-
scale, offset = args.split(/,/).map {|v| v.to_f}
|
40
|
-
opts.usage "You need to specify proper scale and offset" unless scale && offset
|
41
|
-
actions << lambda {|img| img.adjust_brightness(scale, offset) }
|
42
|
-
end
|
43
|
-
|
44
|
-
opts.on("-B", "--border WIDTH,COLOR,STYLE", "Add a simple border") do |args|
|
12
|
+
opts = OptionParser.new do |o|
|
13
|
+
o.banner = 'Usage: image_voodoo [actions] image_file'
|
14
|
+
o.separator 'Perform actions/transformations on an image.'
|
15
|
+
o.separator ''
|
16
|
+
o.separator 'Examples:'
|
17
|
+
o.separator ' image_voodoo --dim small.jpg # Print the dimensions'
|
18
|
+
o.separator ''
|
19
|
+
o.separator ' # Make a thumbnail, preview it, and then save it.'
|
20
|
+
o.separator ' image_voodoo --thumbnail 50 --preview --save thumb.png large.jpg'
|
21
|
+
o.separator ''
|
22
|
+
o.separator ' # Make 2 thumbnails, showing dimensions and previewing them'
|
23
|
+
o.separator ' image_voodoo --dim --resize 50x50 --dim --preview --save t1.jpg'
|
24
|
+
o.separator ' --pop --resize 40x40 --dim --preview --save t2.jpg image.jpg'
|
25
|
+
o.separator ''
|
26
|
+
o.separator 'Actions:'
|
27
|
+
|
28
|
+
o.on('-a', '--alpha rrggbb', 'Make color transparent in image') do |c|
|
29
|
+
o.usage 'rrggbb is in hexidecimal format' if c !~ /[[:xdigit:]]{6,6}/
|
30
|
+
actions << ->(img) { img.alpha(c) }
|
31
|
+
end
|
32
|
+
|
33
|
+
o.on('-b', '--brightness SCALE,OFFSET', 'Adjust brightness') do |args|
|
34
|
+
scale, offset = args.split(/,/).map(&:to_f)
|
35
|
+
o.usage 'You need to specify proper scale and offset' unless scale && offset
|
36
|
+
actions << ->(img) { img.adjust_brightness(scale, offset) }
|
37
|
+
end
|
38
|
+
|
39
|
+
o.on('-B', '--border WIDTH,COLOR,STYLE', 'Add a simple border') do |args|
|
45
40
|
width, color, style = args.split(/,/)
|
46
|
-
options = {:
|
47
|
-
|
48
|
-
actions << lambda {|img| img.add_border(options) }
|
41
|
+
options = { width: width, color: color, style: style }
|
42
|
+
actions << ->(img) { img.add_border(options) }
|
49
43
|
end
|
50
44
|
|
51
|
-
|
52
|
-
actions <<
|
45
|
+
o.on('-d', '--dimensions', 'Print the image dimensions') do
|
46
|
+
actions << ->(img) { img.tap { puts "#{img.width}x#{img.height}" } }
|
53
47
|
end
|
54
48
|
|
55
|
-
|
56
|
-
actions <<
|
49
|
+
o.on('-g', '--greyscale', 'Convert image to greyscale') do
|
50
|
+
actions << ->(img) { img.greyscale }
|
57
51
|
end
|
58
52
|
|
59
|
-
|
60
|
-
actions <<
|
53
|
+
o.on('-h', '--flip_horizontally') do
|
54
|
+
actions << ->(img) { img.flip_horizontally }
|
61
55
|
end
|
62
56
|
|
63
|
-
|
64
|
-
actions <<
|
57
|
+
o.on('-m', '--metadata') do
|
58
|
+
actions << ->(img) { img.tap { puts img.metadata } }
|
65
59
|
end
|
66
60
|
|
67
|
-
|
68
|
-
actions <<
|
61
|
+
o.on('-n', '--negative', 'Make a negative out of the image') do
|
62
|
+
actions << ->(img) { img.negative }
|
69
63
|
end
|
70
64
|
|
71
|
-
|
72
|
-
actions <<
|
65
|
+
o.on('-o', '--orient', 'Rotate image to orient it based on metadata') do
|
66
|
+
actions << ->(img) { img.correct_orientation }
|
73
67
|
end
|
74
68
|
|
75
|
-
|
76
|
-
actions <<
|
69
|
+
o.on('-q', '--quality 0..1', Float, 'Set % of quality for lossy compression') do |quality|
|
70
|
+
actions << ->(img) { img.quality(quality) }
|
77
71
|
end
|
78
72
|
|
79
|
-
|
80
|
-
actions <<
|
73
|
+
o.on('-R', '--rotate 0..360', Float, 'Set angle to rotate image') do |angle|
|
74
|
+
actions << ->(img) { img.rotate(angle.to_f) }
|
81
75
|
end
|
82
76
|
|
83
|
-
|
84
|
-
width, height = dim.split(/x/i).map
|
85
|
-
|
86
|
-
actions <<
|
77
|
+
o.on('-r', '--resize WIDTHxHEIGHT', 'Make a new resized image') do |dim|
|
78
|
+
width, height = dim.split(/x/i).map(&:to_i)
|
79
|
+
o.usage 'You need to specify proper dimensions' unless width && width > 0 && height && height > 0
|
80
|
+
actions << ->(img) { img.resize(width, height) }
|
87
81
|
end
|
88
82
|
|
89
|
-
|
90
|
-
|
91
|
-
actions << lambda {|img| img.save(f); img }
|
83
|
+
o.on('-s', '--save FILENAME', 'Save the results to a new file') do |f|
|
84
|
+
actions << ->(img) { img.tap { img.save(f) } }
|
92
85
|
end
|
93
86
|
|
94
|
-
|
95
|
-
actions <<
|
87
|
+
o.on('-t', '--thumbnail SIZE', Integer, 'Create a thumbnail of the given size') do |size|
|
88
|
+
actions << ->(img) { img.thumbnail(size) }
|
96
89
|
end
|
97
90
|
|
98
|
-
|
99
|
-
actions << lambda {|img| img.flip_vertically }
|
100
|
-
end
|
101
|
-
|
102
|
-
opts.on("-p", "--preview", "Preview the image. Close the frame window",
|
103
|
-
"to continue, or quit the application to", "abort the action pipeline") do
|
91
|
+
o.on('-v', '--flip_vertically') { actions << ->(img) { img.flip_vertically } }
|
104
92
|
|
93
|
+
o.on('-p', '--preview',
|
94
|
+
'Preview the image. Close the frame window',
|
95
|
+
'to continue, or quit the application to',
|
96
|
+
'abort the action pipeline') do
|
105
97
|
headless = false
|
106
98
|
actions << lambda do |img|
|
107
99
|
done = false
|
@@ -111,36 +103,33 @@ opts = OptionParser.new do |opts|
|
|
111
103
|
end
|
112
104
|
end
|
113
105
|
|
114
|
-
|
115
|
-
actions <<
|
106
|
+
o.on('--push', 'Save the current image to be popped later') do
|
107
|
+
actions << ->(img) { img.tap { images << img } }
|
116
108
|
end
|
117
109
|
|
118
|
-
|
119
|
-
actions <<
|
110
|
+
o.on('--pop', 'Revert back to the previous image') do
|
111
|
+
actions << -> { images.pop || original_image }
|
120
112
|
end
|
121
113
|
|
122
|
-
|
123
|
-
actions <<
|
114
|
+
o.on('-f', '--format', 'Print the image format') do
|
115
|
+
actions << ->(img) { img.tap { img.format } }
|
124
116
|
end
|
125
117
|
|
126
|
-
|
127
|
-
puts opts
|
128
|
-
exit 0
|
129
|
-
end
|
118
|
+
o.on_tail('-h', '--help', 'Show this message') { o.usage }
|
130
119
|
|
131
|
-
def
|
132
|
-
puts msg
|
120
|
+
def o.usage(msg=nil)
|
121
|
+
puts msg if msg
|
133
122
|
puts self
|
134
123
|
exit 1
|
135
124
|
end
|
136
125
|
end
|
137
126
|
opts.parse!(ARGV)
|
138
|
-
opts.usage(
|
139
|
-
opts.usage(
|
127
|
+
opts.usage('You need to supply a source image filename.') unless ARGV.first
|
128
|
+
opts.usage('You need to supply one or more actions.') if actions.empty?
|
140
129
|
|
141
130
|
# For this binstub we only want to load non-headless if we are using
|
142
131
|
# the preview feature. top of See lib/image_voodoo.rb for more info...
|
143
|
-
|
132
|
+
require 'image_voodoo/needs_head' unless headless
|
144
133
|
|
145
134
|
require 'image_voodoo'
|
146
135
|
file_name = ARGV.first
|
data/image_voodoo.gemspec
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
$LOAD_PATH.push File.expand_path('lib', __dir__)
|
5
|
+
require 'image_voodoo/version'
|
4
6
|
|
5
7
|
Gem::Specification.new do |s|
|
6
8
|
s.name = 'image_voodoo'
|
@@ -12,11 +14,16 @@ Gem::Specification.new do |s|
|
|
12
14
|
s.summary = 'Image manipulation in JRuby with ImageScience compatible API'
|
13
15
|
s.description = 'Image manipulation in JRuby with ImageScience compatible API'
|
14
16
|
|
15
|
-
s.rubyforge_project = "image_voodoo"
|
16
|
-
|
17
17
|
s.files = `git ls-files`.split("\n")
|
18
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 = [
|
21
|
-
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
20
|
+
s.require_paths = %w[lib vendor]
|
21
|
+
|
22
|
+
s.add_development_dependency 'jar-dependencies'
|
23
|
+
s.add_development_dependency 'rake'
|
24
|
+
s.add_development_dependency 'rubocop'
|
25
|
+
s.add_development_dependency 'ruby-maven'
|
26
|
+
s.add_development_dependency 'test-unit'
|
27
|
+
|
28
|
+
s.requirements << 'jar com.drewnoakes, metadata-extractor, 2.11.0'
|
22
29
|
end
|
data/lib/image_science.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'java'
|
4
|
+
|
5
|
+
# Hide in ImageVoodoo so awt.rb can see this and we will not polute global
|
6
|
+
class ImageVoodoo
|
7
|
+
java_import java.awt.image.BufferedImage
|
8
|
+
|
9
|
+
# Re-open to add convenience methods.
|
10
|
+
class BufferedImage
|
11
|
+
def each
|
12
|
+
height.times { |j| width.times { |i| yield i, j } }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'java'
|
4
|
+
|
5
|
+
# Hide in ImageVoodoo so awt.rb can see this and we will not polute global
|
6
|
+
class ImageVoodoo
|
7
|
+
java_import java.awt.Graphics2D
|
8
|
+
|
9
|
+
# Re-open to add convenience methods.
|
10
|
+
class Graphics2D
|
11
|
+
def draw_this_image(image)
|
12
|
+
draw_image image, 0, 0, nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,5 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class ImageVoodoo
|
4
|
+
# (Experimental) An attempt at some primitive drawing in images.
|
2
5
|
module Shapes
|
6
|
+
# FIXME: if image has alpha values the border shows through since it is
|
7
|
+
# a solid fill.
|
8
|
+
##
|
9
|
+
# *AWT* (experimental) Add a border to the image and yield/return a new
|
10
|
+
# image. The following options are supported:
|
11
|
+
# - width: How thick is the border (default: 3)
|
12
|
+
# - color: Which color is the border (in rrggbb hex value)
|
13
|
+
# - style: etched, raised, plain (default: plain)
|
14
|
+
#
|
15
|
+
def add_border(options = {})
|
16
|
+
border_width = options[:width].to_i || 2
|
17
|
+
new_width, new_height = width + 2*border_width, height + 2*border_width
|
18
|
+
target = paint(BufferedImage.new(new_width, new_height, color_type)) do |g|
|
19
|
+
paint_border(g, new_width, new_height, options)
|
20
|
+
g.draw_image(@src, nil, border_width, border_width)
|
21
|
+
end
|
22
|
+
block_given? ? yield(target) : target
|
23
|
+
end
|
24
|
+
|
25
|
+
def paint_border(g, new_width, new_height, options)
|
26
|
+
g.color = hex_to_color(options[:color])
|
27
|
+
fill_method, *args = border_style(options)
|
28
|
+
g.send fill_method, 0, 0, new_width, new_height, *args
|
29
|
+
end
|
30
|
+
|
31
|
+
def border_style(options)
|
32
|
+
case (options[:style] || "").to_s
|
33
|
+
when "raised" then
|
34
|
+
[:fill3DRect, true]
|
35
|
+
when "etched" then
|
36
|
+
[:fill3DRect, false]
|
37
|
+
else
|
38
|
+
[:fill_rect]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
3
42
|
##
|
4
43
|
# *AWT* Draw a square
|
5
44
|
#
|
@@ -18,14 +57,14 @@ class ImageVoodoo
|
|
18
57
|
# *AWT* Draw a rounded square
|
19
58
|
#
|
20
59
|
def square_rounded(x, y, dim, rgb, arc_width=0, fill=true)
|
21
|
-
rect_rounded(x,y, dim, dim, rgb, arc_width, arc_width, fill)
|
60
|
+
rect_rounded(x, y, dim, dim, rgb, arc_width, arc_width, fill)
|
22
61
|
end
|
23
62
|
|
24
63
|
##
|
25
64
|
# *AWT* Draw a rounded rectangle
|
26
65
|
#
|
27
66
|
def rect_rounded(x, y, width, height, rgb, arc_width=0, arc_height=0, fill=true)
|
28
|
-
as_color(
|
67
|
+
as_color(hex_to_color(rgb)) do |g|
|
29
68
|
if fill
|
30
69
|
g.fill_round_rect x, y, width, height, arc_width, arc_height
|
31
70
|
else
|
@@ -44,4 +83,3 @@ class ImageVoodoo
|
|
44
83
|
end
|
45
84
|
end
|
46
85
|
end
|
47
|
-
|