vips 8.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.travis.yml +47 -0
  4. data/.yardopts +2 -0
  5. data/Gemfile +4 -0
  6. data/README.md +64 -0
  7. data/Rakefile +28 -0
  8. data/example/annotate.rb +17 -0
  9. data/example/daltonize8.rb +75 -0
  10. data/example/example1.rb +12 -0
  11. data/example/example2.rb +34 -0
  12. data/example/example3.rb +19 -0
  13. data/example/example4.rb +18 -0
  14. data/example/example5.rb +31 -0
  15. data/example/inheritance_with_refcount.rb +286 -0
  16. data/example/thumb.rb +31 -0
  17. data/example/trim8.rb +41 -0
  18. data/example/watermark.rb +44 -0
  19. data/example/wobble.rb +36 -0
  20. data/ext/Rakefile +35 -0
  21. data/lib/vips.rb +597 -0
  22. data/lib/vips/access.rb +11 -0
  23. data/lib/vips/align.rb +11 -0
  24. data/lib/vips/angle.rb +12 -0
  25. data/lib/vips/angle45.rb +16 -0
  26. data/lib/vips/bandformat.rb +20 -0
  27. data/lib/vips/coding.rb +14 -0
  28. data/lib/vips/compass_direction.rb +17 -0
  29. data/lib/vips/direction.rb +11 -0
  30. data/lib/vips/extend.rb +17 -0
  31. data/lib/vips/gobject.rb +122 -0
  32. data/lib/vips/gvalue.rb +281 -0
  33. data/lib/vips/image.rb +1500 -0
  34. data/lib/vips/interesting.rb +14 -0
  35. data/lib/vips/interpolate.rb +62 -0
  36. data/lib/vips/interpretation.rb +28 -0
  37. data/lib/vips/kernel.rb +22 -0
  38. data/lib/vips/methods.rb +2145 -0
  39. data/lib/vips/object.rb +244 -0
  40. data/lib/vips/operation.rb +365 -0
  41. data/lib/vips/operationboolean.rb +14 -0
  42. data/lib/vips/operationcomplex.rb +12 -0
  43. data/lib/vips/operationcomplex2.rb +10 -0
  44. data/lib/vips/operationcomplexget.rb +11 -0
  45. data/lib/vips/operationmath.rb +18 -0
  46. data/lib/vips/operationmath2.rb +10 -0
  47. data/lib/vips/operationrelational.rb +15 -0
  48. data/lib/vips/operationround.rb +11 -0
  49. data/lib/vips/size.rb +13 -0
  50. data/lib/vips/version.rb +4 -0
  51. data/vips.gemspec +36 -0
  52. metadata +209 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 17ebdb3047de6d3b6982f72e06bcb58dbadd39af057918e601970818646b4007
