├── .gitignore ├── .rspec ├── .yardopts ├── CHANGELOG.md ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── README.md ├── Rakefile ├── TODO ├── VERSION ├── example ├── annotate.rb ├── daltonize8.rb ├── example1.rb ├── example2.rb ├── example3.rb ├── example4.rb ├── example5.rb ├── trim8.rb ├── watermark.rb └── wobble.rb ├── lib ├── vips8.rb └── vips8 │ ├── access.rb │ ├── align.rb │ ├── angle.rb │ ├── angle45.rb │ ├── argument.rb │ ├── bandformat.rb │ ├── call.rb │ ├── coding.rb │ ├── demandstyle.rb │ ├── direction.rb │ ├── error.rb │ ├── extend.rb │ ├── foreignflags.rb │ ├── image.rb │ ├── interpolate.rb │ ├── interpretation.rb │ ├── methods.rb │ └── operation.rb ├── ruby-vips8.gemspec └── spec ├── image_spec.rb ├── samples ├── balloon.v ├── ghost.ppm ├── huge.jpg ├── icc.jpg ├── lcd.icc ├── lion.svg ├── sample.csv ├── sample.exr ├── wagon.jpg └── wagon.v ├── spec_helper.rb └── vips_spec.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | 3 | # rcov generated 4 | coverage 5 | coverage.data 6 | 7 | # rdoc generated 8 | rdoc 9 | 10 | # yard generated 11 | doc 12 | .yardoc 13 | 14 | # bundler 15 | .bundle 16 | 17 | # jeweler generated 18 | pkg 19 | 20 | # tmp files from our spec tests 21 | spec/tmp 22 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --no-private 2 | --markup-provider=redcarpet 3 | --markup=markdown 4 | --main=README.md 5 | --title="ruby-vips8 documentation" 6 | lib/**/*.rb 7 | - 8 | LICENSE.txt 9 | CHANGELOG.md 10 | 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # master 2 | 3 | # Version 0.1 4 | 5 | * first upload [John Cupitt] 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | # runtime deps 4 | gem "gobject-introspection", "~> 3.0" 5 | 6 | # Add dependencies to develop your gem here. 7 | # Include everything needed to run rake, tests, features, etc. 8 | group :development do 9 | gem "rspec", "~> 3.3" 10 | gem "yard", "~> 0.8" 11 | gem "redcarpet", "~> 3.3" 12 | gem "github-markup", "~> 1.4" 13 | gem "bundler", "~> 1.0" 14 | gem "jeweler", "~> 2.0" 15 | end 16 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | addressable (2.4.0) 5 | builder (3.2.2) 6 | descendants_tracker (0.0.4) 7 | thread_safe (~> 0.3, >= 0.3.1) 8 | diff-lcs (1.2.5) 9 | faraday (0.9.2) 10 | multipart-post (>= 1.2, < 3) 11 | git (1.2.9.1) 12 | github-markup (1.4.0) 13 | github_api (0.13.1) 14 | addressable (~> 2.4.0) 15 | descendants_tracker (~> 0.0.4) 16 | faraday (~> 0.8, < 0.10) 17 | hashie (>= 3.4) 18 | multi_json (>= 1.7.5, < 2.0) 19 | oauth2 20 | glib2 (3.0.7) 21 | pkg-config 22 | gobject-introspection (3.0.7) 23 | glib2 (= 3.0.7) 24 | hashie (3.4.3) 25 | highline (1.7.8) 26 | jeweler (2.0.1) 27 | builder 28 | bundler (>= 1.0) 29 | git (>= 1.2.5) 30 | github_api 31 | highline (>= 1.6.15) 32 | nokogiri (>= 1.5.10) 33 | rake 34 | rdoc 35 | json (1.8.3) 36 | jwt (1.5.2) 37 | mini_portile2 (2.0.0) 38 | multi_json (1.11.2) 39 | multi_xml (0.5.5) 40 | multipart-post (2.0.0) 41 | nokogiri (1.6.7.2) 42 | mini_portile2 (~> 2.0.0.rc2) 43 | oauth2 (1.0.0) 44 | faraday (>= 0.8, < 0.10) 45 | jwt (~> 1.0) 46 | multi_json (~> 1.3) 47 | multi_xml (~> 0.5) 48 | rack (~> 1.2) 49 | pkg-config (1.1.7) 50 | rack (1.6.4) 51 | rake (10.5.0) 52 | rdoc (4.2.1) 53 | json (~> 1.4) 54 | redcarpet (3.3.4) 55 | rspec (3.4.0) 56 | rspec-core (~> 3.4.0) 57 | rspec-expectations (~> 3.4.0) 58 | rspec-mocks (~> 3.4.0) 59 | rspec-core (3.4.1) 60 | rspec-support (~> 3.4.0) 61 | rspec-expectations (3.4.0) 62 | diff-lcs (>= 1.2.0, < 2.0) 63 | rspec-support (~> 3.4.0) 64 | rspec-mocks (3.4.1) 65 | diff-lcs (>= 1.2.0, < 2.0) 66 | rspec-support (~> 3.4.0) 67 | rspec-support (3.4.1) 68 | thread_safe (0.3.5) 69 | yard (0.8.7.6) 70 | 71 | PLATFORMS 72 | ruby 73 | 74 | DEPENDENCIES 75 | bundler (~> 1.0) 76 | github-markup (~> 1.4) 77 | gobject-introspection (~> 3.0) 78 | jeweler (~> 2.0) 79 | redcarpet (~> 3.3) 80 | rspec (~> 3.3) 81 | yard (~> 0.8) 82 | 83 | BUNDLED WITH 84 | 1.10.6 85 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 John Cupitt 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ruby-vips8 2 | 3 | ❗ ️❗ ❗ ❗ ️❗ ❗ ❗ ️❗ ❗ ❗ ️❗ ❗ ❗ ️❗ ❗ ❗ ️❗ ❗ ❗ ️❗ ❗ ❗ ️❗ ❗ ❗ ️❗ ❗ 4 | **This gem has been renamed to `ruby-vips` (version 1.0)** 5 | 6 | Go here for the current version of this repo: https://github.com/libvips/ruby-vips 7 | 8 | All you need to do to switch is (1) update your `Gemfile` to ask for `ruby-vips` and (2) 9 | change `require 'vips8'` to `require 'vips'` (see example below). 10 | 11 | ```ruby 12 | # Gemfile 13 | gem 'ruby-vips8', '~> 0.1.0' # OLD 14 | gem 'ruby-vips', '~> 1.0' # NEW 15 | 16 | # your-ruby-code.rb 17 | require 'vips8' # OLD 18 | require 'vips' # NEW 19 | ``` 20 | 21 | Sorry for any inconvenience! 22 | 23 | ❗ ️❗ ❗ ❗ ️❗ ❗ ❗ ️❗ ❗ ❗ ️❗ ❗ ❗ ️❗ ❗ ❗ ️❗ ❗ ❗ ️❗ ❗ ❗ ️❗ ❗ ❗ ️❗ ❗ 24 | 25 | *** 26 | 27 | This gem provides a Ruby binding for the [vips image processing 28 | library](http://www.vips.ecs.soton.ac.uk). It wraps version 8 of the API. 29 | The older vips7-based [ruby-vips](https://github.com/jcupitt/ruby-vips) 30 | gem is still being maintained. 31 | 32 | `ruby-vips8` is fast and it can work without needing to have the 33 | entire image loaded into memory. Programs that use `ruby-vips8` don't 34 | manipulate images directly, instead they create pipelines of image processing 35 | operations building on a source image. When the end of the pipe is connected 36 | to a destination, the whole pipline executes at once, streaming the image 37 | in parallel from source to destination a section at a time. 38 | 39 | For example, the benchmark at 40 | [vips-benchmarks](https://github.com/stanislaw/vips-benchmarks) loads a large 41 | image, crops, shrinks, sharpens and saves again, and repeats 10 times. 42 | 43 | ```text 44 | real time in seconds, fastest of three runs 45 | benchmark tiff jpeg 46 | ruby-vips.rb 2.77 2.98 47 | ruby-vips8.rb 2.97 3.29 48 | image-magick 8.18 9.71 49 | rmagick.rb 9.22 10.06 50 | image_sci.rb 9.39 7.20 51 | 52 | peak memory use in bytes 53 | benchmark peak RSS 54 | ruby-vips.rb 107340 55 | ruby-vips8.rb 117604 56 | image_sci.rb 146536 57 | rmagick.rb 3352020 58 | ``` 59 | 60 | See also [benchmarks at the official libvips 61 | website](http://www.vips.ecs.soton.ac.uk/index.php?title=Speed_and_Memory_Use). 62 | There's a handy blog post explaining [how libvips opens 63 | files](http://libvips.blogspot.co.uk/2012/06/how-libvips-opens-file.html) 64 | which gives some more background. 65 | 66 | ## Requirements 67 | 68 | * OS X or Linux 69 | * libvips 8.2 and later 70 | 71 | ## Installation prerequisites 72 | 73 | ### OS X 74 | 75 | Install [homebrew](http://mxcl.github.com/homebrew) and enter: 76 | 77 | ```bash 78 | $ brew install homebrew/science/vips 79 | ``` 80 | 81 | To verify that your vips install is working, try: 82 | 83 | ```bash 84 | $ vips --version 85 | vips-8.2.1 86 | ``` 87 | 88 | Depending on how your system is configured, you may need to add the 89 | directory containing `Vips-8.0.typelib` to your 90 | your `GI_TYPELIB_PATH`. Something like: 91 | 92 | ```bash 93 | $ export GI_TYPELIB_PATH=/usr/local/lib/girepository-1.0 94 | ``` 95 | 96 | ### Other platforms 97 | 98 | You need to install libvips from source since 8.2 has not been packaged yet 99 | (Jan 2016). 100 | 101 | Download a tarball from the 102 | [libvips website](http://www.vips.ecs.soton.ac.uk/supported/current), or build 103 | from [the git repository](https://github.com/jcupitt/libvips) and see the 104 | README. 105 | 106 | ## Installing the gem. 107 | 108 | ```bash 109 | $ gem install ruby-vips8 110 | ``` 111 | 112 | or include it in Gemfile: 113 | 114 | ```ruby 115 | gem 'ruby-vips8' 116 | ``` 117 | 118 | And take a look in `examples/`. There is full yard documentation, take a look 119 | there too. 120 | 121 | # Example 122 | 123 | ```ruby 124 | require 'vips8' 125 | 126 | im = Vips::Image.new_from_file filename 127 | 128 | # put im at position (100, 100) in a 3000 x 3000 pixel image, 129 | # make the other pixels in the image by mirroring im up / down / 130 | # left / right, see 131 | # http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/libvips-conversion.html#vips-embed 132 | im = im.embed 100, 100, 3000, 3000, :extend => :mirror 133 | 134 | # multiply the green (middle) band by 2, leave the other two alone 135 | im *= [1, 2, 1] 136 | 137 | # make an image from an array constant, convolve with it 138 | mask = Vips::Image.new_from_array [ 139 | [-1, -1, -1], 140 | [-1, 16, -1], 141 | [-1, -1, -1]], 8 142 | im = im.conv mask 143 | 144 | # finally, write the result back to a file on disk 145 | im.write_to_file output_filename 146 | ``` 147 | 148 | # Changes due to vips7 -> vips8 149 | 150 | ruby-vips is based on the old vips7 API. There's now vips8, which adds several 151 | very useful new features: 152 | 153 | * [GObject](https://developer.gnome.org/gobject/stable/)-based API with full 154 | introspection. You can discover the vips8 API at runtime. This means that if 155 | libvips gets a new operator, any binding that goes via vips8 will 156 | get the new thing immediately. With vips7, whenever libvips was changed, all 157 | the bindings needed to be changed too. 158 | 159 | * No C required. Thanks to 160 | [gobject-introspection](https://wiki.gnome.org/Projects/GObjectIntrospection) 161 | you can write the binding in Ruby itself, there's no need for any C. This 162 | makes it a lot smaller and more portable. 163 | 164 | * vips7 probably won't get new features. vips7 doesn't really exist any more: 165 | the API is still there, but now just a thin compatibility layer over vips8. 166 | New features may well not get added to the vips7 API. 167 | 168 | There are some more minor pluses as well: 169 | 170 | * Named and optional arguments. vips8 lets you have optional and required 171 | arguments, both input and output, and optional arguments can have default 172 | values. 173 | 174 | * Operation cache. vips8 keeps track of the last 1,000 or so operations and 175 | will automatically reuse results when it can. This can give a huge speedup 176 | in some cases. 177 | 178 | * vips8 is much simpler and more regular. For example, 179 | ruby-vips had to work hard to offer a nice loader system, but that's all 180 | built into vips8. It can do things like load and save formatted images to 181 | and from memory buffers as well, which just wasn't possible before. 182 | 183 | This binding adds some extra useful features over the old `ruby-vips` binding. 184 | 185 | * Full set of arithmetic operator overloads. 186 | 187 | * Automatic constant expansion. You can write things like 188 | `image.bandjoin(255)` and the 255 will be automatically expanded to an image 189 | and attached as an extra band. You can mix int, float, scalar, vector and 190 | image constants freely. 191 | 192 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'rubygems' 4 | require 'bundler' 5 | 6 | begin 7 | Bundler.setup(:default, :development) 8 | rescue Bundler::BundlerError => e 9 | $stderr.puts e.message 10 | $stderr.puts "Run `bundle install` to install missing gems" 11 | exit e.status_code 12 | end 13 | 14 | require 'rake' 15 | 16 | require 'jeweler' 17 | 18 | Jeweler::Tasks.new do |gem| 19 | # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options 20 | gem.name = "ruby-vips8" 21 | gem.homepage = "http://github.com/jcupitt/ruby-vips8" 22 | gem.license = "MIT" 23 | gem.summary = %Q{Ruby extension for the vips8 image processing library.} 24 | gem.description = %Q{ruby-vips8 is a ruby extension for vips8. It is extremely fast and it can process huge images without requiring the entire image to be loaded into memory.} 25 | gem.email = "jcupitt@gmail.com" 26 | gem.authors = ["John Cupitt"] 27 | # dependencies defined in Gemfile 28 | end 29 | Jeweler::RubygemsDotOrgTasks.new 30 | 31 | require 'rspec/core' 32 | require 'rspec/core/rake_task' 33 | RSpec::Core::RakeTask.new(:spec) do |spec| 34 | spec.pattern = FileList['spec/**/*_spec.rb'] 35 | end 36 | 37 | task :default => :spec 38 | 39 | require "github/markup" 40 | require "redcarpet" 41 | require "yard" 42 | require "yard/rake/yardoc_task" 43 | 44 | YARD::Rake::YardocTask.new do |yard| 45 | end 46 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - need something like ruby-vips occasional GC stuff, we can fill memory before 2 | a GC is triggered 3 | 4 | - mail about the getpoint unimplemented error 5 | 6 | http://sourceforge.net/p/ruby-gnome2/mailman/ruby-gnome2-devel-en/thread/CAGNS0RuZ5N6bha3M7B0%2BYf2M9-oni44idzZO17mtQiykS%2BmJKQ%40mail.gmail.com/#msg34790843 7 | 8 | - still missing a few enum docs 9 | 10 | - add complex constants, eg. 11 | 12 | Complex(1, 2) 13 | => (1+2i) 14 | 15 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.1.0 -------------------------------------------------------------------------------- /example/annotate.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'vips8' 4 | 5 | im = Vips::Image.new_from_file ARGV[0], :access => :sequential 6 | 7 | left_text = Vips::Image.text "left corner", :dpi => 300 8 | left = left_text.embed 50, 50, im.width, 150 9 | 10 | right_text = Vips::Image.text "right corner", :dpi => 300 11 | right = right_text.embed im.width - right_text.width - 50, 50, im.width, 150 12 | 13 | footer = (left | right).ifthenelse(0, [255, 0, 0], :blend => true) 14 | 15 | im = im.insert footer, 0, im.height, :expand => true 16 | 17 | im.write_to_file ARGV[1] 18 | -------------------------------------------------------------------------------- /example/daltonize8.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | # daltonize an image with ruby-vips8 4 | # based on 5 | # http://scien.stanford.edu/pages/labsite/2005/psych221/projects/05/ofidaner/colorblindness_project.htm 6 | # see 7 | # http://libvips.blogspot.co.uk/2013/05/daltonize-in-ruby-vips-carrierwave-and.html 8 | # for a discussion of this code 9 | 10 | require 'vips8' 11 | 12 | #Vips.set_debug true 13 | 14 | # matrices to convert D65 XYZ to and from bradford cone space 15 | xyz_to_brad = [ 16 | [0.8951, 0.2664, -0.1614], 17 | [-0.7502, 1.7135, 0.0367], 18 | [0.0389, -0.0685, 1.0296] 19 | ] 20 | brad_to_xyz = [ 21 | [0.987, -0.147, 0.16], 22 | [0.432, 0.5184, 0.0493], 23 | [-0.0085, 0.04, 0.968] 24 | ] 25 | 26 | im = Vips::Image.new_from_file ARGV[0] 27 | 28 | # remove any alpha channel before processing 29 | alpha = nil 30 | if im.bands == 4 31 | alpha = im[3] 32 | im = im.extract_band 0, :n => 3 33 | end 34 | 35 | begin 36 | # import to XYZ with lcms 37 | # if there's no profile there, we'll fall back to the thing below 38 | xyz = im.icc_import :embedded => true, :pcs => :xyz 39 | rescue Vips::Error 40 | # nope .. use the built-in converter instead 41 | xyz = im.colourspace :xyz 42 | end 43 | 44 | brad = xyz.recomb xyz_to_brad 45 | 46 | # through the Deuteranope matrix 47 | # we need rows to sum to 1 in Bradford space --- the matrix in the original 48 | # Python code sums to 1.742 49 | deut = brad.recomb [ 50 | [1, 0, 0], 51 | [0.7, 0, 0.3], 52 | [0, 0, 1] 53 | ] 54 | 55 | xyz = deut.recomb brad_to_xyz 56 | 57 | # .. and back to sRGB 58 | rgb = xyz.colourspace :srgb 59 | 60 | # so this is the colour error 61 | err = im - rgb 62 | 63 | # add the error back to other channels to make a compensated image 64 | im = im + err.recomb([ 65 | [0, 0, 0], 66 | [0.7, 1, 0], 67 | [0.7, 0, 1] 68 | ]) 69 | 70 | # reattach any alpha we saved above 71 | if alpha 72 | im = im.bandjoin(alpha) 73 | end 74 | 75 | im.write_to_file ARGV[1] 76 | -------------------------------------------------------------------------------- /example/example1.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'vips8' 4 | $vips_debug = true 5 | 6 | puts "" 7 | puts "starting up:" 8 | 9 | # this makes vips keep a list of all active objects which we can print out 10 | Vips::leak_set true 11 | 12 | # disable the operation cache 13 | Vips::cache_set_max 0 14 | 15 | puts "" 16 | puts "creating object:" 17 | x = Vips::Image.new 18 | Vips::Object::print_all 19 | 20 | puts "" 21 | puts "freeing object:" 22 | x = nil 23 | GC.start 24 | Vips::Object::print_all 25 | 26 | puts "" 27 | puts "creating operation:" 28 | op = Vips::Operation.new "black" 29 | Vips::Object::print_all 30 | op.set_property "width", 200 31 | op.set_property "height", 300 32 | 33 | puts "" 34 | puts "after operation init:" 35 | GC.start 36 | Vips::Object::print_all 37 | 38 | puts "" 39 | puts "operation lookup:" 40 | op2 = Vips::cache_operation_lookup op 41 | if op2 42 | puts "cache hit" 43 | op = op2 44 | op2 = nil 45 | else 46 | puts "cache miss ... building" 47 | if not op.build 48 | puts "*** build error" 49 | end 50 | Vips::cache_operation_add op 51 | end 52 | 53 | puts "" 54 | puts "after build:" 55 | GC.start 56 | Vips::Object::print_all 57 | 58 | puts "" 59 | puts "fetching output:" 60 | im = op.get_property "out" 61 | GC.start 62 | Vips::Object::print_all 63 | 64 | puts "" 65 | puts "fetching output again:" 66 | im2 = op.get_property "out" 67 | GC.start 68 | Vips::Object::print_all 69 | 70 | puts "" 71 | puts "freeing operation:" 72 | op.unref_outputs 73 | op = nil 74 | op2 = nil 75 | GC.start 76 | Vips::Object::print_all 77 | 78 | puts "" 79 | puts "shutting down:" 80 | im = nil 81 | im2 = nil 82 | GC.start 83 | Vips::shutdown 84 | GC.start 85 | -------------------------------------------------------------------------------- /example/example2.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'vips8' 4 | 5 | puts "" 6 | puts "starting up:" 7 | 8 | # this makes vips keep a list of all active objects which we can print out 9 | Vips::leak_set true 10 | 11 | # disable the operation cache 12 | Vips::cache_set_max 0 13 | 14 | n = 100 15 | 16 | n.times do |i| 17 | puts "" 18 | puts "call #{i} ..." 19 | out = Vips::call "black", 200, 300 20 | if out.width != 200 or out.height != 300 21 | puts "bad image result from black" 22 | end 23 | end 24 | 25 | puts "" 26 | puts "after #{n} calls:" 27 | GC.start 28 | Vips::Object::print_all 29 | 30 | puts "" 31 | puts "shutting down:" 32 | -------------------------------------------------------------------------------- /example/example3.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'vips8' 4 | 5 | # this makes vips keep a list of all active objects 6 | # Vips::leak_set true 7 | 8 | # disable the operation cache 9 | # Vips::cache_set_max 0 10 | 11 | # turn on debug logging 12 | #Vips.set_debug true 13 | 14 | 10000.times do |i| 15 | puts "loop #{i} ..." 16 | im = Vips::Image.new_from_file ARGV[0] 17 | im = im.embed 100, 100, 3000, 3000, :extend => :mirror 18 | im.write_to_file "x.v" 19 | end 20 | -------------------------------------------------------------------------------- /example/example4.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'vips8' 4 | 5 | # this makes vips keep a list of all active objects 6 | Vips::leak_set true 7 | 8 | # disable the operation cache 9 | #Vips::cache_set_max 0 10 | 11 | # turn on debug logging 12 | #Vips.set_debug true 13 | 14 | ARGV.each do |filename| 15 | im = Vips::Image.new_from_file filename 16 | profile = im.get_value "icc-profile-data" 17 | puts "profile has #{profile.length} bytes" 18 | end 19 | -------------------------------------------------------------------------------- /example/example5.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'vips8' 4 | 5 | # this makes vips keep a list of all active objects 6 | # Vips::leak_set true 7 | 8 | # disable the operation cache 9 | # Vips::cache_set_max 0 10 | 11 | # turn on debug logging 12 | #Vips.set_debug true 13 | 14 | if ARGV.length < 2 15 | raise "usage: #{$PROGRAM_NAME}: input-file output-file" 16 | end 17 | 18 | im = Vips::Image.new_from_file ARGV[0], :access => :sequential 19 | 20 | im *= [1, 2, 1] 21 | 22 | # we want to be able to specify a scale for the convolution mask, so we have to 23 | # make it ourselves 24 | # if you are OK with scale=1, you can just pass the array directly to .conv() 25 | mask = Vips::Image.new_from_array [ 26 | [-1, -1, -1], 27 | [-1, 16, -1], 28 | [-1, -1, -1]], 8 29 | im = im.conv mask 30 | 31 | im.write_to_file ARGV[1] 32 | -------------------------------------------------------------------------------- /example/trim8.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | # An equivalent of ImageMagick's -trim in ruby-vips8 ... automatically remove 4 | # "boring" image edges. 5 | 6 | # We use .project to sum the rows and columns of a 0/255 mask image, the first 7 | # non-zero row or column is the object edge. We make the mask image with an 8 | # amount-different-from-background image plus a threshold. 9 | 10 | require 'vips8' 11 | 12 | im = Vips::Image.new_from_file ARGV[0] 13 | 14 | # find the value of the pixel at (0, 0) ... we will search for all pixels 15 | # significantly different from this 16 | background = im.getpoint(0, 0) 17 | 18 | # we need to smooth the image, subtract the background from every pixel, take 19 | # the absolute value of the difference, then threshold 20 | mask = (im.median - background).abs > 10 21 | 22 | # sum mask rows and columns, then search for the first non-zero sum in each 23 | # direction 24 | columns, rows = mask.project 25 | 26 | first_column, first_row = columns.profile 27 | left = first_row.min 28 | 29 | first_column, first_row = columns.fliphor.profile 30 | right = columns.width - first_row.min 31 | 32 | first_column, first_row = rows.profile 33 | top = first_column.min 34 | 35 | first_column, first_row = rows.flipver.profile 36 | bottom = rows.height - first_column.min 37 | 38 | # and now crop the original image 39 | im = im.crop left, top, right - left, bottom - top 40 | 41 | im.write_to_file ARGV[1] 42 | -------------------------------------------------------------------------------- /example/watermark.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'vips8' 4 | 5 | im = Vips::Image.new_from_file ARGV[0], :access => :sequential 6 | 7 | text = Vips::Image.text ARGV[2], :width => 500, :dpi => 300 8 | text = (text * 0.3).cast(:uchar) 9 | text = text.embed 100, 100, text.width + 200, text.width + 200 10 | text = text.replicate 1 + im.width / text.width, 1 + im.height / text.height 11 | text = text.crop 0, 0, im.width, im.height 12 | 13 | # we want to blend into the visible part of the image and leave any alpha 14 | # channels untouched ... we need to split im into two parts 15 | 16 | # guess how many bands from the start of im contain visible colour information 17 | if im.bands >= 4 and im.interpretation == :cmyk 18 | # cmyk image 19 | n_visible_bands = 4 20 | text_colour = [0, 255, 0, 0] 21 | elsif im.bands >= 3 22 | # rgb image 23 | n_visible_bands = 3 24 | text_colour = [255, 0, 0] 25 | else 26 | # mono image 27 | n_visible_bands = 1 28 | text_colour = 255 29 | end 30 | 31 | # split into image and alpha 32 | if im.bands - n_visible_bands > 0 33 | alpha = im.extract_band n_visible_bands, :n => im.bands - n_visible_bands 34 | im = im.extract_band 0, :n => n_visible_bands 35 | else 36 | alpha = nil 37 | end 38 | 39 | marked = text.ifthenelse text_colour, im, :blend => true 40 | 41 | # reattach alpha 42 | marked = marked.bandjoin alpha if alpha 43 | 44 | marked.write_to_file ARGV[1] 45 | -------------------------------------------------------------------------------- /example/wobble.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'vips8' 4 | 5 | image = Vips::Image.new_from_file ARGV[0] 6 | 7 | module Vips 8 | class Image 9 | def wobble 10 | # this makes an image where pixel (0, 0) (at the top-left) has 11 | # value [0, 0], and pixel (image.width, image.height) at the 12 | # bottom-right has value [image.width, image.height] 13 | index = Vips::Image.xyz width, height 14 | 15 | # make a version with (0, 0) at the centre, negative values up 16 | # and left, positive down and right 17 | centre = index - [width / 2, height / 2] 18 | 19 | # to polar space, so each pixel is now distance and angle in degrees 20 | polar = centre.polar 21 | 22 | # scale sin(distance) by 1/distance to make a wavey pattern 23 | d = ((polar[0] * 3).sin * 10000) / (polar[0] + 1) 24 | 25 | # and back to rectangular coordinates again to make a set of 26 | # vectors we can apply to the original index image 27 | index += d.bandjoin(polar[1]).rect 28 | 29 | # finally, use our modified index image to distort! 30 | mapim index 31 | end 32 | end 33 | end 34 | 35 | image = image.wobble 36 | image.write_to_file ARGV[1] 37 | -------------------------------------------------------------------------------- /lib/vips8.rb: -------------------------------------------------------------------------------- 1 | # This module provides a set of overrides for the vips image processing library 2 | # used via the gobject-introspection gem. 3 | # 4 | # Author:: John Cupitt (mailto:jcupitt@gmail.com) 5 | # License:: MIT 6 | 7 | # @private 8 | def log str 9 | if $vips_debug 10 | puts str 11 | end 12 | end 13 | 14 | # copied from ruby-gnome2/gstreamer/lib/gst.rb without much understanding 15 | 16 | require 'pathname' 17 | require 'gobject-introspection' 18 | 19 | # pick up a local girepository/lib in preference to the system one 20 | base_dir = Pathname.new(__FILE__).dirname.dirname.expand_path 21 | vendor_dir = base_dir + "vendor" + "local" 22 | vendor_bin_dir = vendor_dir + "bin" 23 | GLib.prepend_dll_path(vendor_bin_dir) 24 | vendor_girepository_dir = vendor_dir + "lib" + "girepository-1.0" 25 | GObjectIntrospection.prepend_typelib_path(vendor_girepository_dir) 26 | 27 | module Vips 28 | # @private 29 | LOG_DOMAIN = "VIPS" 30 | GLib::Log.set_log_domain(LOG_DOMAIN) 31 | 32 | # about as crude as you could get 33 | $vips_debug = false 34 | 35 | # Turn debug logging on and off. 36 | # 37 | # @param dbg [Boolean] Set true to print debug log messages 38 | def self.set_debug dbg 39 | $vips_debug = dbg 40 | end 41 | 42 | class << self 43 | # @private 44 | def const_missing(name) 45 | log "Vips::const_missing: #{name}" 46 | 47 | init() 48 | if const_defined?(name) 49 | const_get(name) 50 | else 51 | super 52 | end 53 | end 54 | 55 | # @private 56 | def method_missing(name, *args, &block) 57 | log "Vips::method_missing: #{name}, #{args}, #{block}" 58 | 59 | init() 60 | if respond_to?(name) 61 | __send__(name, *args, &block) 62 | else 63 | super 64 | end 65 | end 66 | 67 | # @private 68 | def init(*argv) 69 | log "Vips::init: #{argv}" 70 | 71 | class << self 72 | remove_method(:init) 73 | remove_method(:const_missing) 74 | remove_method(:method_missing) 75 | end 76 | 77 | loader = Loader.new(self, argv) 78 | begin 79 | loader.load("Vips") 80 | rescue 81 | puts "Unable to load Vips" 82 | puts " Check that the vips library has been installed and is" 83 | puts " on your library path." 84 | puts " Check that the typelib `Vips-8.0.typelib` has been " 85 | puts " installed, and that it is on your GI_TYPELIB_PATH." 86 | raise 87 | end 88 | 89 | require 'vips8/error' 90 | require 'vips8/argument' 91 | require 'vips8/operation' 92 | require 'vips8/call' 93 | require 'vips8/image' 94 | end 95 | end 96 | 97 | # @private 98 | class Loader < GObjectIntrospection::Loader 99 | def initialize(base_module, init_arguments) 100 | log "Vips::Loader.initialize: #{base_module}, #{init_arguments}" 101 | 102 | super(base_module) 103 | @init_arguments = init_arguments 104 | end 105 | 106 | private 107 | def pre_load(repository, namespace) 108 | log "Vips::Loader.pre_load: #{repository}, #{namespace}" 109 | 110 | call_init_function(repository, namespace) 111 | define_value_modules 112 | end 113 | 114 | def call_init_function(repository, namespace) 115 | log "Vips::Loader.call_init_function: #{repository}, #{namespace}" 116 | 117 | # call Vips::init 118 | init = repository.find(namespace, "init") 119 | succeeded, argv, error = init.invoke(:arguments => [$PROGRAM_NAME]) 120 | 121 | # TODO get the vips error buffer 122 | raise error unless succeeded 123 | end 124 | 125 | def define_value_modules 126 | @value_functions_module = Module.new 127 | @value_methods_module = Module.new 128 | @base_module.const_set("ValueFunctions", @value_functions_module) 129 | @base_module.const_set("ValueMethods", @value_methods_module) 130 | end 131 | 132 | def post_load(repository, namespace) 133 | log "Vips::Loader.post_load:" 134 | end 135 | 136 | end 137 | end 138 | 139 | at_exit { 140 | Vips::shutdown if Vips.respond_to? :shutdown 141 | } 142 | 143 | # this makes vips keep a list of all active objects which we can print out 144 | Vips::leak_set true if $vips_debug 145 | 146 | # @private 147 | def showall 148 | if $vips_debug 149 | GC.start 150 | Vips::Object::print_all 151 | end 152 | end 153 | 154 | -------------------------------------------------------------------------------- /lib/vips8/access.rb: -------------------------------------------------------------------------------- 1 | module Vips 2 | # The type of access an operation has to supply. 3 | # 4 | # * `:random` means requests can come in any order. 5 | # 6 | # * `:sequential` means requests will be top-to-bottom, but with some 7 | # amount of buffering behind the read point for small non-local 8 | # accesses. 9 | # 10 | # * `:sequential_unbuffered` means requests will be strictly 11 | # top-to-bottom with no read-behind. This can save some memory. 12 | class Access 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/vips8/align.rb: -------------------------------------------------------------------------------- 1 | module Vips 2 | 3 | # Various types of alignment. See {Vips::Image.join}, for example. 4 | # 5 | # * `:low` Align on the low coordinate edge 6 | # * `:centre` Align on the centre 7 | # * `:high` Align on the high coordinate edge 8 | 9 | class Align 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/vips8/angle.rb: -------------------------------------------------------------------------------- 1 | module Vips 2 | 3 | # Various fixed 90 degree rotation angles. See {Vips::Image.rot}. 4 | # 5 | # * `:d0` no rotate 6 | # * `:d90` 90 degrees clockwise 7 | # * `:d180` 180 degrees 8 | # * `:d270` 90 degrees anti-clockwise 9 | 10 | class Angle 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/vips8/angle45.rb: -------------------------------------------------------------------------------- 1 | module Vips 2 | 3 | # Various fixed 45 degree rotation angles. See {Vips::Image.rot45}. 4 | # 5 | # * `:d0` no rotate 6 | # * `:d45` 45 degrees clockwise 7 | # * `:d90` 90 degrees clockwise 8 | # * `:d135` 135 degrees clockwise 9 | # * `:d180` 180 degrees 10 | # * `:d225` 135 degrees anti-clockwise 11 | # * `:d270` 90 degrees anti-clockwise 12 | # * `:d315` 45 degrees anti-clockwise 13 | 14 | class Angle45 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/vips8/argument.rb: -------------------------------------------------------------------------------- 1 | 2 | module Vips 3 | 4 | # This class is used internally to convert Ruby values to arguments to 5 | # libvips operations. 6 | # @private 7 | class Argument 8 | attr_reader :op, :name, :flags, :priority, :isset, :prop 9 | attr_reader :blurb, :gtype, :type 10 | 11 | # map gobject-introspection's ruby class names to ours 12 | @@map_goi_to_vips = { 13 | "TrueClass" => "Boolean", 14 | "Vips::ArrayDouble" => "Array", 15 | "Vips::ArrayInt" => "Array", 16 | "Vips::ArrayImage" => "Array", 17 | "Vips::ArrayString" => "Array", 18 | } 19 | 20 | def initialize(op, name) 21 | @op = op 22 | @name = name.tr '-', '_' 23 | @prop = op.gtype.to_class.property name 24 | @blurb = @prop.blurb 25 | @gtype = prop.value_type 26 | @flags = op.get_argument_flags name 27 | @priority = op.get_argument_priority @name 28 | @isset = op.argument_isset @name 29 | 30 | type = GLib::Type[gtype.name].to_class.name 31 | type = @@map_goi_to_vips[type] if @@map_goi_to_vips.include? type 32 | @type = type 33 | end 34 | 35 | private 36 | 37 | def self.imageize match_image, value 38 | return value if match_image == nil 39 | return value if value.is_a? Vips::Image 40 | 41 | # 2D array values become tiny 2D images 42 | if value.is_a? Array and value[0].is_a? Array 43 | return Vips::Image.new_from_array value 44 | end 45 | 46 | # if there's nothing to match to, we also make a 2D image 47 | if match_image == nil 48 | return Vips::Image.new_from_array value 49 | end 50 | 51 | # we have a 1D array ... use that as a pixel constant and expand 52 | # to match match_image 53 | pixel = (Vips::Image.black(1, 1) + value).cast(match_image.format) 54 | pixel = pixel.copy :interpretation => match_image.interpretation, 55 | :xres => match_image.xres, :yres => match_image.yres 56 | pixel.embed(0, 0, match_image.width, match_image.height, 57 | :extend => :copy) 58 | end 59 | 60 | # @private 61 | class ArrayImageConst < Vips::ArrayImage 62 | def self.new(value) 63 | if not value.is_a? Array 64 | value = [value] 65 | end 66 | 67 | match_image = value.find {|x| x.is_a? Vips::Image} 68 | if match_image == nil 69 | raise Vips::Error, 70 | "Argument must contain at least one image." 71 | end 72 | 73 | value = value.map {|x| Argument::imageize match_image, x} 74 | 75 | # we'd like to just 76 | # super(value) 77 | # to construct, but the gobject-introspection gem does not 78 | # support new from object array ... instead, we build in stages 79 | array = Vips::ArrayImage.empty 80 | value.each {|x| array = array.append(x)} 81 | 82 | return array 83 | end 84 | end 85 | 86 | # if this gtype needs an array, try to transform the value into one 87 | def self.arrayize(gtype, value) 88 | arrayize_map = { 89 | GLib::Type["VipsArrayDouble"] => Vips::ArrayDouble, 90 | GLib::Type["VipsArrayInt"] => Vips::ArrayInt, 91 | GLib::Type["VipsArrayImage"] => ArrayImageConst 92 | } 93 | 94 | if arrayize_map.has_key? gtype 95 | if not value.is_a? Array 96 | value = [value] 97 | end 98 | 99 | value = arrayize_map[gtype].new(value) 100 | end 101 | 102 | value 103 | end 104 | 105 | def self.unwrap value 106 | [Vips::Blob, Vips::ArrayDouble, Vips::ArrayImage, 107 | Vips::ArrayInt, Vips::RefString].each do |cls| 108 | if value.is_a? cls 109 | value, length = value.get 110 | 111 | # blobs come from gobject-introspection as arrays ... 112 | # repack as strings for convenience 113 | if value and cls == Vips::Blob 114 | value = value.pack("C*") 115 | end 116 | 117 | end 118 | 119 | end 120 | 121 | value 122 | end 123 | 124 | public 125 | 126 | def set_value(match_image, value) 127 | # array-ize 128 | value = Argument::arrayize gtype, value 129 | 130 | # blob-ize 131 | if gtype.type_is_a? GLib::Type["VipsBlob"] 132 | if not value.is_a? Vips::Blob 133 | value = Vips::Blob.copy value 134 | end 135 | end 136 | 137 | # image-ize 138 | if gtype.type_is_a? GLib::Type["VipsImage"] 139 | if not value.is_a? Vips::Image 140 | value = Argument::imageize match_image, value 141 | end 142 | end 143 | 144 | # MODIFY input images need to be copied before assigning them 145 | if (flags & :modify) != 0 146 | # don't use .copy(): we want to make a new pipeline with no 147 | # reference back to the old stuff ... this way we can free the 148 | # previous image earlier 149 | new_image = Vips::Image.memory 150 | value.write new_image 151 | value = new_image 152 | end 153 | 154 | op.set_property @name, value 155 | end 156 | 157 | def get_value 158 | Argument::unwrap @op.get_property(@name) 159 | end 160 | 161 | end 162 | 163 | end 164 | -------------------------------------------------------------------------------- /lib/vips8/bandformat.rb: -------------------------------------------------------------------------------- 1 | module Vips 2 | 3 | # The format used for each band element. Each corresponds to a native C type 4 | # for the current machine. 5 | # 6 | # * `:notset` invalid setting 7 | # * `:uchar` unsigned char format 8 | # * `:char` char format 9 | # * `:ushort` unsigned short format 10 | # * `:short` short format 11 | # * `:uint` unsigned int format 12 | # * `:int` int format 13 | # * `:float` float format 14 | # * `:complex` complex (two floats) format 15 | # * `:double` double float format 16 | # * `:dpcomplex` double complex (two double) format 17 | class BandFormat 18 | end 19 | 20 | end 21 | -------------------------------------------------------------------------------- /lib/vips8/call.rb: -------------------------------------------------------------------------------- 1 | module Vips 2 | 3 | # call a vips operation ... this will crash if there's a GC during the call, 4 | # I've really no idea why :-( 5 | # 6 | # Workaround: don't call this directly, use call_base (see below) instead. 7 | # This will disable the GC, call this operation, then reenable it. 8 | 9 | private 10 | def self.call_base_nogc(name, instance, option_string, supplied_values) 11 | log "in Vips::call_base" 12 | log "name = #{name}" 13 | log "instance = #{instance}" 14 | log "option_string = #{option_string}" 15 | log "supplied_values are:" 16 | supplied_values.each {|x| log " #{x}"} 17 | 18 | if supplied_values.last.is_a? Hash 19 | optional_values = supplied_values.last 20 | supplied_values.delete_at -1 21 | else 22 | optional_values = {} 23 | end 24 | 25 | begin 26 | op = Vips::Operation.new name 27 | rescue 28 | raise Vips::Error, "no operator '#{name}'" 29 | end 30 | 31 | # set string options first 32 | log "setting string options ..." 33 | if option_string 34 | if op.set_from_string(option_string) != 0 35 | raise Error 36 | end 37 | end 38 | 39 | all_args = op.get_args 40 | 41 | # the instance, if supplied, must be a vips image ... we use it for 42 | # match_image, below 43 | if instance and not instance.is_a? Vips::Image 44 | raise Vips::Error, "@instance is not a Vips::Image." 45 | end 46 | 47 | # if the op needs images but the user supplies constants, we expand 48 | # them to match the first input image argument ... find the first 49 | # image 50 | log "searching for first image argument ..." 51 | match_image = instance 52 | if match_image == nil 53 | match_image = supplied_values.find {|x| x.is_a? Vips::Image} 54 | end 55 | if match_image == nil 56 | match = optional_values.find do |name, value| 57 | value.is_a? Vips::Image 58 | end 59 | # if we found a match, it'll be [name, value] 60 | if match 61 | match_image = match[1] 62 | end 63 | end 64 | 65 | # find unassigned required input args 66 | log "finding unassigned required input arguments ..." 67 | required_input = all_args.select do |arg| 68 | not arg.isset and 69 | (arg.flags & :input) != 0 and 70 | (arg.flags & :required) != 0 71 | end 72 | 73 | # do we have a non-nil instance? set the first image arg with this 74 | if instance != nil 75 | log "setting first image arg with instance ..." 76 | x = required_input.find do |arg| 77 | gtype = GLib::Type["VipsImage"] 78 | vtype = arg.prop.value_type 79 | 80 | vtype.type_is_a? gtype 81 | end 82 | if x == nil 83 | raise Vips::Error, 84 | "No #{instance.class} argument to #{name}." 85 | end 86 | x.set_value match_image, instance 87 | required_input.delete x 88 | end 89 | 90 | if required_input.length != supplied_values.length 91 | raise Vips::Error, 92 | "Wrong number of arguments. '#{name}' requires " + 93 | "#{required_input.length} arguments, you supplied " + 94 | "#{supplied_values.length}." 95 | end 96 | 97 | log "setting required input arguments ..." 98 | required_input.zip(supplied_values).each do |arg, value| 99 | arg.set_value match_image, value 100 | end 101 | 102 | # find optional unassigned input args 103 | log "finding optional unassigned input arguments ..." 104 | optional_input = all_args.select do |arg| 105 | not arg.isset and 106 | (arg.flags & :input) != 0 and 107 | (arg.flags & :required) == 0 108 | end 109 | 110 | # make a hash from name to arg 111 | optional_input = Hash[ 112 | optional_input.map(&:name).zip(optional_input)] 113 | 114 | # find optional unassigned output args 115 | log "finding optional unassigned output arguments ..." 116 | optional_output = all_args.select do |arg| 117 | not arg.isset and 118 | (arg.flags & :output) != 0 and 119 | (arg.flags & :required) == 0 120 | end 121 | optional_output = Hash[ 122 | optional_output.map(&:name).zip(optional_output)] 123 | 124 | # set all optional args 125 | log "setting optional values ..." 126 | optional_values.each do |name, value| 127 | # we are passed symbols as keys 128 | name = name.to_s 129 | if optional_input.has_key? name 130 | log "setting #{name} to #{value}" 131 | optional_input[name].set_value match_image, value 132 | elsif optional_output.has_key? name and value != true 133 | raise Vips::Error, 134 | "Optional output argument #{name} must be true." 135 | elsif not optional_output.has_key? name 136 | raise Vips::Error, "No such option '#{name}'," 137 | end 138 | end 139 | 140 | log "building ..." 141 | 142 | op2 = Vips::cache_operation_lookup op 143 | if op2 144 | log "cache hit" 145 | op = op2 146 | 147 | all_args = op.get_args 148 | 149 | # find optional output args 150 | optional_output = all_args.select do |arg| 151 | (arg.flags & :output) != 0 and 152 | (arg.flags & :required) == 0 153 | end 154 | optional_output = Hash[ 155 | optional_output.map(&:name).zip(optional_output)] 156 | else 157 | log "cache miss ... building" 158 | if not op.build 159 | raise Vips::Error 160 | end 161 | showall 162 | 163 | log "adding to cache ... " 164 | Vips::cache_operation_add op 165 | end 166 | 167 | log "fetching outputs ..." 168 | 169 | # gather output args 170 | out = [] 171 | 172 | all_args.each do |arg| 173 | # required output 174 | if (arg.flags & :output) != 0 and 175 | (arg.flags & :required) != 0 176 | log "fetching required output #{arg.name}" 177 | out << arg.get_value 178 | end 179 | 180 | # modified input arg ... this will get the result of the 181 | # copy() we did in Argument.set_value above 182 | if (arg.flags & :input) != 0 and 183 | (arg.flags & :modify) != 0 184 | log "fetching modified input arg ..." 185 | out << arg.get_value 186 | end 187 | end 188 | 189 | opts = {} 190 | optional_values.each do |name, value| 191 | # we are passed symbols as keys 192 | name = name.to_s 193 | if optional_output.has_key? name 194 | log "fetching optional output arg ..." 195 | opts[name] = optional_output[name].get_value 196 | end 197 | end 198 | out << opts if opts != {} 199 | 200 | if out.length == 1 201 | out = out[0] 202 | elsif out.length == 0 203 | out = nil 204 | end 205 | 206 | log "unreffing outputs ..." 207 | op.unref_outputs 208 | op = nil 209 | # showall 210 | 211 | log "success! #{name}.out = #{out}" 212 | 213 | return out 214 | end 215 | 216 | # run call_base_nogc, with the GC disabled 217 | private 218 | def self.call_base(name, instance, option_string, supplied_values) 219 | gc_was_enabled = GC.disable 220 | begin 221 | result = call_base_nogc name, instance, 222 | option_string, supplied_values 223 | ensure 224 | GC.enable if gc_was_enabled 225 | end 226 | 227 | return result 228 | end 229 | 230 | public 231 | 232 | # This is the public entry point for the vips8 binding. {call} will run 233 | # any vips operation, for example: 234 | # 235 | # ```ruby 236 | # out = Vips::call "black", 100, 100, :bands => 12 237 | # ``` 238 | # 239 | # will call the C function 240 | # 241 | # ```C 242 | # vips_black( &out, 100, 100, "bands", 12, NULL ); 243 | # ``` 244 | # 245 | # There are {Image#method_missing} hooks which will run {call} for you 246 | # on {Image} for undefined instance or class methods. So you can also 247 | # write: 248 | # 249 | # ```ruby 250 | # out = Vips::Image.black 100, 100, :bands => 12 251 | # ``` 252 | # 253 | # Or perhaps: 254 | # 255 | # ```ruby 256 | # x = Vips::Image.black 100, 100 257 | # y = x.invert 258 | # ``` 259 | # 260 | # to run the `vips_invert()` operator. 261 | # 262 | # There are also a set of operator overloads and some convenience functions, 263 | # see {Image}. 264 | # 265 | # If the operator needs a vector constant, {call} will turn a scalar into a 266 | # vector for you. So for `x.linear(a, b)`, which calculates 267 | # `x * a + b` where `a` and `b` are vector constants, you can write: 268 | # 269 | # ```ruby 270 | # x = Vips::Image.black 100, 100, :bands => 3 271 | # y = x.linear(1, 2) 272 | # y = x.linear([1], 4) 273 | # y = x.linear([1, 2, 3], 4) 274 | # ``` 275 | # 276 | # or any other combination. The operator overloads use this facility to 277 | # support all the variations on: 278 | # 279 | # ```ruby 280 | # x = Vips::Image.black 100, 100, :bands => 3 281 | # y = x * 2 282 | # y = x + [1,2,3] 283 | # y = x % [1] 284 | # ``` 285 | # 286 | # Similarly, whereever an image is required, you can use a constant. The 287 | # constant will be expanded to an image matching the first input image 288 | # argument. For example, you can write: 289 | # 290 | # ``` 291 | # x = Vips::Image.black 100, 100, :bands => 3 292 | # y = x.bandjoin(255) 293 | # ``` 294 | # 295 | # to add an extra band to the image where each pixel in the new band has 296 | # the constant value 255. 297 | 298 | def self.call(name, *args) 299 | Vips::call_base name, nil, "", args 300 | end 301 | 302 | end 303 | -------------------------------------------------------------------------------- /lib/vips8/coding.rb: -------------------------------------------------------------------------------- 1 | module Vips 2 | 3 | # How pixels are coded. 4 | # 5 | # Normally, pixels are uncoded and can be manipulated as you would expect. 6 | # However some file formats code pixels for compression, and sometimes it's 7 | # useful to be able to manipulate images in the coded format. 8 | # 9 | # * `:none` pixels are not coded 10 | # * `:labq` pixels encode 3 float CIELAB values as 4 uchar 11 | # * `:rad` pixels encode 3 float RGB as 4 uchar (Radiance coding) 12 | class Coding 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/vips8/demandstyle.rb: -------------------------------------------------------------------------------- 1 | module Vips 2 | 3 | # Operations can hint to the VIPS image IO 4 | # system about the kind of demand geometry they prefer. 5 | # 6 | # These demand styles are given below in order of increasing 7 | # restrictiveness. When demanding output from a pipeline, 8 | # vips_image_generate() 9 | # will use the most restrictive of the styles requested by the operations 10 | # in the pipeline. 11 | # 12 | # * `:thinstrip` --- This operation would like to output strips 13 | # the width of the image and a few pels high. This is option suitable 14 | # for point-to-point operations, such as those in the arithmetic 15 | # package. 16 | # 17 | # This option is only efficient for cases where each output pel depends 18 | # upon the pel in the corresponding position in the input image. 19 | # 20 | # * `:fatstrip` --- This operation would like to output strips 21 | # the width of the image and as high as possible. This option is 22 | # suitable for area operations which do not violently transform 23 | # coordinates, such as vips_conv(). 24 | # 25 | # * `:smalltile` --- This is the most general demand format. 26 | # Output is demanded in small (around 100x100 pel) sections. This style 27 | # works reasonably efficiently, even for bizzare operations like 45 28 | # degree rotate. 29 | # 30 | # * `:any` --- This image is not being demand-read from a disc 31 | # file (even indirectly) so any demand style is OK. It's used for 32 | # things like vips_black() where the pixels are calculated. 33 | class DemandStyle 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/vips8/direction.rb: -------------------------------------------------------------------------------- 1 | module Vips 2 | 3 | # Operations like {Vips::Image.flip} need to be told whether to flip 4 | # left-right or top-bottom. 5 | # 6 | # * `:horizontal` left-right 7 | # * `:vertical` top-bottom 8 | 9 | class Direction 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/vips8/error.rb: -------------------------------------------------------------------------------- 1 | module Vips 2 | 3 | # The ruby-vips8 error class. 4 | class Error < RuntimeError 5 | # @param msg [String] The error message. If this is not supplied, grab 6 | # and clear the vips error buffer and use that. 7 | def initialize(msg = nil) 8 | if msg 9 | @details = msg 10 | elsif Vips::error_buffer != "" 11 | @details = Vips::error_buffer 12 | Vips::error_clear 13 | else 14 | @details = nil 15 | end 16 | end 17 | 18 | # Pretty-print a {Vips::Error}. 19 | # 20 | # @return [String] The error message 21 | def to_s 22 | if @details != nil 23 | @details 24 | else 25 | super.to_s 26 | end 27 | end 28 | end 29 | 30 | end 31 | -------------------------------------------------------------------------------- /lib/vips8/extend.rb: -------------------------------------------------------------------------------- 1 | module Vips 2 | 3 | # When the edges of an image are extended, you can specify 4 | # how you want the extension done. 5 | # See {Vips::Image.embed}, {Vips::Image.conv}, {Vips::Image.affine} and 6 | # so on. 7 | # 8 | # * `:black` new pixels are black, ie. all bits are zero. 9 | # 10 | # * `:copy` each new pixel takes the value of the nearest edge pixel 11 | # 12 | # * `:repeat` the image is tiled to fill the new area 13 | # 14 | # * `:mirror` the image is reflected and tiled to reduce hash edges 15 | # 16 | # * `:white` new pixels are white, ie. all bits are set 17 | # 18 | # * `:background` colour set from the @background property 19 | 20 | class Extend 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/vips8/foreignflags.rb: -------------------------------------------------------------------------------- 1 | module Vips 2 | 3 | # Some hints about the image loader. 4 | # 5 | # * `:partial` means that the image can be read directly from the 6 | # file without needing to be unpacked to a temporary image first. 7 | # 8 | # * `:sequential` means that the loader supports lazy reading, but 9 | # only top-to-bottom (sequential) access. Formats like PNG can read 10 | # sets of scanlines, for example, but only in order. 11 | # 12 | # If neither partial` or sequential` is set, the loader only supports 13 | # whole image read. Setting both partial` and sequential` is an error. 14 | # 15 | # * `:bigendian` means that image pixels are most-significant byte 16 | # first. Depending on the native byte order of the host machine, you may 17 | # need to swap bytes. See vips_copy(). 18 | class ForeignFlags 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/vips8/image.rb: -------------------------------------------------------------------------------- 1 | # This module provides a set of overrides for the [vips image processing 2 | # library](http://www.vips.ecs.soton.ac.uk) 3 | # used via the [gobject-introspection 4 | # gem](https://rubygems.org/gems/gobject-introspection). 5 | # 6 | # It needs vips-8.2 or later to be installed, 7 | # and `Vips-8.0.typelib`, the vips typelib, needs to be on your 8 | # `GI_TYPELIB_PATH`. 9 | # 10 | # # Example 11 | # 12 | # ```ruby 13 | # require 'vips8' 14 | # 15 | # if ARGV.length < 2 16 | # raise "usage: #{$PROGRAM_NAME}: input-file output-file" 17 | # end 18 | # 19 | # im = Vips::Image.new_from_file ARGV[0], :access => :sequential 20 | # 21 | # im *= [1, 2, 1] 22 | # 23 | # mask = Vips::Image.new_from_array [ 24 | # [-1, -1, -1], 25 | # [-1, 16, -1], 26 | # [-1, -1, -1]], 8 27 | # im = im.conv mask 28 | # 29 | # im.write_to_file ARGV[1] 30 | # ``` 31 | # 32 | # This example loads a file, boosts the green channel (I'm not sure why), 33 | # sharpens the image, and saves it back to disc again. 34 | # 35 | # Reading this example line by line, we have: 36 | # 37 | # ```ruby 38 | # im = Vips::Image.new_from_file ARGV[0], :access => :sequential 39 | # ``` 40 | # 41 | # {Image.new_from_file} can load any image file supported by vips. In this 42 | # example, we will be accessing pixels top-to-bottom as we sweep through the 43 | # image reading and writing, so `:sequential` access mode is best for us. The 44 | # default mode is `:random`, this allows for full random access to image pixels, 45 | # but is slower and needs more memory. See {Access} 46 | # for full details 47 | # on the various modes available. You can also load formatted images from 48 | # memory buffers or create images that wrap C-style memory arrays. 49 | # 50 | # The next line: 51 | # 52 | # ```ruby 53 | # im *= [1, 2, 1] 54 | # ``` 55 | # 56 | # Multiplying the image by an array constant uses one array element for each 57 | # image band. This line assumes that the input image has three bands and will 58 | # double the middle band. For RGB images, that's doubling green. 59 | # 60 | # Next we have: 61 | # 62 | # ```ruby 63 | # mask = Vips::Image.new_from_array [ 64 | # [-1, -1, -1], 65 | # [-1, 16, -1], 66 | # [-1, -1, -1]], 8 67 | # im = im.conv mask 68 | # ``` 69 | # 70 | # {Image.new_from_array} creates an image from an array constant. The 8 at 71 | # the end sets the scale: the amount to divide the image by after 72 | # integer convolution. See the libvips API docs for `vips_conv()` (the operation 73 | # invoked by {Vips::Image.conv}) for details on the convolution operator. 74 | # 75 | # Finally: 76 | # 77 | # ```ruby 78 | # im.write_to_file ARGV[1] 79 | # ``` 80 | # 81 | # {Vips::Image.write_to_file} writes an image back to the filesystem. It can 82 | # write any format supported by vips: the file type is set from the filename 83 | # suffix. You can also write formatted images to memory buffers, or dump 84 | # image data to a raw memory array. 85 | # 86 | # # How it works 87 | # 88 | # The C sources to libvips include a set of specially formatted 89 | # comments which describe its interfaces. When you compile the library, 90 | # gobject-introspection generates `Vips-8.0.typelib`, a file 91 | # describing how to use libvips. 92 | # 93 | # The `gobject-introspection` gem loads this typelib and uses it to let you 94 | # call 95 | # functions in libvips directly from Ruby. However, the interface you get 96 | # from raw gobject-introspection is rather ugly, so the `ruby-vips8` gem 97 | # adds a set 98 | # of overrides which try to make it nicer to use. 99 | # 100 | # The API you end up with is a Ruby-ish version of the [VIPS C 101 | # API](http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/). 102 | # Full documentation 103 | # on the operations and what they do is there, you can use it directly. This 104 | # document explains the extra features of the Ruby API and lists the available 105 | # operations very briefly. 106 | # 107 | # # Automatic wrapping 108 | # 109 | # `ruby-vips8` adds a {Image.method_missing} handler to {Image} and uses 110 | # it to look up vips operations. For example, the libvips operation `add`, which 111 | # appears in C as `vips_add()`, appears in Ruby as {Vips::Image.add}. 112 | # 113 | # The operation's list of required arguments is searched and the first input 114 | # image is set to the value of `self`. Operations which do not take an input 115 | # image, such as {Image.black}, appear as class methods. The remainder of 116 | # the arguments you supply in the function call are used to set the other 117 | # required input arguments. If the final supplied argument is a hash, it is used 118 | # to set any optional input arguments. The result is the required output 119 | # argument if there is only one result, or an array of values if the operation 120 | # produces several results. If the operation has optional output objects, they 121 | # are returned as a final hash. 122 | # 123 | # For example, {Vips::Image.min}, the vips operation that searches an image for 124 | # the minimum value, has a large number of optional arguments. You can use it to 125 | # find the minimum value like this: 126 | # 127 | # ```ruby 128 | # min_value = image.min 129 | # ``` 130 | # 131 | # You can ask it to return the position of the minimum with `:x` and `:y`. 132 | # 133 | # ```ruby 134 | # min_value, opts = min :x => true, :y => true 135 | # x_pos = opts['x'] 136 | # y_pos = opts['y'] 137 | # ``` 138 | # 139 | # Now `x_pos` and `y_pos` will have the coordinates of the minimum value. 140 | # There's actually a convenience function for this, {Vips::Image.minpos}. 141 | # 142 | # You can also ask for the top *n* minimum, for example: 143 | # 144 | # ```ruby 145 | # min_value, opts = min :size => 10, :x_array => true, :y_array => true 146 | # x_pos = opts['x_array'] 147 | # y_pos = opts['y_array'] 148 | # ``` 149 | # 150 | # Now `x_pos` and `y_pos` will be 10-element arrays. 151 | # 152 | # Because operations are member functions and return the result image, you can 153 | # chain them. For example, you can write: 154 | # 155 | # ```ruby 156 | # result_image = image.real.cos 157 | # ``` 158 | # 159 | # to calculate the cosine of the real part of a complex image. 160 | # There are also a full set 161 | # of arithmetic operator overloads, see below. 162 | # 163 | # libvips types are also automatically wrapped. The override looks at the type 164 | # of argument required by the operation and converts the value you supply, 165 | # when it can. For example, {Vips::Image.linear} takes a `VipsArrayDouble` as 166 | # an argument 167 | # for the set of constants to use for multiplication. You can supply this 168 | # value as an integer, a float, or some kind of compound object and it 169 | # will be converted for you. You can write: 170 | # 171 | # ```ruby 172 | # result_image = image.linear 1, 3 173 | # result_image = image.linear 12.4, 13.9 174 | # result_image = image.linear [1, 2, 3], [4, 5, 6] 175 | # result_image = image.linear 1, [4, 5, 6] 176 | # ``` 177 | # 178 | # And so on. A set of overloads are defined for {Vips::Image.linear}, see below. 179 | # 180 | # It does a couple of more ambitious conversions. It will automatically convert 181 | # to and from the various vips types, like `VipsBlob` and `VipsArrayImage`. For 182 | # example, you can read the ICC profile out of an image like this: 183 | # 184 | # ```ruby 185 | # profile = im.get_value "icc-profile-data" 186 | # ``` 187 | # 188 | # and profile will be a byte array. 189 | # 190 | # If an operation takes several input images, you can use a constant for all but 191 | # one of them and the wrapper will expand the constant to an image for you. For 192 | # example, {Vips::Image.ifthenelse} uses a condition image to pick pixels 193 | # between a then and an else image: 194 | # 195 | # ```ruby 196 | # result_image = condition_image.ifthenelse then_image, else_image 197 | # ``` 198 | # 199 | # You can use a constant instead of either the then or the else parts and it 200 | # will be expanded to an image for you. If you use a constant for both then and 201 | # else, it will be expanded to match the condition image. For example: 202 | # 203 | # ```ruby 204 | # result_image = condition_image.ifthenelse [0, 255, 0], [255, 0, 0] 205 | # ``` 206 | # 207 | # Will make an image where true pixels are green and false pixels are red. 208 | # 209 | # This is useful for {Vips::Image.bandjoin}, the thing to join two or more 210 | # images up bandwise. You can write: 211 | # 212 | # ```ruby 213 | # rgba = rgb.bandjoin 255 214 | # ``` 215 | # 216 | # to append a constant 255 band to an image, perhaps to add an alpha channel. Of 217 | # course you can also write: 218 | # 219 | # ```ruby 220 | # result_image = image1.bandjoin image2 221 | # result_image = image1.bandjoin [image2, image3] 222 | # result_image = Vips::Image.bandjoin [image1, image2, image3] 223 | # result_image = image1.bandjoin [image2, 255] 224 | # ``` 225 | # 226 | # and so on. 227 | # 228 | # # Automatic YARD documentation 229 | # 230 | # The bulk of these API docs are generated automatically by 231 | # {Vips::generate_yard}. It examines 232 | # libvips and writes a summary of each operation and the arguments and options 233 | # that operation expects. 234 | # 235 | # Use the [C API 236 | # docs](http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips) 237 | # for more detail. 238 | # 239 | # # Exceptions 240 | # 241 | # The wrapper spots errors from vips operations and raises the {Vips::Error} 242 | # exception. You can catch it in the usual way. 243 | # 244 | # # Draw operations 245 | # 246 | # Paint operations like {Vips::Image.draw_circle} and {Vips::Image.draw_line} 247 | # modify their input image. This 248 | # makes them hard to use with the rest of libvips: you need to be very careful 249 | # about the order in which operations execute or you can get nasty crashes. 250 | # 251 | # The wrapper spots operations of this type and makes a private copy of the 252 | # image in memory before calling the operation. This stops crashes, but it does 253 | # make it inefficient. If you draw 100 lines on an image, for example, you'll 254 | # copy the image 100 times. The wrapper does make sure that memory is recycled 255 | # where possible, so you won't have 100 copies in memory. 256 | # 257 | # If you want to avoid the copies, you'll need to call drawing operations 258 | # yourself. 259 | # 260 | # # Overloads 261 | # 262 | # The wrapper defines the usual set of arithmetic, boolean and relational 263 | # overloads on image. You can mix images, constants and lists of constants 264 | # (almost) freely. For example, you can write: 265 | # 266 | # ```ruby 267 | # result_image = ((image * [1, 2, 3]).abs < 128) | 4 268 | # ``` 269 | # 270 | # # Expansions 271 | # 272 | # Some vips operators take an enum to select an action, for example 273 | # {Vips::Image.math} can be used to calculate sine of every pixel like this: 274 | # 275 | # ```ruby 276 | # result_image = image.math :sin 277 | # ``` 278 | # 279 | # This is annoying, so the wrapper expands all these enums into separate members 280 | # named after the enum. So you can write: 281 | # 282 | # ```ruby 283 | # result_image = image.sin 284 | # ``` 285 | # 286 | # # Convenience functions 287 | # 288 | # The wrapper defines a few extra useful utility functions: 289 | # {Vips::Image.get_value}, {Vips::Image.set_value}, {Vips::Image.bandsplit}, 290 | # {Vips::Image.maxpos}, {Vips::Image.minpos}, 291 | # {Vips::Image.median}. 292 | 293 | module Vips 294 | 295 | # This class represents a libvips image. See the {Vips} module documentation 296 | # for an introduction to using this module. 297 | 298 | class Image 299 | private 300 | 301 | # handy for overloads ... want to be able to apply a function to an 302 | # array or to a scalar 303 | def self.smap(x, &block) 304 | x.is_a?(Array) ? x.map {|x| smap(x, &block)} : block.(x) 305 | end 306 | 307 | # run a complex operation on a complex image, or an image with an even 308 | # number of bands ... handy for things like running .polar on .index 309 | # images 310 | def self.run_cmplx(image, &block) 311 | original_format = image.format 312 | 313 | if not Vips::band_format_iscomplex image.format 314 | if image.bands % 2 != 0 315 | raise Error, "not an even number of bands" 316 | end 317 | 318 | if not Vips::band_format_isfloat image.format 319 | image = image.cast :float 320 | end 321 | 322 | new_format = image.format == :double ? :dpcomplex : :complex 323 | image = image.copy :format => new_format, 324 | :bands => image.bands / 2 325 | end 326 | 327 | image = block.(image) 328 | 329 | if not Vips::band_format_iscomplex original_format 330 | new_format = image.format == :dpcomplex ? :double : :float 331 | image = image.copy :format => new_format, 332 | :bands => image.bands * 2 333 | end 334 | 335 | image 336 | end 337 | 338 | # Write can fail due to no file descriptors and memory can fill if 339 | # large objects are not collected fairly soon. We can't try a 340 | # write and GC and retry on fail, since the write may take a 341 | # long time and may not be repeatable. 342 | # 343 | # GCing before every write would have a horrible effect on 344 | # performance, so as a compromise we GC every @@gc_interval writes. 345 | # 346 | # ruby2.1 introduced a generational GC which is fast enough to be 347 | # able to GC on every write. 348 | 349 | @@generational_gc = RUBY_ENGINE == "ruby" && RUBY_VERSION.to_f >= 2.1 350 | 351 | @@gc_interval = 100 352 | @@gc_countdown = @@gc_interval 353 | 354 | def write_gc 355 | if @@generational_gc 356 | GC.start full_mark: false 357 | else 358 | @@gc_countdown -= 1 359 | if @@gc_countdown < 0 360 | @@gc_countdown = @@gc_interval 361 | GC.start 362 | end 363 | end 364 | end 365 | 366 | public 367 | 368 | # Invoke a vips operation with {call}, using self as the first 369 | # input image argument. 370 | # 371 | # @param name [String] vips operation to call 372 | # @return result of vips operation 373 | def method_missing(name, *args) 374 | Vips::call_base(name.to_s, self, "", args) 375 | end 376 | 377 | # Invoke a vips operation with {call}. 378 | def self.method_missing(name, *args) 379 | Vips::call_base name.to_s, nil, "", args 380 | end 381 | 382 | # Return a new {Image} for a file on disc. This method can load 383 | # images in any format supported by vips. The filename can include 384 | # load options, for example: 385 | # 386 | # ``` 387 | # image = Vips::new_from_file "fred.jpg[shrink=2]" 388 | # ``` 389 | # 390 | # You can also supply options as a hash, for example: 391 | # 392 | # ``` 393 | # image = Vips::new_from_file "fred.jpg", :shrink => 2 394 | # ``` 395 | # 396 | # The full set of options available depend upon the load operation that 397 | # will be executed. Try something like: 398 | # 399 | # ``` 400 | # $ vips jpegload 401 | # ``` 402 | # 403 | # at the command-line to see a summary of the available options. 404 | # 405 | # Loading is fast: only enough of the image is loaded to be able to fill 406 | # out the header. Pixels will only be processed when they are needed. 407 | # 408 | # @!macro [new] vips.loadopts 409 | # @param [Hash] opts set of options 410 | # @option opts [Boolean] :disc (true) Open large images via a 411 | # temporary disc file 412 | # @option opts [Vips::Access] :access (:random) Access mode for file 413 | # 414 | # @param name [String] the filename to load from 415 | # @macro vips.loadopts 416 | # @return [Image] the loaded image 417 | def self.new_from_file(name, opts = {}) 418 | # very common, and Vips::filename_get_filename will segv if we pass 419 | # this 420 | if name == nil 421 | raise Error, "filename is nil" 422 | end 423 | filename = Vips::filename_get_filename name 424 | option_string = Vips::filename_get_options name 425 | loader = Vips::Foreign.find_load filename 426 | if loader == nil 427 | raise Vips::Error 428 | end 429 | 430 | Vips::call_base loader, nil, option_string, [filename, opts] 431 | end 432 | 433 | # Create a new {Image} for an image encoded in a format, such as 434 | # JPEG, in a memory string. Load options may be passed encoded as 435 | # strings, or appended as a hash. For example: 436 | # 437 | # ``` 438 | # image = Vips::new_from_from_buffer memory_buffer, "shrink=2" 439 | # ``` 440 | # 441 | # or alternatively: 442 | # 443 | # ``` 444 | # image = Vips::new_from_from_buffer memory_buffer, "", :shrink => 2 445 | # ``` 446 | # 447 | # The options available depend on the file format. Try something like: 448 | # 449 | # ``` 450 | # $ vips jpegload_buffer 451 | # ``` 452 | # 453 | # at the command-line to see the available options. Only JPEG, PNG and 454 | # TIFF images can be read from memory buffers. 455 | # 456 | # Loading is fast: only enough of the image is loaded to be able to fill 457 | # out the header. Pixels will only be processed when they are needed. 458 | # 459 | # @param data [String] the data to load from 460 | # @param option_string [String] load options as a string 461 | # @macro vips.loadopts 462 | # @return [Image] the loaded image 463 | def self.new_from_buffer(data, option_string, opts = {}) 464 | loader = Vips::Foreign.find_load_buffer data 465 | if loader == nil 466 | raise Vips::Error 467 | end 468 | 469 | Vips::call_base loader, nil, option_string, [data, opts] 470 | end 471 | 472 | # Create a new Image from a 1D or 2D array. A 1D array becomes an 473 | # image with height 1. Use `scale` and `offset` to set the scale and 474 | # offset fields in the header. These are useful for integer 475 | # convolutions. 476 | # 477 | # For example: 478 | # 479 | # ``` 480 | # image = Vips::new_from_array [1, 2, 3] 481 | # ``` 482 | # 483 | # or 484 | # 485 | # ``` 486 | # image = Vips::new_from_array [ 487 | # [-1, -1, -1], 488 | # [-1, 16, -1], 489 | # [-1, -1, -1]], 8 490 | # ``` 491 | # 492 | # for a simple sharpening mask. 493 | # 494 | # @param array [Array] the pixel data as an array of numbers 495 | # @param scale [Real] the convolution scale 496 | # @param offset [Real] the convolution offset 497 | # @return [Image] the image 498 | def self.new_from_array(array, scale = 1, offset = 0) 499 | # we accept a 1D array and assume height == 1, or a 2D array 500 | # and check all lines are the same length 501 | if not array.is_a? Array 502 | raise Vips::Error, "Argument is not an array." 503 | end 504 | 505 | if array[0].is_a? Array 506 | height = array.length 507 | width = array[0].length 508 | if not array.all? {|x| x.is_a? Array} 509 | raise Vips::Error, "Not a 2D array." 510 | end 511 | if not array.all? {|x| x.length == width} 512 | raise Vips::Error, "Array not rectangular." 513 | end 514 | array = array.flatten 515 | else 516 | height = 1 517 | width = array.length 518 | end 519 | 520 | if not array.all? {|x| x.is_a? Numeric} 521 | raise Vips::Error, "Not all array elements are Numeric." 522 | end 523 | 524 | image = Vips::Image.matrix_from_array width, height, array 525 | if image == nil 526 | raise Vips::Error 527 | end 528 | 529 | # be careful to set them as double 530 | image.set_double 'scale', scale.to_f 531 | image.set_double 'offset', offset.to_f 532 | 533 | return image 534 | end 535 | 536 | # Write this image to a file. Save options may be encoded in the 537 | # filename or given as a hash. For example: 538 | # 539 | # ``` 540 | # image.write_to_file "fred.jpg[Q=90]" 541 | # ``` 542 | # 543 | # or equivalently: 544 | # 545 | # ``` 546 | # image.write_to_file "fred.jpg", :Q => 90 547 | # ``` 548 | # 549 | # The full set of save options depend on the selected saver. Try 550 | # something like: 551 | # 552 | # ``` 553 | # $ vips jpegsave 554 | # ``` 555 | # 556 | # to see all the available options. 557 | # 558 | # @!macro [new] vips.saveopts 559 | # @param [Hash] opts set of options 560 | # @option opts [Boolean] :strip (false) Strip all metadata from image 561 | # @option opts [Array] :background (0) Background colour to 562 | # flatten alpha against, if necessary 563 | # 564 | # @param name [String] filename to write to 565 | def write_to_file(name, opts = {}) 566 | filename = Vips::filename_get_filename name 567 | option_string = Vips::filename_get_options name 568 | saver = Vips::Foreign.find_save filename 569 | if saver == nil 570 | raise Vips::Error, "No known saver for '#{filename}'." 571 | end 572 | 573 | Vips::call_base saver, self, option_string, [filename, opts] 574 | 575 | write_gc 576 | end 577 | 578 | # Write this image to a memory buffer. Save options may be encoded in 579 | # the format_string or given as a hash. For example: 580 | # 581 | # ``` 582 | # buffer = image.write_to_buffer ".jpg[Q=90]" 583 | # ``` 584 | # 585 | # or equivalently: 586 | # 587 | # ``` 588 | # image.write_to_buffer ".jpg", :Q => 90 589 | # ``` 590 | # 591 | # The full set of save options depend on the selected saver. Try 592 | # something like: 593 | # 594 | # ``` 595 | # $ vips jpegsave 596 | # ``` 597 | # 598 | # to see all the available options. 599 | # 600 | # @param format_string [String] save format plus options 601 | # @macro vips.saveopts 602 | # @return [String] the image saved in the specified format 603 | def write_to_buffer(format_string, opts = {}) 604 | filename = Vips::filename_get_filename format_string 605 | option_string = Vips::filename_get_options format_string 606 | saver = Vips::Foreign.find_save_buffer filename 607 | if saver == nil 608 | raise Vips::Error, "No known saver for '#{filename}'." 609 | end 610 | 611 | buffer = Vips::call_base saver, self, option_string, [opts] 612 | 613 | write_gc 614 | 615 | return buffer 616 | end 617 | 618 | # @!attribute [r] width 619 | # @return [Integer] image width, in pixels 620 | # @!attribute [r] height 621 | # @return [Integer] image height, in pixels 622 | # @!attribute [r] bands 623 | # @return [Integer] image bands 624 | # @!attribute [r] format 625 | # @return [Vips::BandFormat] image format 626 | # @!attribute [r] interpretation 627 | # @return [Vips::Interpretation] image interpretation 628 | # @!attribute [r] coding 629 | # @return [Vips::Coding] image coding 630 | # @!attribute [r] filename 631 | # @return [String] image filename 632 | # @!attribute [r] xres 633 | # @return [Float] horizontal image resolution, in pixels per mm 634 | # @!attribute [r] yres 635 | # @return [Float] vertical image resolution, in pixels per mm 636 | 637 | # Fetch a `GType` from an image. `GType` will be 0 for no such field. 638 | # 639 | # @see get 640 | # @see get_value 641 | # @!method get_typeof(name) 642 | # @param name [String] Metadata field to fetch 643 | # @return [Integer] GType 644 | 645 | # Fetch a `GValue` from an image. The return status is 0 for success, -1 646 | # for failure. 647 | # 648 | # @see get_value 649 | # @see get_typeof 650 | # @!method get(name) 651 | # @param name [String] Metadata field to fetch 652 | # @return [Integer, GValue] Return status, GValue from image 653 | 654 | # Set a `GValue` on an image 655 | # 656 | # @see set_value 657 | # @!method set(name, value) 658 | # @param name [String] Metadata field to set 659 | # @param value [GValue] GValue to set 660 | 661 | # Get a metadata item from an image. Ruby types are constructed 662 | # automatically from the `GValue`, if possible. 663 | # 664 | # For example, you can read the ICC profile from an image like this: 665 | # 666 | # ``` 667 | # profile = image.get_value "icc-profile-data" 668 | # ``` 669 | # 670 | # and profile will be an array containing the profile. 671 | # 672 | # @see get 673 | # @param name [String] Metadata field to set 674 | # @return [Object] Value of field 675 | def get_value(name) 676 | ret, gval = get name 677 | if ret[0] != 0 678 | raise Vips::Error, "Field #{name} not found." 679 | end 680 | value = gval.value 681 | 682 | Argument::unwrap(value) 683 | end 684 | 685 | # Set a metadata item on an image. Ruby types are automatically 686 | # transformed into the matching `GValue`, if possible. 687 | # 688 | # For example, you can use this to set an image's ICC profile: 689 | # 690 | # ``` 691 | # x = y.set_value "icc-profile-data", profile 692 | # ``` 693 | # 694 | # where `profile` is an ICC profile held as a binary string object. 695 | # 696 | # @see set 697 | # @param name [String] Metadata field to set 698 | # @param value [Object] Value to set 699 | def set_value(name, value) 700 | gtype = get_typeof name 701 | if gtype != 0 702 | # array-ize 703 | value = Argument::arrayize gtype, value 704 | 705 | # blob-ize 706 | if gtype.type_is_a? GLib::Type["VipsBlob"] 707 | if not value.is_a? Vips::Blob 708 | value = Vips::Blob.copy value 709 | end 710 | end 711 | 712 | # image-ize 713 | if gtype.type_is_a? GLib::Type["VipsImage"] 714 | if not value.is_a? Vips::Image 715 | value = imageize match_image, value 716 | end 717 | end 718 | 719 | end 720 | 721 | set name, value 722 | end 723 | 724 | # Add an image, constant or array. 725 | # 726 | # @param other [Image, Real, Array] Thing to add to self 727 | # @return [Image] result of addition 728 | def +(other) 729 | other.is_a?(Vips::Image) ? add(other) : linear(1, other) 730 | end 731 | 732 | # Subtract an image, constant or array. 733 | # 734 | # @param other [Image, Real, Array] Thing to subtract from self 735 | # @return [Image] result of subtraction 736 | def -(other) 737 | other.is_a?(Vips::Image) ? 738 | subtract(other) : linear(1, Image::smap(other) {|x| x * -1}) 739 | end 740 | 741 | # Multiply an image, constant or array. 742 | # 743 | # @param other [Image, Real, Array] Thing to multiply by self 744 | # @return [Image] result of multiplication 745 | def *(other) 746 | other.is_a?(Vips::Image) ? multiply(other) : linear(other, 0) 747 | end 748 | 749 | # Divide an image, constant or array. 750 | # 751 | # @param other [Image, Real, Array] Thing to divide self by 752 | # @return [Image] result of division 753 | def /(other) 754 | other.is_a?(Vips::Image) ? 755 | divide(other) : linear(Image::smap(other) {|x| 1.0 / x}, 0) 756 | end 757 | 758 | # Remainder after integer division with an image, constant or array. 759 | # 760 | # @param other [Image, Real, Array] self modulo this 761 | # @return [Image] result of modulo 762 | def %(other) 763 | other.is_a?(Vips::Image) ? 764 | remainder(other) : remainder_const(other) 765 | end 766 | 767 | # Raise to power of an image, constant or array. 768 | # 769 | # @param other [Image, Real, Array] self to the power of this 770 | # @return [Image] result of power 771 | def **(other) 772 | other.is_a?(Vips::Image) ? 773 | math2(other, :pow) : math2_const(other, :pow) 774 | end 775 | 776 | # Integer left shift with an image, constant or array. 777 | # 778 | # @param other [Image, Real, Array] shift left by this much 779 | # @return [Image] result of left shift 780 | def <<(other) 781 | other.is_a?(Vips::Image) ? 782 | boolean(other, :lshift) : boolean_const(other, :lshift) 783 | end 784 | 785 | # Integer right shift with an image, constant or array. 786 | # 787 | # @param other [Image, Real, Array] shift right by this much 788 | # @return [Image] result of right shift 789 | def >>(other) 790 | other.is_a?(Vips::Image) ? 791 | boolean(other, :rshift) : boolean_const(other, :rshift) 792 | end 793 | 794 | # Integer bitwise OR with an image, constant or array. 795 | # 796 | # @param other [Image, Real, Array] bitwise OR with this 797 | # @return [Image] result of bitwise OR 798 | def |(other) 799 | other.is_a?(Vips::Image) ? 800 | boolean(other, :or) : boolean_const(other, :or) 801 | end 802 | 803 | # Integer bitwise AND with an image, constant or array. 804 | # 805 | # @param other [Image, Real, Array] bitwise AND with this 806 | # @return [Image] result of bitwise AND 807 | def &(other) 808 | other.is_a?(Vips::Image) ? 809 | boolean(other, :and) : boolean_const(other, :and) 810 | end 811 | 812 | # Integer bitwise EOR with an image, constant or array. 813 | # 814 | # @param other [Image, Real, Array] bitwise EOR with this 815 | # @return [Image] result of bitwise EOR 816 | def ^(other) 817 | other.is_a?(Vips::Image) ? 818 | boolean(other, :eor) : boolean_const(other, :eor) 819 | end 820 | 821 | # Equivalent to image ^ -1 822 | # 823 | # @return [Image] image with bits flipped 824 | def ! 825 | self ^ -1 826 | end 827 | 828 | # Equivalent to image ^ -1 829 | # 830 | # @return [Image] image with bits flipped 831 | def ~ 832 | self ^ -1 833 | end 834 | 835 | # @return [Image] image 836 | def +@ 837 | self 838 | end 839 | 840 | # Equivalent to image * -1 841 | # 842 | # @return [Image] negative of image 843 | def -@ 844 | self * -1 845 | end 846 | 847 | # Relational less than with an image, constant or array. 848 | # 849 | # @param other [Image, Real, Array] relational less than with this 850 | # @return [Image] result of less than 851 | def <(other) 852 | other.is_a?(Vips::Image) ? 853 | relational(other, :less) : relational_const(other, :less) 854 | end 855 | 856 | # Relational less than or equal to with an image, constant or array. 857 | # 858 | # @param other [Image, Real, Array] relational less than or 859 | # equal to with this 860 | # @return [Image] result of less than or equal to 861 | def <=(other) 862 | other.is_a?(Vips::Image) ? 863 | relational(other, :lesseq) : relational_const(other, :lesseq) 864 | end 865 | 866 | # Relational more than with an image, constant or array. 867 | # 868 | # @param other [Image, Real, Array] relational more than with this 869 | # @return [Image] result of more than 870 | def >(other) 871 | other.is_a?(Vips::Image) ? 872 | relational(other, :more) : relational_const(other, :more) 873 | end 874 | 875 | # Relational more than or equal to with an image, constant or array. 876 | # 877 | # @param other [Image, Real, Array] relational more than or 878 | # equal to with this 879 | # @return [Image] result of more than or equal to 880 | def >=(other) 881 | other.is_a?(Vips::Image) ? 882 | relational(other, :moreeq) : relational_const(other, :moreeq) 883 | end 884 | 885 | # Compare equality to nil, an image, constant or array. 886 | # 887 | # @param other [nil, Image, Real, Array] test equality to this 888 | # @return [Image] result of equality 889 | def ==(other) 890 | if other == nil 891 | false 892 | elsif other.is_a?(Vips::Image) 893 | relational(other, :equal) 894 | else 895 | relational_const(other, :equal) 896 | end 897 | end 898 | 899 | # Compare inequality to nil, an image, constant or array. 900 | # 901 | # @param other [nil, Image, Real, Array] test inequality to this 902 | # @return [Image] result of inequality 903 | def !=(other) 904 | if other == nil 905 | true 906 | elsif other.is_a?(Vips::Image) 907 | relational(other, :noteq) 908 | else 909 | relational_const(other, :noteq) 910 | end 911 | end 912 | 913 | # Fetch bands using a number or a range 914 | # 915 | # @param index [Numeric, Range] extract these band(s) 916 | # @return [Image] extracted band(s) 917 | def [](index) 918 | if index.is_a? Range 919 | n = index.end - index.begin 920 | n += 1 if not index.exclude_end? 921 | extract_band index.begin, :n => n 922 | elsif index.is_a? Numeric 923 | extract_band index 924 | else 925 | raise Vips::Error, "[] index is not range or numeric." 926 | end 927 | end 928 | 929 | # Return the largest integral value not greater than the argument. 930 | # 931 | # @return [Image] floor of image 932 | def floor 933 | round :floor 934 | end 935 | 936 | # Return the smallest integral value not less than the argument. 937 | # 938 | # @return [Image] ceil of image 939 | def ceil 940 | round :ceil 941 | end 942 | 943 | # Return the nearest integral value. 944 | # 945 | # @return [Image] rint of image 946 | def rint 947 | round :rint 948 | end 949 | 950 | # AND the bands of an image together 951 | # 952 | # @return [Image] all bands ANDed together 953 | def bandand 954 | bandbool :and 955 | end 956 | 957 | # OR the bands of an image together 958 | # 959 | # @return [Image] all bands ORed together 960 | def bandor 961 | bandbool :or 962 | end 963 | 964 | # EOR the bands of an image together 965 | # 966 | # @return [Image] all bands EORed together 967 | def bandeor 968 | bandbool :eor 969 | end 970 | 971 | # Split an n-band image into n separate images. 972 | # 973 | # @return [Array] Array of n one-band images 974 | def bandsplit 975 | (0...bands).map {|i| extract_band(i)} 976 | end 977 | 978 | # Join a set of images bandwise. 979 | # 980 | # @param other [Image, Array, Real, Array] bands to append 981 | # @return [Image] many band image 982 | def bandjoin(other) 983 | if not other.is_a? Array 984 | other = [other] 985 | end 986 | 987 | Vips::Image.bandjoin([self] + other) 988 | end 989 | 990 | # Return the coordinates of the image maximum. 991 | # 992 | # @return [Real, Real, Real] maximum value, x coordinate of maximum, y 993 | # coordinate of maximum 994 | def maxpos 995 | v, opts = max :x => true, :y => true 996 | x = opts['x'] 997 | y = opts['y'] 998 | return v, x, y 999 | end 1000 | 1001 | # Return the coordinates of the image minimum. 1002 | # 1003 | # @return [Real, Real, Real] minimum value, x coordinate of minimum, y 1004 | # coordinate of minimum 1005 | def minpos 1006 | v, opts = min :x => true, :y => true 1007 | x = opts['x'] 1008 | y = opts['y'] 1009 | return v, x, y 1010 | end 1011 | 1012 | # get the value of a pixel as an array 1013 | # 1014 | # @param x [Integer] x coordinate to sample 1015 | # @param y [Integer] y coordinate to sample 1016 | # @return [Array] the pixel values as an array 1017 | def getpoint(x, y) 1018 | # vips has an operation that does this, but we can't call it via 1019 | # gobject-introspection 3.0.7 since it's missing array double 1020 | # get 1021 | # 1022 | # remove this def when gobject-introspection updates 1023 | crop(x, y, 1, 1).bandsplit.map {|i| i.avg} 1024 | end 1025 | 1026 | # a median filter 1027 | # 1028 | # @param size [Integer] size of filter window 1029 | # @return [Image] result of median filter 1030 | def median(size = 3) 1031 | rank(size, size, (size * size) / 2) 1032 | end 1033 | 1034 | # Return the real part of a complex image. 1035 | # 1036 | # @return [Image] real part of complex image 1037 | def real 1038 | complexget :real 1039 | end 1040 | 1041 | # Return the imaginary part of a complex image. 1042 | # 1043 | # @return [Image] imaginary part of complex image 1044 | def imag 1045 | complexget :imag 1046 | end 1047 | 1048 | # Return an image with rectangular pixels converted to polar. 1049 | # 1050 | # The image 1051 | # can be complex, in which case the return image will also be complex, 1052 | # or must have an even number of bands, in which case pairs of 1053 | # bands are treated as (x, y) coordinates. 1054 | # 1055 | # @see xyz 1056 | # @return [Image] image converted to polar coordinates 1057 | def polar 1058 | Image::run_cmplx(self) {|x| x.complex :polar} 1059 | end 1060 | 1061 | # Return an image with polar pixels converted to rectangular. 1062 | # 1063 | # The image 1064 | # can be complex, in which case the return image will also be complex, 1065 | # or must have an even number of bands, in which case pairs of 1066 | # bands are treated as (x, y) coordinates. 1067 | # 1068 | # @see xyz 1069 | # @return [Image] image converted to rectangular coordinates 1070 | def rect 1071 | Image::run_cmplx(self) {|x| x.complex :rect} 1072 | end 1073 | 1074 | # Return the complex conjugate of an image. 1075 | # 1076 | # The image 1077 | # can be complex, in which case the return image will also be complex, 1078 | # or must have an even number of bands, in which case pairs of 1079 | # bands are treated as (x, y) coordinates. 1080 | # 1081 | # @return [Image] complex conjugate 1082 | def conj 1083 | Image::run_cmplx(self) {|x| x.complex :conj} 1084 | end 1085 | 1086 | # Return the sine of an image in degrees. 1087 | # 1088 | # @return [Image] sine of each pixel 1089 | def sin 1090 | math :sin 1091 | end 1092 | 1093 | # Return the cosine of an image in degrees. 1094 | # 1095 | # @return [Image] cosine of each pixel 1096 | def cos 1097 | math :cos 1098 | end 1099 | 1100 | # Return the tangent of an image in degrees. 1101 | # 1102 | # @return [Image] tangent of each pixel 1103 | def tan 1104 | math :tan 1105 | end 1106 | 1107 | # Return the inverse sine of an image in degrees. 1108 | # 1109 | # @return [Image] inverse sine of each pixel 1110 | def asin 1111 | math :asin 1112 | end 1113 | 1114 | # Return the inverse cosine of an image in degrees. 1115 | # 1116 | # @return [Image] inverse cosine of each pixel 1117 | def acos 1118 | math :acos 1119 | end 1120 | 1121 | # Return the inverse tangent of an image in degrees. 1122 | # 1123 | # @return [Image] inverse tangent of each pixel 1124 | def atan 1125 | math :atan 1126 | end 1127 | 1128 | # Return the natural log of an image. 1129 | # 1130 | # @return [Image] natural log of each pixel 1131 | def log 1132 | math :log 1133 | end 1134 | 1135 | # Return the log base 10 of an image. 1136 | # 1137 | # @return [Image] base 10 log of each pixel 1138 | def log10 1139 | math :log10 1140 | end 1141 | 1142 | # Return e ** pixel. 1143 | # 1144 | # @return [Image] e ** pixel 1145 | def exp 1146 | math :exp 1147 | end 1148 | 1149 | # Return 10 ** pixel. 1150 | # 1151 | # @return [Image] 10 ** pixel 1152 | def exp10 1153 | math :exp10 1154 | end 1155 | 1156 | # Flip horizontally. 1157 | # 1158 | # @return [Image] image flipped horizontally 1159 | def fliphor 1160 | flip :horizontal 1161 | end 1162 | 1163 | # Flip vertically. 1164 | # 1165 | # @return [Image] image flipped vertically 1166 | def flipver 1167 | flip :vertical 1168 | end 1169 | 1170 | # Erode with a structuring element. 1171 | # 1172 | # The structuring element must be an array with 0 for black, 255 for 1173 | # white and 128 for don't care. 1174 | # 1175 | # @param mask [Image, Array, Array>] structuring 1176 | # element 1177 | # @return [Image] eroded image 1178 | def erode(mask) 1179 | morph mask, :erode 1180 | end 1181 | 1182 | # Dilate with a structuring element. 1183 | # 1184 | # The structuring element must be an array with 0 for black, 255 for 1185 | # white and 128 for don't care. 1186 | # 1187 | # @param mask [Image, Array, Array>] structuring 1188 | # element 1189 | # @return [Image] dilated image 1190 | def dilate(mask) 1191 | morph mask, :dilate 1192 | end 1193 | 1194 | # Rotate by 90 degrees clockwise. 1195 | # 1196 | # @return [Image] rotated image 1197 | def rot90 1198 | rot :d90 1199 | end 1200 | 1201 | # Rotate by 180 degrees clockwise. 1202 | # 1203 | # @return [Image] rotated image 1204 | def rot180 1205 | rot :d180 1206 | end 1207 | 1208 | # Rotate by 270 degrees clockwise. 1209 | # 1210 | # @return [Image] rotated image 1211 | def rot270 1212 | rot :d270 1213 | end 1214 | 1215 | # Select pixels from `th` if `self` is non-zero and from `el` if 1216 | # `self` is zero. Use the `:blend` option to fade smoothly 1217 | # between `th` and `el`. 1218 | # 1219 | # @param th [Image, Real, Array] true values 1220 | # @param el [Image, Real, Array] false values 1221 | # @param [Hash] opts set of options 1222 | # @option opts [Boolean] :blend (false) Blend smoothly between th and el 1223 | # @return [Image] merged image 1224 | def ifthenelse(th, el, opts = {}) 1225 | match_image = [th, el, self].find {|x| x.is_a? Vips::Image} 1226 | 1227 | if not th.is_a? Vips::Image 1228 | th = Argument::imageize match_image, th 1229 | end 1230 | if not el.is_a? Vips::Image 1231 | el = Argument::imageize match_image, el 1232 | end 1233 | 1234 | Vips::call_base "ifthenelse", self, "", [th, el, opts] 1235 | end 1236 | 1237 | end 1238 | 1239 | # This method generates yard comments for all the dynamically bound 1240 | # vips operations. 1241 | # 1242 | # Regenerate with something like: 1243 | # 1244 | # ruby > methods.rb 1245 | # require 'vips8'; Vips::generate_yard 1246 | # ^D 1247 | 1248 | def self.generate_yard 1249 | # these have hand-written methods, see above 1250 | no_generate = ["bandjoin", "ifthenelse"] 1251 | 1252 | generate_operation = lambda do |op| 1253 | flags = op.flags 1254 | return if (flags & :deprecated) != 0 1255 | nickname = Vips::nickname_find op.gtype 1256 | 1257 | return if no_generate.include? nickname 1258 | 1259 | all_args = op.get_args.select {|arg| not arg.isset} 1260 | 1261 | # separate args into various categories 1262 | 1263 | required_input = all_args.select do |arg| 1264 | (arg.flags & :input) != 0 and 1265 | (arg.flags & :required) != 0 1266 | end 1267 | 1268 | optional_input = all_args.select do |arg| 1269 | (arg.flags & :input) != 0 and 1270 | (arg.flags & :required) == 0 1271 | end 1272 | 1273 | required_output = all_args.select do |arg| 1274 | (arg.flags & :output) != 0 and 1275 | (arg.flags & :required) != 0 1276 | end 1277 | 1278 | # required input args with :modify are copied and appended to 1279 | # output 1280 | modified_required_input = required_input.select do |arg| 1281 | (arg.flags & :modify) != 0 1282 | end 1283 | required_output += modified_required_input 1284 | 1285 | optional_output = all_args.select do |arg| 1286 | (arg.flags & :output) != 0 and 1287 | (arg.flags & :required) == 0 1288 | end 1289 | 1290 | # optional input args with :modify are copied and appended to 1291 | # output 1292 | modified_optional_input = optional_input.select do |arg| 1293 | (arg.flags & :modify) != 0 1294 | end 1295 | optional_output += modified_optional_input 1296 | 1297 | # find the first input image, if any ... we will be a method of this 1298 | # instance 1299 | member_x = required_input.find do |x| 1300 | x.gtype.type_is_a? GLib::Type["VipsImage"] 1301 | end 1302 | if member_x != nil 1303 | required_input.delete member_x 1304 | end 1305 | 1306 | print "# @!method " 1307 | print "self." if not member_x 1308 | print "#{nickname}(" 1309 | print required_input.map(&:name).join(", ") 1310 | puts ", opts = {})" 1311 | 1312 | puts "# #{op.description.capitalize}." 1313 | 1314 | required_input.each do |arg| 1315 | puts "# @param #{arg.name} [#{arg.type}] #{arg.blurb}" 1316 | end 1317 | 1318 | puts "# @param [Hash] opts Set of options" 1319 | optional_input.each do |arg| 1320 | puts "# @option opts [#{arg.type}] :#{arg.name} #{arg.blurb}" 1321 | end 1322 | optional_output.each do |arg| 1323 | print "# @option opts [#{arg.type}] :#{arg.name}" 1324 | puts " Output #{arg.blurb}" 1325 | end 1326 | 1327 | print "# @return [" 1328 | if required_output.length == 0 1329 | print "nil" 1330 | elsif required_output.length == 1 1331 | print required_output[0].type 1332 | elsif 1333 | print "Array<" 1334 | print required_output.map(&:type).join(", ") 1335 | print ">" 1336 | end 1337 | if optional_output.length > 0 1338 | print ", Hash Object>" 1339 | end 1340 | print "] " 1341 | print required_output.map(&:blurb).join(", ") 1342 | if optional_output.length > 0 1343 | print ", " if required_output.length > 0 1344 | print "Hash of optional output items" 1345 | end 1346 | puts "" 1347 | 1348 | puts "" 1349 | end 1350 | 1351 | generate_class = lambda do |gtype| 1352 | begin 1353 | # can fail for abstract types 1354 | # can't find a way to get to #abstract? from a gtype 1355 | op = Vips::Operation.new gtype.name 1356 | rescue 1357 | op = nil 1358 | end 1359 | 1360 | generate_operation.(op) if op 1361 | 1362 | gtype.children.each do |x| 1363 | generate_class.(x) 1364 | end 1365 | end 1366 | 1367 | puts "module Vips" 1368 | puts " class Image" 1369 | puts "" 1370 | 1371 | # gobject-introspection 3.0.7 crashes a lot if it GCs while doing 1372 | # something 1373 | GC.disable 1374 | 1375 | generate_class.(GLib::Type["VipsOperation"]) 1376 | 1377 | puts " end" 1378 | puts "end" 1379 | end 1380 | 1381 | end 1382 | 1383 | -------------------------------------------------------------------------------- /lib/vips8/interpolate.rb: -------------------------------------------------------------------------------- 1 | module Vips 2 | 3 | # An interpolator. One of these can be given to operations like 4 | # {Vips::affine} or {Vips::mapim} to select the type of pixel interpolation 5 | # to use. 6 | # 7 | # To see all interpolators supported by your 8 | # libvips, try 9 | # 10 | # ``` 11 | # $ vips -l interpolate 12 | # ``` 13 | # 14 | # But at least these should be available: 15 | # 16 | # * `:nearest` Nearest-neighbour interpolation. 17 | # * `:bilinear` Bilinear interpolation. 18 | # * `:bicubic` Bicubic interpolation. 19 | # * `:lbb` Reduced halo bicubic interpolation. 20 | # * `:nohalo` Edge sharpening resampler with halo reduction. 21 | # * `:vsqbs` B-Splines with antialiasing smoothing. 22 | # 23 | # For example: 24 | # 25 | # ```ruby 26 | # im = im.affine :interpolate => Vips::Interpolate.new :bicubic 27 | # ``` 28 | 29 | class Interpolate 30 | 31 | # @!method self.new(name, opts = {}) 32 | # @param name [Symbol] interpolator to create 33 | # @param [Hash] opts Set of options 34 | # @return [Interpolate] constructed interpolator 35 | 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/vips8/interpretation.rb: -------------------------------------------------------------------------------- 1 | module Vips 2 | 3 | # How the values in an image should be interpreted. For example, a 4 | # three-band float image of type :lab should have its 5 | # pixels interpreted as coordinates in CIE Lab space. 6 | # 7 | # * `:multiband` generic many-band image 8 | # * `:b_w` some kind of single-band image 9 | # * `:histogram` a 1D image, eg. histogram or lookup table 10 | # * `:fourier` image is in fourier space 11 | # * `:xyz` the first three bands are CIE XYZ 12 | # * `:lab` pixels are in CIE Lab space 13 | # * `:cmyk` the first four bands are in CMYK space 14 | # * `:labq` implies #VIPS_CODING_LABQ 15 | # * `:rgb` generic RGB space 16 | # * `:cmc` a uniform colourspace based on CMC(1:1) 17 | # * `:lch` pixels are in CIE LCh space 18 | # * `:labs` CIE LAB coded as three signed 16-bit values 19 | # * `:srgb` pixels are sRGB 20 | # * `:hsv` pixels are HSV 21 | # * `:scrgb` pixels are scRGB 22 | # * `:yxy` pixels are CIE Yxy 23 | # * `:rgb16` generic 16-bit RGB 24 | # * `:grey16` generic 16-bit mono 25 | # * `:matrix` a matrix 26 | class Interpretation 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/vips8/operation.rb: -------------------------------------------------------------------------------- 1 | module Vips 2 | 3 | # add a conventience method to Operation 4 | # @private 5 | class Operation 6 | # Fetch arg list, remove boring ones, sort into priority order. 7 | def get_args 8 | gobject_class = gtype.to_class 9 | props = gobject_class.properties.select do |name| 10 | flags = get_argument_flags name 11 | io = ((flags & :input) | (flags & :output)) != 0 12 | dep = (flags & :deprecated) != 0 13 | io & (not dep) 14 | end 15 | args = props.map {|name| Argument.new self, name} 16 | args.sort! {|a, b| a.priority - b.priority} 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /ruby-vips8.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' 4 | # -*- encoding: utf-8 -*- 5 | # stub: ruby-vips8 0.1.0 ruby lib 6 | 7 | Gem::Specification.new do |s| 8 | s.name = "ruby-vips8" 9 | s.version = "0.1.0" 10 | 11 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 12 | s.require_paths = ["lib"] 13 | s.authors = ["John Cupitt"] 14 | s.date = "2016-01-25" 15 | s.description = "ruby-vips8 is a ruby extension for vips8. It is extremely fast and it can process huge images without requiring the entire image to be loaded into memory." 16 | s.email = "jcupitt@gmail.com" 17 | s.extra_rdoc_files = [ 18 | "LICENSE.txt", 19 | "README.md", 20 | "TODO" 21 | ] 22 | s.files = [ 23 | ".rspec", 24 | ".yardopts", 25 | "CHANGELOG.md", 26 | "Gemfile", 27 | "Gemfile.lock", 28 | "LICENSE.txt", 29 | "README.md", 30 | "Rakefile", 31 | "TODO", 32 | "VERSION", 33 | "example/annotate.rb", 34 | "example/daltonize8.rb", 35 | "example/example1.rb", 36 | "example/example2.rb", 37 | "example/example3.rb", 38 | "example/example4.rb", 39 | "example/example5.rb", 40 | "example/trim8.rb", 41 | "example/watermark.rb", 42 | "example/wobble.rb", 43 | "lib/vips8.rb", 44 | "lib/vips8/access.rb", 45 | "lib/vips8/align.rb", 46 | "lib/vips8/angle.rb", 47 | "lib/vips8/angle45.rb", 48 | "lib/vips8/argument.rb", 49 | "lib/vips8/bandformat.rb", 50 | "lib/vips8/call.rb", 51 | "lib/vips8/coding.rb", 52 | "lib/vips8/demandstyle.rb", 53 | "lib/vips8/direction.rb", 54 | "lib/vips8/error.rb", 55 | "lib/vips8/extend.rb", 56 | "lib/vips8/foreignflags.rb", 57 | "lib/vips8/image.rb", 58 | "lib/vips8/interpolate.rb", 59 | "lib/vips8/interpretation.rb", 60 | "lib/vips8/methods.rb", 61 | "lib/vips8/operation.rb", 62 | "ruby-vips8.gemspec", 63 | "spec/image_spec.rb", 64 | "spec/samples/balloon.v", 65 | "spec/samples/ghost.ppm", 66 | "spec/samples/huge.jpg", 67 | "spec/samples/icc.jpg", 68 | "spec/samples/lcd.icc", 69 | "spec/samples/lion.svg", 70 | "spec/samples/sample.csv", 71 | "spec/samples/sample.exr", 72 | "spec/samples/wagon.jpg", 73 | "spec/samples/wagon.v", 74 | "spec/spec_helper.rb", 75 | "spec/vips_spec.rb" 76 | ] 77 | s.homepage = "http://github.com/jcupitt/ruby-vips8" 78 | s.licenses = ["MIT"] 79 | s.rubygems_version = "2.2.2" 80 | s.summary = "Ruby extension for the vips8 image processing library." 81 | 82 | if s.respond_to? :specification_version then 83 | s.specification_version = 4 84 | 85 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 86 | s.add_runtime_dependency(%q, ["~> 3.0"]) 87 | s.add_development_dependency(%q, ["~> 3.3"]) 88 | s.add_development_dependency(%q, ["~> 0.8"]) 89 | s.add_development_dependency(%q, ["~> 3.3"]) 90 | s.add_development_dependency(%q, ["~> 1.4"]) 91 | s.add_development_dependency(%q, ["~> 1.0"]) 92 | s.add_development_dependency(%q, ["~> 2.0"]) 93 | else 94 | s.add_dependency(%q, ["~> 3.0"]) 95 | s.add_dependency(%q, ["~> 3.3"]) 96 | s.add_dependency(%q, ["~> 0.8"]) 97 | s.add_dependency(%q, ["~> 3.3"]) 98 | s.add_dependency(%q, ["~> 1.4"]) 99 | s.add_dependency(%q, ["~> 1.0"]) 100 | s.add_dependency(%q, ["~> 2.0"]) 101 | end 102 | else 103 | s.add_dependency(%q, ["~> 3.0"]) 104 | s.add_dependency(%q, ["~> 3.3"]) 105 | s.add_dependency(%q, ["~> 0.8"]) 106 | s.add_dependency(%q, ["~> 3.3"]) 107 | s.add_dependency(%q, ["~> 1.4"]) 108 | s.add_dependency(%q, ["~> 1.0"]) 109 | s.add_dependency(%q, ["~> 2.0"]) 110 | end 111 | end 112 | 113 | -------------------------------------------------------------------------------- /spec/image_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper.rb' 2 | 3 | def has_jpeg? 4 | Vips::type_find("VipsOperation", "jpegload") != nil 5 | end 6 | 7 | RSpec.describe Vips::Image do 8 | describe '#new' do 9 | it 'can make an empty image' do 10 | image = Vips::Image.new 11 | 12 | expect(image.width).to eq(1) 13 | expect(image.height).to eq(1) 14 | expect(image.bands).to eq(1) 15 | end 16 | 17 | it 'can save an image to a file' do 18 | filename = timg "x.v" 19 | 20 | image = Vips::Image.black(16, 16) + 128 21 | image.write_to_file filename 22 | 23 | expect(File.exist?(filename)).to be true 24 | end 25 | 26 | it 'can load an image from a file' do 27 | filename = timg "x.v" 28 | 29 | image = Vips::Image.black(16, 16) + 128 30 | image.write_to_file(filename) 31 | 32 | x = Vips::Image.new_from_file filename 33 | expect(x.width).to eq(16) 34 | expect(x.height).to eq(16) 35 | expect(x.bands).to eq(1) 36 | expect(x.avg).to eq(128) 37 | end 38 | 39 | if has_jpeg? 40 | it 'can save an image to a buffer' do 41 | image = Vips::Image.black(16, 16) + 128 42 | buffer = image.write_to_buffer ".jpg" 43 | expect(buffer.length).to be > 100 44 | end 45 | end 46 | 47 | if has_jpeg? 48 | it 'can load an image from a buffer' do 49 | image = Vips::Image.black(16, 16) + 128 50 | buffer = image.write_to_buffer ".jpg" 51 | x = Vips::Image.new_from_buffer buffer, "" 52 | expect(x.width).to eq(16) 53 | expect(x.height).to eq(16) 54 | end 55 | end 56 | 57 | it 'can make an image from a 2d array' do 58 | image = Vips::Image.new_from_array [[1, 2], [3, 4]] 59 | expect(image.width).to eq(2) 60 | expect(image.height).to eq(2) 61 | expect(image.bands).to eq(1) 62 | expect(image.avg).to eq(2.5) 63 | end 64 | 65 | it 'can make an image from a 1d array' do 66 | image = Vips::Image.new_from_array [1, 2] 67 | expect(image.width).to eq(2) 68 | expect(image.height).to eq(1) 69 | expect(image.bands).to eq(1) 70 | expect(image.avg).to eq(1.5) 71 | end 72 | 73 | it 'can use array consts for image args' do 74 | r = Vips::Image.black(16, 16) 75 | r = r.draw_rect 255, 10, 12, 1, 1 76 | g = Vips::Image.black(16, 16) 77 | g = g.draw_rect 255, 10, 11, 1, 1 78 | b = Vips::Image.black(16, 16) 79 | b = b.draw_rect 255, 10, 10, 1, 1 80 | im = r.bandjoin([g, b]) 81 | 82 | expect(im.width).to eq(16) 83 | expect(im.height).to eq(16) 84 | expect(im.bands).to eq(3) 85 | 86 | im = im.conv [ 87 | [0.11, 0.11, 0.11], 88 | [0.11, 0.11, 0.11], 89 | [0.11, 0.11, 0.11] 90 | ], :precision => :float 91 | 92 | expect(im.width).to eq(16) 93 | expect(im.height).to eq(16) 94 | expect(im.bands).to eq(3) 95 | end 96 | 97 | it 'can set scale and offset on a convolution mask' do 98 | image = Vips::Image.new_from_array [1, 2], 8, 2 99 | expect(image.width).to eq(2) 100 | expect(image.height).to eq(1) 101 | expect(image.bands).to eq(1) 102 | expect(image.scale).to eq(8) 103 | expect(image.offset).to eq(2) 104 | expect(image.avg).to eq(1.5) 105 | end 106 | 107 | if has_jpeg? 108 | it 'can load a sample jpg image' do 109 | x = Vips::Image.new_from_file simg("wagon.jpg") 110 | expect(x.width).to eq(685) 111 | expect(x.height).to eq(478) 112 | expect(x.bands).to eq(3) 113 | expect(x.avg).to be_within(0.001).of(109.789) 114 | end 115 | end 116 | 117 | if has_jpeg? 118 | it 'can extract an ICC profile from a jpg image' do 119 | x = Vips::Image.new_from_file simg("icc.jpg") 120 | expect(x.width).to eq(2800) 121 | expect(x.height).to eq(2100) 122 | expect(x.bands).to eq(3) 123 | expect(x.avg).to be_within(0.001).of(109.189) 124 | 125 | profile = x.get_value "icc-profile-data" 126 | expect(profile.class).to eq(String) 127 | expect(profile.length).to eq(2360) 128 | end 129 | end 130 | 131 | if has_jpeg? 132 | it 'can set an ICC profile on a jpg image' do 133 | x = Vips::Image.new_from_file simg("icc.jpg") 134 | profile = File.open(simg("lcd.icc"), "rb").read 135 | x.set_value "icc-profile-data", profile 136 | x.write_to_file(timg("x.jpg")) 137 | 138 | x = Vips::Image.new_from_file timg("x.jpg") 139 | expect(x.width).to eq(2800) 140 | expect(x.height).to eq(2100) 141 | expect(x.bands).to eq(3) 142 | expect(x.avg).to be_within(0.1).of(109.189) 143 | 144 | profile = x.get_value "icc-profile-data" 145 | expect(profile.class).to eq(String) 146 | expect(profile.length).to eq(3048) 147 | end 148 | end 149 | 150 | if has_jpeg? 151 | it 'can load a sample jpg image' do 152 | x = Vips::Image.new_from_file simg("wagon.jpg") 153 | expect(x.width).to eq(685) 154 | expect(x.height).to eq(478) 155 | expect(x.bands).to eq(3) 156 | expect(x.avg).to be_within(0.001).of(109.789) 157 | end 158 | end 159 | 160 | it 'has binary arithmetic operator overloads with constants' do 161 | image = Vips::Image.black(16, 16) + 128 162 | 163 | image += 128 164 | image -= 128 165 | image *= 2 166 | image /= 2 167 | image %= 100 168 | image += 100 169 | image **= 2 170 | image **= 0.5 171 | image <<= 1 172 | image >>= 1 173 | image |= 64 174 | image &= 32 175 | image ^= 128 176 | 177 | expect(image.avg).to eq(128) 178 | end 179 | 180 | it 'has binary arithmetic operator overloads with array constants' do 181 | image = Vips::Image.black(16, 16, :bands => 3) + 128 182 | 183 | image += [128, 0, 0] 184 | image -= [128, 0, 0] 185 | image *= [2, 1, 1] 186 | image /= [2, 1, 1] 187 | image %= [100, 99, 98] 188 | image += [100, 99, 98] 189 | image **= [2, 3, 4] 190 | image **= [1.0 / 2.0, 1.0 / 3.0, 1.0 / 4.0] 191 | image <<= [1, 2, 3] 192 | image >>= [1, 2, 3] 193 | image |= [64, 128, 256] 194 | image &= [64, 128, 256] 195 | image ^= [64 + 128, 0, 256 + 128] 196 | 197 | expect(image.avg).to eq(128) 198 | end 199 | 200 | it 'has binary arithmetic operator overloads with image args' do 201 | image = Vips::Image.black(16, 16) + 128 202 | x = image 203 | 204 | x += image 205 | x -= image 206 | x *= image 207 | x /= image 208 | x %= image 209 | x += image 210 | x |= image 211 | x &= image 212 | x ^= image 213 | 214 | expect(image.avg).to eq(128) 215 | end 216 | 217 | it 'has relational operator overloads with constants' do 218 | image = Vips::Image.black(16, 16) + 128 219 | 220 | expect((image > 128).avg).to eq(0) 221 | expect((image >= 128).avg).to eq(255) 222 | expect((image < 128).avg).to eq(0) 223 | expect((image <= 128).avg).to eq(255) 224 | expect((image == 128).avg).to eq(255) 225 | expect((image != 128).avg).to eq(0) 226 | end 227 | 228 | it 'has relational operator overloads with array constants' do 229 | image = Vips::Image.black(16, 16, :bands => 3) + [100, 128, 130] 230 | 231 | expect((image > [100, 128, 130]).avg).to eq(0) 232 | expect((image >= [100, 128, 130]).avg).to eq(255) 233 | expect((image < [100, 128, 130]).avg).to eq(0) 234 | expect((image <= [100, 128, 130]).avg).to eq(255) 235 | expect((image == [100, 128, 130]).avg).to eq(255) 236 | expect((image != [100, 128, 130]).avg).to eq(0) 237 | end 238 | 239 | it 'has relational operator overloads with image args' do 240 | image = Vips::Image.black(16, 16) + 128 241 | 242 | expect((image > image).avg).to eq(0) 243 | expect((image >= image).avg).to eq(255) 244 | expect((image < image).avg).to eq(0) 245 | expect((image <= image).avg).to eq(255) 246 | expect((image == image).avg).to eq(255) 247 | expect((image != image).avg).to eq(0) 248 | end 249 | 250 | it 'has band extract with numeric arg' do 251 | image = Vips::Image.black(16, 16, :bands => 3) + [100, 128, 130] 252 | x = image[1] 253 | 254 | expect(x.width).to eq(16) 255 | expect(x.height).to eq(16) 256 | expect(x.bands).to eq(1) 257 | expect(x.avg).to eq(128) 258 | end 259 | 260 | it 'has band extract with range arg' do 261 | image = Vips::Image.black(16, 16, :bands => 3) + [100, 128, 130] 262 | x = image[1..2] 263 | 264 | expect(x.width).to eq(16) 265 | expect(x.height).to eq(16) 266 | expect(x.bands).to eq(2) 267 | expect(x.avg).to eq(129) 268 | end 269 | 270 | it 'has rounding members' do 271 | # need to avoid rounding down to 0.499999 272 | image = Vips::Image.black(16, 16) + 0.500001 273 | 274 | expect(image.floor.avg).to eq(0) 275 | expect(image.ceil.avg).to eq(1) 276 | expect(image.rint.avg).to eq(1) 277 | end 278 | 279 | it 'has bandsplit and bandjoin' do 280 | image = Vips::Image.black(16, 16, :bands => 3) + [100, 128, 130] 281 | 282 | split = image.bandsplit 283 | x = split[0].bandjoin split[1..2] 284 | 285 | expect(x[0].avg).to eq(100) 286 | expect(x[1].avg).to eq(128) 287 | expect(x[2].avg).to eq(130) 288 | end 289 | 290 | it 'can bandjoin constants' do 291 | image = Vips::Image.black(16, 16, :bands => 3) + [100, 128, 130] 292 | 293 | x = image.bandjoin 255 294 | 295 | expect(x[0].avg).to eq(100) 296 | expect(x[1].avg).to eq(128) 297 | expect(x[2].avg).to eq(130) 298 | expect(x[3].avg).to eq(255) 299 | expect(x.bands).to eq(4) 300 | 301 | x = image.bandjoin [1, 2] 302 | 303 | expect(x[0].avg).to eq(100) 304 | expect(x[1].avg).to eq(128) 305 | expect(x[2].avg).to eq(130) 306 | expect(x[3].avg).to eq(1) 307 | expect(x[4].avg).to eq(2) 308 | expect(x.bands).to eq(5) 309 | 310 | end 311 | 312 | it 'has minpos/maxpos' do 313 | image = Vips::Image.black(16, 16) + 128 314 | image = image.draw_rect 255, 10, 12, 1, 1 315 | v, x, y = image.maxpos 316 | 317 | expect(v).to eq(255) 318 | expect(x).to eq(10) 319 | expect(y).to eq(12) 320 | 321 | image = Vips::Image.black(16, 16) + 128 322 | image = image.draw_rect 12, 10, 12, 1, 1 323 | v, x, y = image.minpos 324 | 325 | expect(v).to eq(12) 326 | expect(x).to eq(10) 327 | expect(y).to eq(12) 328 | 329 | end 330 | 331 | it 'can form complex images' do 332 | r = Vips::Image.black(16, 16) + 128 333 | i = Vips::Image.black(16, 16) + 12 334 | cmplx = r.complexform i 335 | re = cmplx.real 336 | im = cmplx.imag 337 | 338 | expect(re.avg).to eq(128) 339 | expect(im.avg).to eq(12) 340 | end 341 | 342 | it 'can convert complex polar <-> rectangular' do 343 | r = Vips::Image.black(16, 16) + 128 344 | i = Vips::Image.black(16, 16) + 12 345 | cmplx = r.complexform i 346 | 347 | cmplx = cmplx.rect.polar 348 | 349 | expect(cmplx.real.avg).to be_within(0.001).of(128) 350 | expect(cmplx.imag.avg).to be_within(0.001).of(12) 351 | end 352 | 353 | it 'can take complex conjugate' do 354 | r = Vips::Image.black(16, 16) + 128 355 | i = Vips::Image.black(16, 16) + 12 356 | cmplx = r.complexform i 357 | 358 | cmplx = cmplx.conj 359 | 360 | expect(cmplx.real.avg).to be_within(0.001).of(128) 361 | expect(cmplx.imag.avg).to be_within(0.001).of(-12) 362 | end 363 | 364 | it 'has working trig functions' do 365 | image = Vips::Image.black(16, 16) + 67 366 | 367 | image = image.sin.cos.tan 368 | image = image.atan.acos.asin 369 | 370 | expect(image.avg).to be_within(0.01).of(67) 371 | end 372 | 373 | it 'has working log functions' do 374 | image = Vips::Image.black(16, 16) + 67 375 | 376 | image = image.log.exp.log10.exp10 377 | 378 | expect(image.avg).to be_within(0.01).of(67) 379 | end 380 | 381 | it 'can flip' do 382 | a = Vips::Image.black(16, 16) 383 | a = a.draw_rect 255, 10, 12, 1, 1 384 | b = Vips::Image.black(16, 16) 385 | b = b.draw_rect 255, 15 - 10, 12, 1, 1 386 | 387 | expect((a - b.fliphor).abs.max).to eq(0.0) 388 | 389 | a = Vips::Image.black(16, 16) 390 | a = a.draw_rect 255, 10, 15 - 12, 1, 1 391 | b = Vips::Image.black(16, 16) 392 | b = b.draw_rect 255, 10, 12, 1, 1 393 | 394 | expect((a - b.flipver).abs.max).to eq(0.0) 395 | end 396 | 397 | it 'can getpoint' do 398 | a = Vips::Image.black(16, 16) 399 | a = a.draw_rect 255, 10, 12, 1, 1 400 | b = Vips::Image.black(16, 16) 401 | b = b.draw_rect 255, 10, 10, 1, 1 402 | im = a.bandjoin(b) 403 | 404 | expect(im.getpoint(10, 12)).to eq([255, 0]) 405 | expect(im.getpoint(10, 10)).to eq([0, 255]) 406 | end 407 | 408 | it 'can median' do 409 | a = Vips::Image.black(16, 16) 410 | a = a.draw_rect 255, 10, 12, 1, 1 411 | im = a.median 412 | 413 | expect(im.max).to eq(0) 414 | end 415 | 416 | it 'can erode' do 417 | a = Vips::Image.black(16, 16) 418 | a = a.draw_rect 255, 10, 12, 1, 1 419 | mask = Vips::Image.new_from_array [ 420 | [128, 255, 128], 421 | [255, 255, 255], 422 | [128, 255, 128] 423 | ] 424 | im = a.erode mask 425 | 426 | expect(im.max).to eq(0) 427 | end 428 | 429 | it 'can dilate' do 430 | a = Vips::Image.black(16, 16) 431 | a = a.draw_rect 255, 10, 12, 1, 1 432 | mask = Vips::Image.new_from_array [ 433 | [128, 255, 128], 434 | [255, 255, 255], 435 | [128, 255, 128] 436 | ] 437 | im = a.dilate mask 438 | 439 | expect(im.getpoint(10, 12)).to eq([255]) 440 | expect(im.getpoint(11, 12)).to eq([255]) 441 | expect(im.getpoint(12, 12)).to eq([0]) 442 | end 443 | 444 | it 'can rot' do 445 | a = Vips::Image.black(16, 16) 446 | a = a.draw_rect 255, 10, 12, 1, 1 447 | 448 | im = a.rot90.rot90.rot90.rot90 449 | expect((a - im).abs.max).to eq(0.0) 450 | 451 | im = a.rot180.rot180 452 | expect((a - im).abs.max).to eq(0.0) 453 | 454 | im = a.rot270.rot270.rot270.rot270 455 | expect((a - im).abs.max).to eq(0.0) 456 | end 457 | 458 | it 'can bandbool' do 459 | a = Vips::Image.black(16, 16) 460 | a = a.draw_rect 255, 10, 12, 1, 1 461 | b = Vips::Image.black(16, 16) 462 | b = b.draw_rect 255, 10, 10, 1, 1 463 | im = a.bandjoin(b) 464 | 465 | expect(im.bandand.getpoint(10, 12)).to eq([0]) 466 | expect(im.bandor.getpoint(10, 12)).to eq([255]) 467 | expect(im.bandeor.getpoint(10, 12)).to eq([255]) 468 | end 469 | 470 | it 'ifthenelse with image arguments' do 471 | image = Vips::Image.black(16, 16) 472 | image = image.draw_rect 255, 10, 12, 1, 1 473 | black = Vips::Image.black(16, 16) 474 | white = Vips::Image.black(16, 16) + 255 475 | 476 | result = image.ifthenelse black, white 477 | 478 | v, x, y = result.minpos 479 | 480 | expect(v).to eq(0) 481 | expect(x).to eq(10) 482 | expect(y).to eq(12) 483 | end 484 | 485 | it 'ifthenelse with constant arguments' do 486 | image = Vips::Image.black(16, 16) 487 | image = image.draw_rect 255, 10, 12, 1, 1 488 | 489 | result = image.ifthenelse 0, 255 490 | 491 | v, x, y = result.minpos 492 | 493 | expect(v).to eq(0) 494 | expect(x).to eq(10) 495 | expect(y).to eq(12) 496 | end 497 | 498 | it 'ifthenelse with vector arguments' do 499 | image = Vips::Image.black(16, 16) 500 | image = image.draw_rect 255, 10, 12, 1, 1 501 | white = Vips::Image.black(16, 16) + 255 502 | 503 | result = image.ifthenelse [255, 0, 0], white 504 | 505 | v, x, y = result.minpos 506 | 507 | expect(v).to eq(0) 508 | expect(x).to eq(10) 509 | expect(y).to eq(12) 510 | end 511 | 512 | end 513 | 514 | end 515 | 516 | -------------------------------------------------------------------------------- /spec/samples/balloon.v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcupitt/ruby-vips8/99c93e36676341e579e3c66090fe34f679b36e59/spec/samples/balloon.v -------------------------------------------------------------------------------- /spec/samples/ghost.ppm: -------------------------------------------------------------------------------- 1 | P3 2 | # Created by Paint Shop Pro 7 3 | 49 52 4 | 255 5 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 6 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 7 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 8 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 9 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 10 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 11 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 12 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 13 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 14 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 15 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 16 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 17 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 18 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 19 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 20 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 21 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 22 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 23 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 24 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 25 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 26 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 27 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 28 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 29 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 30 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 31 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 32 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 33 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 34 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 35 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 36 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 37 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 38 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 39 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 40 | 255 255 255 255 255 255 255 255 255 248 248 248 204 255 255 204 236 255 41 | 204 236 255 204 255 255 204 236 255 241 241 241 255 255 255 255 255 255 42 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 43 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 44 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 45 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 46 | 255 255 255 255 255 255 255 255 255 241 241 241 241 241 241 255 251 240 47 | 248 248 248 248 248 248 248 248 248 241 241 241 255 251 240 248 248 248 48 | 255 251 240 248 248 248 204 236 255 102 204 255 51 204 255 0 204 255 0 204 255 49 | 0 204 255 0 204 204 0 204 255 51 204 255 102 204 204 204 236 255 248 248 248 50 | 255 251 240 255 251 240 248 248 248 248 248 248 248 248 248 248 248 248 51 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 251 240 52 | 248 248 248 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 53 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 54 | 255 255 255 255 255 255 248 248 248 241 241 241 255 251 240 248 248 248 55 | 255 255 255 248 248 248 255 251 240 248 248 248 255 255 255 241 241 241 56 | 51 204 255 0 204 204 0 204 255 0 204 255 0 204 255 0 204 204 0 204 204 57 | 51 153 204 0 204 255 0 204 255 0 204 255 0 204 204 51 204 204 153 255 255 58 | 248 248 248 255 251 240 241 241 241 248 248 248 248 248 248 255 251 240 59 | 255 251 240 255 255 255 248 248 248 248 248 248 255 251 240 204 255 255 60 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 61 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 62 | 255 255 255 255 255 255 248 248 248 255 255 255 248 248 248 255 255 255 63 | 248 248 248 255 251 240 255 255 255 166 202 240 0 204 255 0 153 204 0 102 204 64 | 0 153 204 0 204 255 0 153 153 51 102 153 51 51 102 0 102 102 0 204 204 65 | 0 204 204 0 204 255 0 204 255 0 204 255 0 204 204 102 204 255 255 255 255 66 | 255 255 204 255 251 240 255 251 240 255 251 240 255 251 240 255 251 240 67 | 248 248 248 248 248 248 248 248 248 255 251 240 255 255 255 255 255 255 68 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 69 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 70 | 255 255 255 255 255 255 255 255 255 255 251 240 255 251 240 255 255 255 71 | 153 255 255 51 204 255 51 204 255 0 153 204 0 102 153 51 51 102 160 160 164 72 | 255 204 255 241 241 241 204 255 255 234 234 234 160 160 164 51 51 102 51 102 153 73 | 0 204 255 0 204 255 0 204 255 51 153 255 204 236 255 241 241 241 255 255 255 74 | 255 255 255 241 241 241 204 236 255 255 251 240 255 255 204 255 251 240 75 | 255 255 255 241 241 241 255 255 255 255 255 255 255 255 255 255 255 255 76 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 77 | 255 255 255 255 255 255 255 255 255 255 251 240 255 255 255 255 255 255 78 | 255 255 255 255 251 240 248 248 248 204 255 255 51 204 204 0 204 255 51 153 153 79 | 0 128 128 51 153 153 204 236 255 255 251 240 255 255 204 255 255 204 255 255 204 80 | 255 251 240 255 251 240 241 241 241 102 102 153 51 153 204 0 204 204 0 204 255 81 | 51 204 255 51 204 255 51 255 255 51 255 255 51 204 255 51 204 255 0 204 255 82 | 102 204 255 241 241 241 248 248 248 248 248 248 248 248 248 255 255 255 83 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 84 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 85 | 255 251 240 255 255 255 255 255 255 255 255 255 255 255 255 241 241 241 86 | 102 204 204 0 204 204 0 153 204 0 51 51 102 153 153 241 241 241 241 241 241 87 | 255 251 240 255 255 204 255 255 204 255 251 240 255 251 240 204 255 204 88 | 255 255 204 255 251 240 119 119 119 0 128 128 0 153 204 0 204 255 0 204 255 89 | 0 204 255 0 204 255 0 128 128 0 102 102 0 153 204 0 204 255 102 204 255 90 | 234 234 234 241 241 241 255 255 204 255 255 255 255 255 255 255 255 255 91 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 92 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 93 | 255 255 255 255 251 240 255 255 255 204 255 255 51 204 204 0 204 255 51 204 255 94 | 102 102 102 248 248 248 241 241 241 255 251 240 255 251 240 255 251 240 95 | 255 251 240 255 251 240 255 251 240 231 231 214 255 255 204 255 255 204 96 | 255 255 255 57 57 57 51 102 153 0 204 204 0 204 255 0 204 255 0 204 255 97 | 51 102 102 66 66 66 51 204 204 0 204 255 0 204 255 51 204 204 153 255 255 98 | 241 241 241 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 99 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 100 | 255 255 255 255 255 255 248 248 248 255 255 255 248 248 248 255 251 240 101 | 255 255 255 153 204 255 0 204 255 0 204 255 0 51 102 204 204 204 255 255 255 102 | 255 251 240 241 241 241 255 251 240 255 251 240 255 251 240 255 251 240 103 | 255 251 240 241 241 241 255 251 240 255 255 204 255 255 204 234 234 234 104 | 0 51 102 51 204 204 0 204 255 51 204 255 51 153 204 51 51 0 102 51 51 66 66 66 105 | 51 153 153 51 204 204 0 204 255 0 153 255 102 204 204 248 248 248 255 255 255 106 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 107 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 108 | 255 255 255 248 248 248 255 251 240 255 255 255 102 204 255 0 204 255 0 204 255 109 | 51 51 102 248 248 248 255 251 240 255 251 240 255 251 240 255 255 204 255 255 204 110 | 255 255 204 255 251 240 255 251 240 248 248 248 102 102 102 239 214 198 111 | 255 251 240 255 255 255 102 153 204 0 204 204 0 204 204 51 102 153 153 102 51 112 | 255 102 51 255 102 51 255 102 51 204 102 51 153 102 51 51 153 153 0 204 255 113 | 0 204 204 153 204 204 248 248 248 255 255 255 255 255 255 255 255 255 255 255 255 114 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 115 | 255 255 255 248 248 248 255 255 255 255 251 240 255 255 255 248 248 248 116 | 51 204 255 0 204 255 0 204 255 51 153 153 241 241 241 255 255 204 255 255 204 117 | 255 251 240 255 251 240 255 255 204 255 255 204 102 102 102 204 204 204 118 | 231 231 214 102 102 153 57 57 57 255 251 240 255 251 240 204 236 255 0 128 128 119 | 51 153 102 255 124 128 204 102 51 204 51 51 255 102 51 204 51 0 204 102 51 120 | 255 102 51 204 102 51 0 204 204 51 204 255 0 204 255 204 236 255 255 251 240 121 | 255 251 240 248 248 248 255 255 255 255 255 255 248 248 248 255 255 255 122 | 255 255 255 255 255 255 255 255 255 255 255 255 248 248 248 255 255 255 123 | 255 251 240 255 255 255 248 248 248 51 204 204 0 204 255 0 204 255 0 102 153 124 | 248 248 248 255 255 204 255 255 204 255 251 240 255 251 240 255 251 240 125 | 231 231 214 150 150 150 22 22 22 234 234 234 51 51 102 51 51 51 255 251 240 126 | 255 251 240 241 241 241 57 57 57 255 204 153 255 102 51 102 51 51 204 102 102 127 | 204 51 51 153 102 51 153 51 51 255 102 51 255 102 51 85 85 85 0 204 255 128 | 0 204 255 153 204 255 255 251 240 255 251 240 255 255 255 255 255 255 255 255 255 129 | 255 255 255 248 248 248 255 255 255 255 255 255 255 255 255 255 255 255 130 | 255 255 255 248 248 248 255 255 255 255 255 255 248 248 248 51 204 204 131 | 0 204 255 0 204 204 0 102 153 248 248 248 255 255 204 255 255 204 255 251 240 132 | 255 251 240 255 251 240 215 215 215 95 95 95 51 0 51 248 248 248 128 128 128 133 | 150 150 150 255 251 240 255 251 240 255 204 204 204 51 51 255 102 51 204 102 0 134 | 204 153 0 204 102 0 153 51 0 255 153 51 255 102 51 255 102 51 255 102 51 135 | 204 102 51 0 204 204 0 204 255 153 204 255 255 251 240 255 251 240 255 255 255 136 | 255 255 255 255 255 255 255 251 240 248 248 248 255 255 255 255 255 255 137 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 248 248 248 138 | 248 248 248 51 204 204 0 204 255 0 204 255 0 102 153 227 227 227 255 255 204 139 | 255 251 240 255 251 240 255 251 240 255 251 240 255 251 240 178 178 178 140 | 160 160 164 255 255 204 160 160 164 255 255 255 255 251 240 255 255 204 141 | 255 204 204 204 102 51 255 102 51 153 102 51 153 102 51 153 51 51 153 102 51 142 | 204 51 51 204 102 0 153 102 0 255 102 51 255 102 0 0 153 204 0 204 255 143 | 153 204 255 248 248 248 255 255 255 255 255 255 248 248 248 255 255 255 144 | 255 251 240 248 248 248 255 255 255 255 255 255 255 255 255 255 255 255 145 | 248 248 248 255 255 255 248 248 248 248 248 248 248 248 248 102 204 255 146 | 0 204 255 0 204 255 0 102 153 192 192 192 255 251 240 255 251 240 255 251 240 147 | 255 251 240 255 251 240 255 255 204 255 255 204 255 255 255 227 227 227 148 | 153 102 204 102 102 153 215 215 215 255 251 240 255 251 240 204 153 102 149 | 204 153 51 153 102 51 204 204 51 204 204 51 204 153 51 153 153 0 255 255 51 150 | 153 102 0 255 102 51 255 51 51 0 153 255 51 204 204 204 255 255 255 255 255 151 | 255 255 255 255 255 255 248 248 248 248 248 248 255 251 240 255 255 255 152 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 153 | 248 248 248 255 251 240 255 255 255 153 204 255 0 204 204 0 204 255 0 153 204 154 | 102 153 153 241 241 241 255 251 240 255 251 240 255 251 240 255 255 204 155 | 255 255 204 255 255 204 248 248 248 102 102 153 153 102 255 153 102 255 156 | 51 102 153 255 251 240 255 255 204 239 214 198 153 102 51 255 102 51 204 204 51 157 | 153 102 0 204 153 51 204 204 51 153 153 0 204 102 51 255 102 102 102 51 51 158 | 0 204 255 51 204 204 248 248 248 248 248 248 248 248 248 255 255 255 248 248 248 159 | 248 248 248 255 251 240 255 255 255 255 255 255 255 255 255 255 255 255 160 | 255 255 255 255 251 240 255 255 255 248 248 248 255 251 240 255 255 255 161 | 204 255 255 0 204 204 0 204 255 0 204 255 102 153 153 204 236 255 255 251 240 162 | 255 255 204 255 255 204 255 251 240 255 255 204 255 251 240 150 150 150 163 | 102 102 204 153 102 255 153 102 255 102 102 153 215 215 215 255 251 240 164 | 255 255 204 102 51 51 204 102 102 204 102 51 204 51 51 153 51 51 153 51 0 165 | 204 153 51 204 102 102 102 51 102 51 204 204 0 204 204 153 255 255 255 255 255 166 | 255 251 240 255 251 240 255 255 255 248 248 248 241 241 241 255 251 240 167 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 251 240 168 | 255 255 255 248 248 248 255 251 240 248 248 248 204 255 255 51 204 204 169 | 0 204 255 0 204 255 51 102 102 166 202 240 255 251 240 255 255 204 255 251 240 170 | 248 248 248 255 251 240 215 215 215 0 0 102 0 51 102 0 0 204 102 102 255 171 | 102 102 153 192 192 192 255 251 240 255 251 240 134 134 134 51 153 204 172 | 102 51 0 153 51 102 204 102 102 204 102 51 102 51 51 85 85 85 51 204 204 173 | 0 204 255 51 204 204 248 248 248 241 241 241 255 251 240 255 251 240 255 255 255 174 | 248 248 248 241 241 241 255 251 240 248 248 248 255 255 255 255 255 255 175 | 255 255 255 255 255 255 255 251 240 255 255 255 248 248 248 255 251 240 176 | 248 248 248 204 255 255 51 204 204 0 204 255 0 204 255 0 51 102 166 202 240 177 | 255 251 240 255 255 204 255 251 240 255 255 255 255 255 255 178 178 178 178 | 0 0 128 0 51 102 0 51 153 102 102 204 153 51 153 204 204 204 255 255 255 179 | 255 251 240 153 204 204 0 102 204 192 220 192 234 234 234 221 221 221 204 236 255 180 | 51 153 153 0 153 204 0 204 255 51 204 255 248 248 248 255 255 255 248 248 248 181 | 255 251 240 255 251 240 255 255 255 248 248 248 241 241 241 255 251 240 182 | 248 248 248 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 183 | 248 248 248 255 255 255 255 251 240 248 248 248 204 255 255 51 204 204 184 | 0 204 255 0 204 255 0 102 153 203 203 203 255 255 204 255 255 204 241 241 241 185 | 231 231 214 255 255 204 160 160 164 0 51 153 0 51 153 0 51 153 102 102 204 186 | 102 51 204 255 204 255 255 255 204 255 255 204 255 251 240 102 102 153 187 | 241 241 241 204 236 255 204 236 255 241 241 241 51 51 102 0 102 153 0 204 204 188 | 204 236 255 248 248 248 255 255 255 255 255 255 255 255 255 255 255 255 189 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 190 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 191 | 255 251 240 248 248 248 204 255 255 51 204 204 0 204 255 0 204 255 0 102 102 192 | 227 227 227 255 255 204 255 251 240 241 241 241 255 251 240 255 255 204 193 | 178 178 178 0 0 128 0 51 153 0 51 153 102 102 204 153 102 204 255 255 255 194 | 255 251 240 192 220 192 134 134 134 241 241 241 234 234 234 204 236 255 195 | 204 236 255 241 241 241 51 51 102 51 153 204 102 204 255 255 255 255 255 251 240 196 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 197 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 198 | 255 255 255 248 248 248 255 255 255 255 255 255 255 251 240 248 248 248 199 | 153 255 255 51 204 255 0 204 255 0 204 255 51 102 153 241 241 241 255 255 204 200 | 255 251 240 150 150 150 255 251 240 255 251 240 203 203 203 0 51 102 0 51 153 201 | 51 51 153 153 102 204 153 102 204 204 204 204 160 160 164 192 192 192 241 241 241 202 | 234 234 234 204 236 255 204 236 255 234 234 234 234 234 234 51 102 153 203 | 51 204 255 204 236 255 255 251 240 255 251 240 255 255 255 255 255 255 204 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 205 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 248 248 248 206 | 255 255 255 255 255 255 255 251 240 248 248 248 102 204 255 0 204 255 0 204 255 207 | 0 204 255 102 153 102 255 251 240 255 255 204 255 251 240 215 215 215 77 77 77 208 | 203 203 203 248 248 248 51 102 102 51 51 204 102 102 204 102 51 153 153 153 204 209 | 227 227 227 241 241 241 204 236 255 204 236 255 234 234 234 204 236 255 210 | 234 234 234 241 241 241 153 153 204 0 153 153 51 204 255 248 248 248 255 251 240 211 | 248 248 248 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 212 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 213 | 255 255 255 255 255 255 248 248 248 255 255 255 255 255 255 255 255 255 214 | 241 241 241 51 204 204 0 204 255 0 204 255 0 204 204 134 134 134 255 251 240 215 | 255 251 240 255 251 240 255 255 255 192 192 192 95 95 95 160 160 164 153 153 204 216 | 51 51 153 102 102 153 227 227 227 241 241 241 234 234 234 204 236 255 204 236 255 217 | 204 236 255 234 234 234 204 236 255 227 227 227 248 248 248 102 153 153 218 | 0 204 204 0 204 255 204 236 255 255 251 240 248 248 248 255 255 255 255 255 255 219 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 220 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 248 248 248 221 | 255 255 255 255 255 255 248 248 248 204 255 255 51 204 204 0 204 255 0 204 255 222 | 0 102 102 192 220 192 255 251 240 255 251 240 255 251 240 255 251 240 255 251 240 223 | 173 169 144 221 221 221 241 241 241 241 241 241 248 248 248 227 227 227 224 | 234 234 234 227 227 227 204 236 255 204 236 255 234 234 234 234 234 234 225 | 234 234 234 204 236 255 241 241 241 102 153 153 51 204 204 0 204 255 102 255 255 226 | 255 255 255 248 248 248 255 255 255 255 255 255 255 255 255 255 255 255 227 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 228 | 255 255 255 255 255 255 255 255 255 255 251 240 255 255 255 255 255 255 229 | 248 248 248 102 204 255 0 204 204 0 204 255 51 153 204 0 51 102 255 255 255 230 | 231 231 214 255 251 240 255 251 240 255 255 204 255 251 240 255 251 240 231 | 178 178 178 234 234 234 234 234 234 234 234 234 234 234 234 227 227 227 232 | 241 241 241 234 234 234 234 234 234 234 234 234 234 234 234 234 234 234 233 | 204 236 255 204 236 255 166 202 240 51 153 153 0 204 255 0 204 204 241 241 241 234 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 235 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 236 | 255 255 255 255 255 255 255 251 240 255 255 255 255 255 255 204 255 255 237 | 51 204 204 0 204 255 0 204 255 0 153 153 153 153 153 255 255 204 231 231 214 238 | 255 251 240 255 251 240 255 255 204 255 255 204 255 251 240 192 192 192 239 | 178 178 178 234 234 234 241 241 241 241 241 241 204 236 255 234 234 234 240 | 234 234 234 234 234 234 204 236 255 204 236 255 227 227 227 204 236 255 241 | 204 236 255 241 241 241 51 102 102 0 204 204 0 204 255 102 204 204 248 248 248 242 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 243 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 244 | 255 255 255 255 251 240 255 255 255 248 248 248 102 204 255 0 204 204 0 204 255 245 | 0 204 255 51 102 153 234 234 234 239 214 198 255 251 240 255 251 240 255 251 240 246 | 255 255 204 255 255 204 255 251 240 255 251 240 178 178 178 134 134 134 247 | 150 150 150 192 192 192 241 241 241 204 236 255 234 234 234 234 234 234 248 | 204 236 255 204 236 255 204 255 204 234 234 234 204 236 255 248 248 248 249 | 153 153 153 0 102 153 0 204 255 0 204 204 166 202 240 255 255 255 248 248 248 250 | 255 255 255 255 255 255 255 255 255 248 248 248 255 255 255 255 255 255 251 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 248 248 248 252 | 255 255 255 153 255 255 0 204 204 0 204 255 0 204 255 0 204 204 102 153 204 253 | 255 255 204 239 214 198 255 255 255 192 192 192 178 178 178 255 255 255 254 | 255 251 240 255 255 204 255 251 240 255 251 240 255 251 240 204 204 204 255 | 192 192 192 241 241 241 241 241 241 234 234 234 234 234 234 234 234 234 256 | 234 234 234 255 204 255 204 236 255 204 236 255 234 234 234 255 204 255 257 | 57 57 57 0 153 153 0 204 255 0 204 255 204 236 255 241 241 241 241 241 241 258 | 241 241 241 248 248 248 241 241 241 248 248 248 248 248 248 255 251 240 259 | 255 255 255 255 255 255 255 255 255 255 255 255 241 241 241 255 255 255 260 | 51 204 255 0 204 255 0 204 255 0 204 255 51 153 204 255 251 240 160 160 164 261 | 255 251 240 255 251 240 150 150 150 150 150 150 204 204 204 255 251 240 262 | 255 251 240 255 255 204 255 251 240 231 231 214 128 128 128 248 248 248 263 | 241 241 241 204 236 255 204 236 255 234 234 234 234 234 234 204 236 255 264 | 204 236 255 204 236 255 204 236 255 204 236 255 241 241 241 153 204 204 265 | 0 51 102 0 153 204 0 204 255 102 204 204 241 241 241 255 255 255 255 251 240 266 | 255 251 240 255 255 255 255 255 255 248 248 248 255 251 240 255 255 255 267 | 255 255 255 255 255 255 255 255 255 255 251 240 153 204 255 0 204 255 0 204 255 268 | 51 204 255 51 153 153 227 227 227 231 231 214 178 178 178 255 251 240 204 204 204 269 | 203 203 203 241 241 241 134 134 134 255 251 240 255 251 240 255 255 204 270 | 255 255 204 255 251 240 203 203 203 204 204 204 204 236 255 204 236 255 271 | 234 234 234 234 234 234 234 234 234 204 236 255 204 236 255 204 236 255 272 | 204 236 255 234 234 234 227 227 227 241 241 241 102 102 153 0 153 204 0 204 255 273 | 0 204 204 204 236 255 255 255 255 248 248 248 241 241 241 255 255 255 255 255 255 274 | 248 248 248 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 275 | 241 241 241 0 204 255 0 204 255 0 153 255 0 102 153 192 192 192 255 255 204 276 | 150 150 150 241 241 241 255 251 240 192 192 192 215 215 215 241 241 241 277 | 150 150 150 255 251 240 255 251 240 255 255 204 255 251 240 255 251 240 278 | 234 234 234 160 160 164 241 241 241 204 236 255 204 236 255 234 234 234 279 | 204 236 255 204 236 255 234 234 234 234 234 234 234 234 234 234 234 234 280 | 234 234 234 234 234 234 241 241 241 51 153 153 0 204 204 0 204 204 102 153 204 281 | 255 255 255 248 248 248 248 248 248 255 255 255 255 255 255 255 255 255 282 | 241 241 241 255 255 255 255 255 255 255 255 255 255 255 255 102 204 255 283 | 0 204 204 0 204 255 0 102 153 150 150 150 255 255 255 178 178 178 204 204 153 284 | 255 255 255 255 251 240 160 160 164 234 234 234 234 234 234 160 160 164 285 | 241 241 241 255 251 240 231 231 214 255 251 240 178 178 178 178 178 178 286 | 178 178 178 241 241 241 204 236 255 204 236 255 234 234 234 204 236 255 287 | 204 236 255 255 204 255 204 236 255 204 236 255 204 236 255 234 234 234 288 | 234 234 234 241 241 241 204 236 255 51 51 102 0 153 204 0 153 204 204 255 255 289 | 255 255 255 255 251 240 248 248 248 255 255 255 248 248 248 248 248 248 290 | 255 255 255 255 255 255 255 255 255 255 255 255 0 204 255 0 204 255 51 153 204 291 | 119 119 119 255 255 255 234 234 234 128 128 128 255 255 204 255 255 255 292 | 255 251 240 153 153 153 234 234 234 234 234 234 192 192 192 221 221 221 293 | 255 251 240 221 221 221 255 255 255 150 150 150 215 215 215 234 234 234 294 | 204 236 255 204 236 255 234 234 234 234 234 234 204 236 255 204 236 255 295 | 234 234 234 204 236 255 204 236 255 234 234 234 234 234 234 241 241 241 296 | 241 241 241 153 204 204 102 153 153 0 153 204 0 204 204 102 255 255 248 248 248 297 | 255 251 240 248 248 248 248 248 248 255 255 255 248 248 248 255 255 255 298 | 255 255 255 255 255 255 255 255 255 0 204 255 0 204 204 102 102 153 255 255 255 299 | 204 236 255 153 204 204 178 178 178 255 255 255 255 251 240 234 234 234 300 | 153 153 153 241 241 241 234 234 234 215 215 215 192 192 192 215 215 215 301 | 134 134 134 215 215 215 153 153 153 204 236 255 204 236 255 204 236 255 302 | 204 236 255 234 234 234 234 234 234 234 234 234 234 234 234 204 236 255 303 | 234 234 234 234 234 234 255 255 255 192 192 192 51 102 153 0 102 153 0 153 153 304 | 0 153 204 51 204 255 0 204 204 153 255 255 255 255 255 255 255 255 255 255 255 305 | 248 248 248 255 255 255 248 248 248 255 255 255 255 255 255 255 255 255 306 | 255 255 255 204 236 255 0 204 204 51 153 153 248 248 248 248 248 248 134 134 134 307 | 255 251 240 241 241 241 255 255 204 203 203 203 192 192 192 241 241 241 308 | 204 236 255 241 241 241 192 192 192 178 178 178 241 241 241 192 192 192 309 | 241 241 241 234 234 234 241 241 241 204 236 255 204 236 255 234 234 234 310 | 234 234 234 234 234 234 234 234 234 204 236 255 241 241 241 241 241 241 311 | 51 102 102 0 153 153 51 204 255 51 204 255 0 204 255 0 204 255 0 204 204 312 | 102 204 204 241 241 241 255 255 255 255 255 255 255 255 255 255 255 255 313 | 255 255 255 248 248 248 255 255 255 255 255 255 255 255 255 255 255 255 314 | 248 248 248 102 255 204 0 204 204 51 153 153 66 66 66 204 204 204 255 251 240 315 | 255 255 204 255 251 240 178 178 178 234 234 234 204 236 255 204 236 255 316 | 204 236 255 241 241 241 234 234 234 234 234 234 234 234 234 204 236 255 317 | 204 236 255 204 236 255 204 236 255 204 236 255 204 236 255 234 234 234 318 | 234 234 234 234 234 234 204 255 255 204 204 204 28 28 28 0 102 102 0 153 153 319 | 0 204 204 0 204 255 0 204 255 0 204 255 153 255 255 248 248 248 248 248 248 320 | 248 248 248 255 251 240 255 255 255 248 248 248 255 255 255 241 241 241 321 | 255 255 255 255 255 255 255 255 255 255 255 255 255 251 240 248 248 248 322 | 0 204 255 0 204 255 0 128 128 204 255 255 241 241 241 239 214 198 178 178 178 323 | 204 153 204 241 241 241 204 236 255 204 236 255 204 236 255 204 236 255 324 | 204 236 255 234 234 234 241 241 241 204 236 255 204 236 255 234 234 234 325 | 234 234 234 227 227 227 241 241 241 204 236 255 204 236 255 241 241 241 326 | 134 134 134 51 51 0 51 51 0 102 51 0 51 51 0 66 66 66 51 102 102 51 204 204 327 | 0 204 255 102 204 255 248 248 248 255 251 240 255 255 204 255 251 240 255 255 255 328 | 248 248 248 255 255 255 248 248 248 255 255 255 255 255 255 255 255 255 329 | 255 255 255 248 248 248 255 255 255 51 204 255 0 204 255 0 204 255 51 153 153 330 | 0 51 51 8 8 8 4 4 4 153 204 153 221 221 221 241 241 241 241 241 241 234 234 234 331 | 234 234 234 204 236 255 204 236 255 204 236 255 204 236 255 234 234 234 332 | 227 227 227 227 227 227 227 227 227 241 241 241 248 248 248 192 192 192 333 | 34 34 34 102 51 0 153 102 0 102 102 0 102 102 0 102 51 0 153 51 0 153 51 51 334 | 0 51 51 51 204 153 51 204 204 102 204 255 241 241 241 255 255 255 248 248 248 335 | 255 255 255 255 255 255 255 255 255 248 248 248 255 255 255 255 255 255 336 | 255 255 255 255 255 255 255 255 255 248 248 248 0 204 255 0 204 255 51 153 153 337 | 51 102 51 102 51 51 102 51 0 51 51 0 17 17 17 22 22 22 34 34 34 95 95 95 338 | 160 160 164 234 234 234 248 248 248 241 241 241 204 236 255 241 241 241 339 | 241 241 241 234 234 234 248 248 248 248 248 248 178 178 178 51 51 102 51 51 51 340 | 85 85 85 51 102 102 51 102 102 0 128 128 0 128 128 0 128 128 51 102 153 341 | 51 153 204 0 153 204 0 204 255 0 204 255 51 153 204 204 255 255 241 241 241 342 | 248 248 248 248 248 248 255 255 255 255 255 255 255 251 240 255 255 255 343 | 255 255 255 255 255 255 255 255 255 248 248 248 204 236 255 51 204 204 344 | 51 102 102 102 51 51 153 51 0 153 51 0 153 51 0 153 51 0 102 51 51 102 51 51 345 | 77 77 77 51 51 51 51 51 51 57 57 57 66 66 66 134 134 134 192 192 192 204 204 204 346 | 204 204 204 192 192 192 51 153 153 0 102 102 0 153 153 0 204 255 0 204 255 347 | 0 204 255 0 204 255 0 204 255 0 204 255 0 204 204 0 204 255 0 204 255 0 204 255 348 | 0 204 255 51 204 255 102 204 204 153 204 255 241 241 241 255 255 255 248 248 248 349 | 255 255 255 248 248 248 248 248 248 241 241 241 255 255 255 255 255 255 350 | 255 255 255 255 255 255 248 248 248 153 255 204 0 204 204 0 153 153 66 66 66 351 | 66 66 66 51 102 102 0 128 128 0 128 128 0 153 153 0 153 204 0 204 204 0 204 255 352 | 0 204 255 0 204 255 0 204 204 0 153 204 0 153 153 0 153 204 0 153 204 0 153 204 353 | 51 153 204 51 204 204 102 204 204 102 204 204 166 202 240 204 236 255 241 241 241 354 | 241 241 241 241 241 241 241 241 241 204 255 255 204 255 255 241 241 241 355 | 248 248 248 248 248 248 248 248 248 255 255 255 255 255 255 255 255 255 356 | 255 255 255 248 248 248 248 248 248 248 248 248 255 255 255 255 255 255 357 | 255 255 255 255 255 255 255 255 255 248 248 248 234 234 234 102 204 255 358 | 51 204 255 51 204 204 51 204 204 51 204 255 51 204 255 51 204 255 102 204 255 359 | 102 204 255 102 204 255 102 204 255 102 204 255 102 204 255 51 204 255 360 | 51 204 255 102 204 255 153 204 255 153 204 255 204 236 255 204 255 255 361 | 204 255 255 248 248 248 248 248 248 255 255 255 255 255 255 255 255 255 362 | 248 248 248 248 248 248 248 248 248 248 248 248 255 255 255 248 248 248 363 | 248 248 248 255 255 255 255 251 240 255 251 240 255 251 240 255 255 255 364 | 255 255 255 248 248 248 255 255 255 248 248 248 248 248 248 255 255 255 365 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 366 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 367 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 368 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 369 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 370 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 371 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 372 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 373 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 374 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 375 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 376 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 377 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 378 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 379 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 380 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 381 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 382 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 383 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 384 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 385 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 386 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 387 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 388 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 389 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 390 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 391 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 392 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 393 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 394 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 395 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 396 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 397 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 398 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 399 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 400 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 401 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 402 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 403 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 404 | 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 405 | 255 255 255 255 255 255 255 255 255 255 255 255 406 | -------------------------------------------------------------------------------- /spec/samples/huge.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcupitt/ruby-vips8/99c93e36676341e579e3c66090fe34f679b36e59/spec/samples/huge.jpg -------------------------------------------------------------------------------- /spec/samples/icc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcupitt/ruby-vips8/99c93e36676341e579e3c66090fe34f679b36e59/spec/samples/icc.jpg -------------------------------------------------------------------------------- /spec/samples/lcd.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcupitt/ruby-vips8/99c93e36676341e579e3c66090fe34f679b36e59/spec/samples/lcd.icc -------------------------------------------------------------------------------- /spec/samples/lion.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 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 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /spec/samples/sample.csv: -------------------------------------------------------------------------------- 1 | 0,0,0,255,0,0,0 2 | 0,0,0,255,0,0,0 3 | 0,0,0,255,0,0,0 4 | 0,0,0,255,0,0,0 5 | 0,255,0,255,0,0,0 6 | 0,255,0,255,0,0,0 7 | 0,0,255,255,0,0,0 8 | -------------------------------------------------------------------------------- /spec/samples/sample.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcupitt/ruby-vips8/99c93e36676341e579e3c66090fe34f679b36e59/spec/samples/sample.exr -------------------------------------------------------------------------------- /spec/samples/wagon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcupitt/ruby-vips8/99c93e36676341e579e3c66090fe34f679b36e59/spec/samples/wagon.jpg -------------------------------------------------------------------------------- /spec/samples/wagon.v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcupitt/ruby-vips8/99c93e36676341e579e3c66090fe34f679b36e59/spec/samples/wagon.v -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'vips8' 2 | 3 | require 'tempfile' 4 | 5 | module Spec 6 | 7 | module Path 8 | def root 9 | @root ||= Pathname.new(File.expand_path('..', __FILE__)) 10 | end 11 | 12 | def sample(*path) 13 | root.join 'samples', *path 14 | end 15 | 16 | def tmp(*path) 17 | root.join 'tmp', 'working', *path 18 | end 19 | 20 | extend self 21 | 22 | end 23 | 24 | module Helpers 25 | def reset_working! 26 | FileUtils.rm Dir[tmp.join('*.*')] 27 | FileUtils.mkdir_p(tmp) 28 | end 29 | end 30 | 31 | end 32 | 33 | def simg(name) 34 | Spec::Path::sample(name).to_s 35 | end 36 | 37 | def timg(name) 38 | Spec::Path::tmp(name).to_s 39 | end 40 | 41 | RSpec.configure do |config| 42 | config.include Spec::Path 43 | config.include Spec::Helpers 44 | 45 | config.before :each do 46 | reset_working! 47 | end 48 | 49 | end 50 | -------------------------------------------------------------------------------- /spec/vips_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper.rb' 2 | 3 | RSpec.describe Vips do 4 | describe '#call' do 5 | it 'can make a black image' do 6 | image = Vips::call "black", 200, 200 7 | 8 | expect(image.width).to eq(200) 9 | expect(image.height).to eq(200) 10 | expect(image.bands).to eq(1) 11 | end 12 | 13 | it 'can take an optional argument' do 14 | image = Vips::call "black", 200, 200, :bands => 12 15 | 16 | expect(image.width).to eq(200) 17 | expect(image.height).to eq(200) 18 | expect(image.bands).to eq(12) 19 | end 20 | 21 | it 'can take an optional argument' do 22 | image = Vips::call "black", 200, 200, :bands => 12 23 | 24 | expect(image.width).to eq(200) 25 | expect(image.height).to eq(200) 26 | expect(image.bands).to eq(12) 27 | end 28 | 29 | it 'can handle enum arguments' do 30 | black = Vips::call "black", 200, 200 31 | embed = Vips::call "embed", black, 10, 10, 500, 500, 32 | :extend => :mirror 33 | 34 | expect(embed.width).to eq(500) 35 | expect(embed.height).to eq(500) 36 | expect(embed.bands).to eq(1) 37 | end 38 | 39 | it 'enum arguments can be strings' do 40 | black = Vips::call "black", 200, 200 41 | embed = Vips::call "embed", black, 10, 10, 500, 500, 42 | :extend => "mirror" 43 | 44 | expect(embed.width).to eq(500) 45 | expect(embed.height).to eq(500) 46 | expect(embed.bands).to eq(1) 47 | end 48 | 49 | it 'can return optional output args' do 50 | point = Vips::call "black", 1, 1 51 | test = Vips::call "embed", point, 20, 10, 100, 100, 52 | :extend => :white 53 | value, opts = Vips::call "min", test, :x => true, :y => true 54 | 55 | expect(value).to eq(0) 56 | expect(opts['x']).to eq(20) 57 | expect(opts['y']).to eq(10) 58 | end 59 | 60 | it 'can call draw operations' do 61 | black = Vips::call "black", 100, 100 62 | test = Vips::call "draw_rect", black, 255, 10, 10, 1, 1 63 | 64 | max_black = Vips::call "max", black 65 | max_test = Vips::call "max", test 66 | 67 | expect(max_black).to eq(0) 68 | expect(max_test).to eq(255) 69 | end 70 | 71 | end 72 | 73 | end 74 | 75 | --------------------------------------------------------------------------------