4
+ data.tar.gz: ecd94b19ffba51de569bdf7bd58398b9b23cd05dc5e617e08ca0508cafb8099d
5
+ SHA512:
6
+ metadata.gz: 7587de2d7115ab17b0af59e4cbd304ceff1a9a3c94d8448a6850a07b9296f7291dcd5ce89f35c0bba912cc7049401a6c05457c49765ff727688f4948fdd682c6
7
+ data.tar.gz: e80bd5ba9216eb25f167ae2aa5a1b3328f603688bacdea269ed78b7620fd2689775e2614f2aca76a9f18851445f936cf53fc9a9dbb53f7040e93676c22734323
@@ -0,0 +1,16 @@
1
+ .yardoc
2
+ *.swp
3
+ doc
4
+ pkg/*.gem
5
+
6
+ # skip Gemfile.lock since this is a gem
7
+ Gemfile.lock
8
+
9
+ /bin/
10
+ /include/
11
+ /share/
12
+ /lib/lib*
13
+ /lib/girepository-1.0
14
+ /lib/pkgconfig
15
+
16
+ /ext/vips-*
@@ -0,0 +1,47 @@
1
+ sudo: false
2
+
3
+ addons:
4
+ apt:
5
+ packages:
6
+ - libexpat1-dev
7
+ - gettext
8
+ - liblcms2-dev
9
+ - libmagickwand-dev
10
+ - libopenexr-dev
11
+ - libcfitsio3-dev
12
+ - libgif-dev
13
+ - libgs-dev
14
+ - libgsf-1-dev
15
+ - libmatio-dev
16
+ - libopenslide-dev
17
+ - liborc-0.4-dev
18
+ - libpango1.0-dev
19
+ - libpoppler-glib-dev
20
+ - librsvg2-dev
21
+ - libwebp-dev
22
+ # missing on trusty, unfortunately
23
+ # - libwebpmux2
24
+ - libfftw3-dev
25
+ - libglib2.0-dev
26
+
27
+ cache:
28
+ bundler: true
29
+ directories: $HOME/vips/ext
30
+
31
+ language: ruby
32
+ rvm:
33
+ - 2.3
34
+ - 2.4
35
+ - 2.5
36
+ - 2.6
37
+
38
+ before_script:
39
+ - gem update --system
40
+ - gem install bundler
41
+ - bundle exec rake ext
42
+
43
+ script:
44
+ - bundle exec rake
45
+
46
+ gemfile:
47
+ - Gemfile
@@ -0,0 +1,2 @@
1
+ --markup-provider=redcarpet
2
+ --markup=markdown
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # all gems are handled by .gemspec (since this repo is for a gem)
4
+ gemspec
@@ -0,0 +1,64 @@
1
+ # Vips
2
+
3
+ This gem is a backwards compatible fork of `ruby-vips` but also includes (and compiles) the [libvips] source code.
4
+
5
+ [![Build Status](https://secure.travis-ci.org/ioquatix/vips.svg)](http://travis-ci.org/ioquatix/vips)
6
+
7
+ [libvips]: https://jcupitt.github.io/libvips
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'vips'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install vips
24
+
25
+ You'll still need to install `glib` and `gobject-introspection`.
26
+
27
+ ## Usage
28
+
29
+ Exactly the same way as [ruby-vips].
30
+
31
+ [ruby-vips]: https://github.com/jcupitt/ruby-vips
32
+
33
+ ## Contributing
34
+
35
+ 1. Fork it
36
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
37
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
38
+ 4. Push to the branch (`git push origin my-new-feature`)
39
+ 5. Create new Pull Request
40
+
41
+ ## License
42
+
43
+ Released under the MIT license.
44
+
45
+ Copyright, 2014, by John Cupitt.
46
+ Copyright, 2017, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
47
+
48
+ Permission is hereby granted, free of charge, to any person obtaining a copy
49
+ of this software and associated documentation files (the "Software"), to deal
50
+ in the Software without restriction, including without limitation the rights
51
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
52
+ copies of the Software, and to permit persons to whom the Software is
53
+ furnished to do so, subject to the following conditions:
54
+
55
+ The above copyright notice and this permission notice shall be included in
56
+ all copies or substantial portions of the Software.
57
+
58
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
59
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
60
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
61
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
62
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
63
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
64
+ THE SOFTWARE.
@@ -0,0 +1,28 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:test)
5
+
6
+ task :default => :test
7
+
8
+ task :ext do
9
+ Dir.chdir "ext" do
10
+ sh "rake"
11
+ end
12
+ end
13
+
14
+ task :console do
15
+ require 'pry'
16
+
17
+ require_relative 'lib/vips'
18
+
19
+ Pry.start
20
+ end
21
+
22
+ require "yard/rake/yardoc_task"
23
+
24
+ YARD::Rake::YardocTask.new do |yard|
25
+ require "yard"
26
+ require "github/markup"
27
+ require "redcarpet"
28
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'vips'
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]
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # daltonize an image with ruby-vips
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 'vips'
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]
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'logger'
4
+ require 'vips'
5
+
6
+ GLib::logger.level = Logger::DEBUG
7
+
8
+ op = Vips::Operation.new "black"
9
+
10
+ op = nil
11
+ GC.start
12
+ Vips::Operation.print_all
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'logger'
4
+ require 'vips'
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
+ # GLib::logger.level = Logger::DEBUG
16
+
17
+ n = 10000
18
+
19
+ n.times do |i|
20
+ puts ""
21
+ puts "call #{i} ..."
22
+ out = Vips::Operation.call "black", [200, 300]
23
+ if out.width != 200 or out.height != 300
24
+ puts "bad image result from black"
25
+ end
26
+ end
27
+
28
+ puts ""
29
+ puts "after #{n} calls:"
30
+ GC.start
31
+ Vips::Object::print_all
32
+
33
+ puts ""
34
+ puts "shutting down:"
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'vips'
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
+ GLib::logger.level = Logger::DEBUG
13
+
14
+ 1.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
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'vips'
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
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'vips'
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]
@@ -0,0 +1,286 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'ffi'
4
+ require 'forwardable'
5
+
6
+ # this is really very crude logging
7
+
8
+ # @private
9
+ $vips_debug = true
10
+
11
+ # @private
12
+ def log str
13
+ if $vips_debug
14
+ puts str
15
+ end
16
+ end
17
+
18
+ def set_debug debug
19
+ $vips_debug = debug
20
+ end
21
+
22
+ module Libc
23
+ extend FFI::Library
24
+ ffi_lib FFI::Library::LIBC
25
+
26
+ attach_function :malloc, [:size_t], :pointer
27
+ attach_function :free, [:pointer], :void
28
+ end
29
+
30
+ module GLib
31
+ extend FFI::Library
32
+ ffi_lib 'gobject-2.0'
33
+
34
+ # nil being the default
35
+ glib_log_domain = nil
36
+
37
+ def self.set_log_domain(domain)
38
+ glib_log_domain = domain
39
+ end
40
+
41
+ # we have a set of things we need to inherit in different ways:
42
+ #
43
+ # - we want to be able to subclass GObject in a simple way
44
+ # - the layouts of the nested structs
45
+ # - casting between structs which share a base
46
+ # - gobject refcounting
47
+ #
48
+ # the solution is to split the class into four areas which we treat
49
+ # differently:
50
+ #
51
+ # - we have a "wrapper" Ruby class to allow easy subclassing ... this has a
52
+ # @struct member which holds the actual pointer
53
+ # - we use "forwardable" to forward the various ffi methods on to the
54
+ # @struct member ... we arrange things so that subclasses do not need to
55
+ # do the forwarding themselves
56
+ # - we have two versions of the struct: a plain one which we can use for
57
+ # casting that will not change the refcounts
58
+ # - and a managed one with an unref which we just use for .new
59
+ # - we separate the struct layout into a separate module to avoid repeating
60
+ # ourselves
61
+
62
+ class GObject
63
+ extend Forwardable
64
+ extend SingleForwardable
65
+
66
+ def_instance_delegators :@struct, :[], :to_ptr
67
+ def_single_delegators :ffi_struct, :ptr
68
+
69
+ # the layout of the GObject struct
70
+ module GObjectLayout
71
+ def self.included(base)
72
+ base.class_eval do
73
+ layout :g_type_instance, :pointer,
74
+ :ref_count, :uint,
75
+ :qdata, :pointer
76
+ end
77
+ end
78
+ end
79
+
80
+ # the struct with unref ... manage object lifetime with this
81
+ class ManagedStruct < FFI::ManagedStruct
82
+ include GObjectLayout
83
+
84
+ def initialize(ptr)
85
+ log "GLib::GObject::ManagedStruct.new: #{ptr}"
86
+ super
87
+ end
88
+
89
+ def self.release(ptr)
90
+ log "GLib::GObject::ManagedStruct.release: unreffing #{ptr}"
91
+ GLib::g_object_unref(ptr) unless ptr.null?
92
+ end
93
+
94
+ end
95
+
96
+ # the plain struct ... cast with this
97
+ class Struct < FFI::Struct
98
+ include GObjectLayout
99
+
100
+ def initialize(ptr)
101
+ log "GLib::GObject::Struct.new: #{ptr}"
102
+ super
103
+ end
104
+
105
+ end
106
+
107
+ # don't allow ptr == nil, we never want to allocate a GObject struct
108
+ # ourselves, we just want to wrap GLib-allocated GObjects
109
+ #
110
+ # here we use ManagedStruct, not Struct, since this is the ref that will
111
+ # need the unref
112
+ def initialize(ptr)
113
+ log "GLib::GObject.initialize: ptr = #{ptr}"
114
+ @struct = ffi_managed_struct.new(ptr)
115
+ end
116
+
117
+ # access to the cast struct for this class
118
+ def ffi_struct
119
+ self.class.ffi_struct
120
+ end
121
+
122
+ class << self
123
+ def ffi_struct
124
+ self.const_get(:Struct)
125
+ end
126
+ end
127
+
128
+ # access to the lifetime managed struct for this class
129
+ def ffi_managed_struct
130
+ self.class.ffi_managed_struct
131
+ end
132
+
133
+ class << self
134
+ def ffi_managed_struct
135
+ self.const_get(:ManagedStruct)
136
+ end
137
+ end
138
+
139
+ end
140
+
141
+ # :gtype will usually be 64-bit, but will be 32-bit on 32-bit Windows
142
+ typedef :ulong, :GType
143
+
144
+ end
145
+
146
+ module Vips
147
+ extend FFI::Library
148
+ ffi_lib 'vips'
149
+
150
+ LOG_DOMAIN = "VIPS"
151
+ GLib::set_log_domain(LOG_DOMAIN)
152
+
153
+ # need to repeat this
154
+ typedef :ulong, :GType
155
+
156
+ attach_function :vips_init, [:string], :int
157
+ attach_function :vips_shutdown, [], :void
158
+
159
+ attach_function :vips_error_buffer, [], :string
160
+ attach_function :vips_error_clear, [], :void
161
+
162
+ def self.get_error
163
+ errstr = Vips::vips_error_buffer
164
+ Vips::vips_error_clear
165
+ errstr
166
+ end
167
+
168
+ if Vips::vips_init($0) != 0
169
+ puts Vips::get_error
170
+ exit 1
171
+ end
172
+
173
+ at_exit {
174
+ Vips::vips_shutdown
175
+ }
176
+
177
+ attach_function :vips_object_print_all, [], :void
178
+ attach_function :vips_leak_set, [:int], :void
179
+
180
+ def self.showall
181
+ if $vips_debug
182
+ GC.start
183
+ vips_object_print_all
184
+ end
185
+ end
186
+
187
+ if $vips_debug
188
+ vips_leak_set 1
189
+ end
190
+
191
+ class VipsObject < GLib::GObject
192
+
193
+ # the layout of the VipsObject struct
194
+ module VipsObjectLayout
195
+ def self.included(base)
196
+ base.class_eval do
197
+ # don't actually need most of these, remove them later
198
+ layout :parent, GLib::GObject::Struct,
199
+ :constructed, :int,
200
+ :static_object, :int,
201
+ :argument_table, :pointer,
202
+ :nickname, :string,
203
+ :description, :string,
204
+ :preclose, :int,
205
+ :close, :int,
206
+ :postclose, :int,
207
+ :local_memory, :size_t
208
+ end
209
+ end
210
+ end
211
+
212
+ class Struct < GLib::GObject::Struct
213
+ include VipsObjectLayout
214
+
215
+ def initialize(ptr)
216
+ log "Vips::VipsObject::Struct.new: #{ptr}"
217
+ super
218
+ end
219
+
220
+ end
221
+
222
+ class ManagedStruct < GLib::GObject::ManagedStruct
223
+ include VipsObjectLayout
224
+
225
+ def initialize(ptr)
226
+ log "Vips::VipsObject::ManagedStruct.new: #{ptr}"
227
+ super
228
+ end
229
+
230
+ end
231
+
232
+ end
233
+
234
+ class VipsImage < VipsObject
235
+
236
+ # the layout of the VipsImage struct
237
+ module VipsImageLayout
238
+ def self.included(base)
239
+ base.class_eval do
240
+ layout :parent, VipsObject::Struct
241
+ # rest opaque
242
+ end
243
+ end
244
+ end
245
+
246
+ class Struct < VipsObject::Struct
247
+ include VipsImageLayout
248
+
249
+ def initialize(ptr)
250
+ log "Vips::VipsImage::Struct.new: #{ptr}"
251
+ super
252
+ end
253
+
254
+ end
255
+
256
+ class ManagedStruct < VipsObject::ManagedStruct
257
+ include VipsImageLayout
258
+
259
+ def initialize(ptr)
260
+ log "Vips::VipsImage::ManagedStruct.new: #{ptr}"
261
+ super
262
+ end
263
+
264
+ end
265
+
266
+ def self.new_partial
267
+ VipsImage.new(Vips::vips_image_new)
268
+ end
269
+
270
+ end
271
+
272
+ attach_function :vips_image_new, [], :pointer
273
+
274
+ end
275
+
276
+ puts "creating image"
277
+ begin
278
+ x = Vips::VipsImage.new_partial
279
+ puts "x = #{x}"
280
+ puts ""
281
+ puts "x[:parent] = #{x[:parent]}"
282
+ puts ""
283
+ puts "x[:parent][:description] = #{x[:parent][:description]}"
284
+ puts ""
285
+ end
286
+