under-os-crop 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 959af2e74aa93432122b4fd46402818a414f53bf
4
+ data.tar.gz: fa0ebb332fb43498c11de8c2f3c4ba67ac795158
5
+ SHA512:
6
+ metadata.gz: d3f40c0cd23bd720bbf6769b8311cb2c5c4e216189cbb50e77b86c0e26a9c35167a76ba5597e6edad800b496b94324cb16a4331a06a30b1c0e966f5c259ebc40
7
+ data.tar.gz: 55c6742e4e0fa43ea4e384ce00a3a167fae0b1dd1aae0e554c8e1943e64dd71dc317527f2d4b7513b84d5650bed5a275065cdd5905ed6d70516e5a08602b880a
@@ -0,0 +1,2 @@
1
+ .repl_history
2
+ build/
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+ gem 'under-os-ui'
3
+ gemspec
@@ -0,0 +1,75 @@
1
+ # UnderOS Crop
2
+
3
+ This is a nice image crop plugin for the [under-os](http://under-os.com) project.
4
+
5
+ ## Usage
6
+
7
+ Add it to your `Gemfile`
8
+
9
+ ```ruby
10
+ gem `under-os`
11
+ gem `under-os-crop`
12
+ ```
13
+
14
+ Then you can either use it as a `CROP` element in your HTML markup
15
+
16
+ ```html
17
+ <page title="Image crop">
18
+ <crop id="the-crop"></crop>
19
+ </page>
20
+ ```
21
+
22
+ Or spawn it programmatically in your `UOS::Page` controller
23
+
24
+ ```ruby
25
+ class CropPage < UOS::Page
26
+ def initialize
27
+ @crop = UOS::Crop.new
28
+ end
29
+ end
30
+ ```
31
+
32
+ Either way the `UOS::Crop` class is a subclass of the `UOS::UI::View` so
33
+ all the normal under-os rules apply.
34
+
35
+ ## Cropping images
36
+
37
+ Once you've got an instance of the `UOS::Crop` class, you can give it an
38
+ instance of a `UIImage` class as the image that needs to be cropped and
39
+ then read it the same way from the same `#src` property
40
+
41
+ ```ruby
42
+ # set a new image to crop
43
+ @crop.src = UIImage.alloc.initWithImage("test.png")
44
+
45
+ # whenever the user is done, you can read it back
46
+ @crop.src # -> the cropped image
47
+ ```
48
+
49
+ ## Setting Aspect Ratio
50
+
51
+ You can set an aspect ratio for the crop widget as well through the `#ratio` property
52
+
53
+ ```ruby
54
+ @crop.ratio = "3:4"
55
+ @crop.ratio = "1:1"
56
+ @crop.ratio = nil # no ratio
57
+ ```
58
+
59
+ ## Tilting & Turning
60
+
61
+ You also can set the image tilt programmatically (say from a slider in UI) or turn
62
+ the image 90 degrees with the `#turn` and `#tilt` methods
63
+
64
+ ```ruby
65
+ @crop.turn Math::PI / 4 # turn 90 degrees
66
+ @crop.tilt Math::PI / 12 # tilt image by 30 degrees
67
+ ```
68
+
69
+ ## Copyright & License
70
+
71
+ All the code in this repository is released under the terms of the MIT license
72
+
73
+ Copyright (C) 2014 Nikolay Nemshilov
74
+
75
+
@@ -0,0 +1,30 @@
1
+ .crop {
2
+ border-radius: 0;
3
+ }
4
+ .crop scroll {
5
+ width: 100%;
6
+ height: 100%;
7
+ overflow: hidden;
8
+ }
9
+
10
+ .crop .overlay {
11
+ background: #000;
12
+ opacity: 0.7;
13
+ z-index: 1;
14
+ }
15
+
16
+ .crop .window {
17
+ z-index: 2;
18
+ border-width: 1px;
19
+ border-color: rgba(200,200,200,0.5);
20
+ }
21
+
22
+ .crop .window .bar {
23
+ background: rgba(255,255,255,0.3);
24
+ }
25
+ .crop .window .bar.horizontal {
26
+ height: 1px;
27
+ }
28
+ .crop .window .bar.vertical {
29
+ width: 1px;
30
+ }
@@ -0,0 +1 @@
1
+ UnderOs.extend __FILE__
@@ -0,0 +1,96 @@
1
+ #
2
+ # An image crop UI
3
+ #
4
+ module UnderOs
5
+ class Crop < UnderOs::UI::View
6
+ tag :crop
7
+
8
+ attr_reader :ratio, :image
9
+
10
+ def initialize(options={})
11
+ super options.merge(class: 'crop')
12
+
13
+ append @scroll = Scroll.new
14
+ append @window = Window.new
15
+
16
+ @processor = Processor.new
17
+ self.ratio = nil
18
+ end
19
+
20
+ def ratio=(ratio)
21
+ ratio = ratio.to_s
22
+ ratio = "#{@original.size.width}:#{@original.size.height}" if ratio == 'true'
23
+
24
+ if m = ratio.match(/^([\d\.+]+):([\d\.]+)$/)
25
+ @ratio = m[1].to_f / m[2].to_f
26
+ else
27
+ @ratio = nil
28
+ end
29
+
30
+ @window.expand @ratio if self.size.x > 0
31
+ end
32
+
33
+ def repaint(*args)
34
+ super(*args).tap{ @window.expand @ratio if @window }
35
+ end
36
+
37
+ def src
38
+ @processor.render *original_crop_rectangle
39
+ end
40
+
41
+ def src=(image)
42
+ @processor.image = image
43
+ @original = @processor.image # fixed original
44
+ reset
45
+ end
46
+
47
+ def reset
48
+ @scroll.resetting!
49
+ render { @processor.reset }
50
+ end
51
+
52
+ def turn(angle)
53
+ render { @processor.turn(angle) }
54
+ end
55
+
56
+ def tilt(angle)
57
+ render { @processor.tilt(angle) }
58
+ end
59
+
60
+ protected
61
+
62
+ def render(&block)
63
+ return if @_working; @_working = true
64
+
65
+ Dispatch::Queue.new("uos.gems.crop.tilt").async do
66
+ new_image = block.call
67
+
68
+ Dispatch::Queue.main.async do
69
+ @scroll.image = new_image
70
+ @_working = false
71
+ end
72
+ end
73
+ end
74
+
75
+ def original_crop_rectangle
76
+ content_size = @scroll.contentSize / @scroll.scale
77
+ crop_offset = @scroll.contentOffset / @scroll.scale + @window.position / 2 + 1
78
+ crop_frame = @window.size / @scroll.scale
79
+
80
+ crop_offset.x = 0 if crop_offset.x < 0 # tall image
81
+ crop_offset.y = 0 if crop_offset.y < 0 # wide image
82
+
83
+ crop_frame.x = content_size.x if crop_frame.x > content_size.x
84
+ crop_frame.y = content_size.y if crop_frame.y > content_size.y
85
+
86
+ original_is_h = @original.size.width / @original.size.height > 1
87
+ content_is_h = content_size.x / content_size.y > 1
88
+ original_scale = @original.size.width / content_size.__send__(original_is_h == content_is_h ? :x : :y)
89
+
90
+ original_offset = crop_offset * original_scale
91
+ original_frame = crop_frame * original_scale
92
+
93
+ [original_offset.x, original_offset.y, original_frame.x, original_frame.y]
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,94 @@
1
+ class UnderOs::Crop::Processor
2
+
3
+ def initialize
4
+ @tilt_filter = CIFilter.filterWithName("CIStraightenFilter")
5
+ @turn_filter = CIFilter.filterWithName("CIAffineTransform")
6
+ @crop_filter = CIFilter.filterWithName("CICrop")
7
+ @context = begin
8
+ gl_context = EAGLContext.alloc.initWithAPI(KEAGLRenderingAPIOpenGLES2)
9
+ options = {KCIContextWorkingColorSpace => nil}
10
+ CIContext.contextWithEAGLContext(gl_context, options:options)
11
+ end
12
+ end
13
+
14
+ def image
15
+ @original
16
+ end
17
+
18
+ def image=(image)
19
+ @original = fix_orientation(image)
20
+
21
+ resize(@original).tap do |ui_image|
22
+ @resized_original = ui_image
23
+ end
24
+ end
25
+
26
+ def reset
27
+ @resized_original.tap do |ui_image|
28
+ @working = CIImage.alloc.initWithImage(ui_image)
29
+ end
30
+ end
31
+
32
+ def turn(angle)
33
+ transform = NSValue.valueWithCGAffineTransform(CGAffineTransformMakeRotation(angle))
34
+ @turn_filter.setValue(transform, forKey:"inputTransform")
35
+ reset
36
+ apply(@turn_filter).tap{ |image| @working = CIImage.alloc.initWithImage(image) }
37
+ apply(@tilt_filter)
38
+ end
39
+
40
+ def tilt(angle)
41
+ @tilt_filter.setValue(angle, forKey:"inputAngle")
42
+ apply @tilt_filter
43
+ end
44
+
45
+ def crop(x, y, width, height)
46
+ img_size = UIImage.alloc.initWithCIImage(@working).size
47
+ y = img_size.height - y - height # this filter counts from the bottom
48
+ rectangle = CIVector.vectorWithX(x, Y:y, Z:width, W:height)
49
+ @crop_filter.setValue(rectangle, forKey:'inputRectangle')
50
+ apply @crop_filter
51
+ end
52
+
53
+ def render(*crop_rect)
54
+ @_working_image = @working
55
+
56
+ @working = CIImage.alloc.initWithImage(@original)
57
+ @working = CIImage.alloc.initWithImage(apply(@turn_filter))
58
+ @working = CIImage.alloc.initWithImage(apply(@tilt_filter))
59
+
60
+ crop(*crop_rect).tap{ |i| @working = @_working_image }
61
+ end
62
+
63
+ protected
64
+
65
+ def apply(filter)
66
+ filter.setValue(@working, forKey: 'inputImage')
67
+
68
+ image = filter.outputImage
69
+ cg_image = @context.createCGImage(image, fromRect:image.extent)
70
+ image = UIImage.imageWithCGImage(cg_image)
71
+
72
+ CGImageRelease(cg_image)
73
+ filter.setValue(nil, forKey: 'inputImage')
74
+
75
+ image
76
+ end
77
+
78
+ def fix_orientation(image)
79
+ resize(image, image.size.width)
80
+ end
81
+
82
+ def resize(image, max_width=nil)
83
+ max_width ||= UOS::Screen.size.x * 4 # double the retina for zoom
84
+ ratio = max_width / image.size.width
85
+ new_size = CGSizeMake(max_width, image.size.height * ratio)
86
+
87
+ UIGraphicsBeginImageContext(new_size)
88
+ image.drawInRect(CGRectMake(0,0,new_size.width,new_size.height))
89
+ new_image = UIGraphicsGetImageFromCurrentImageContext()
90
+ UIGraphicsEndImageContext()
91
+
92
+ new_image
93
+ end
94
+ end
@@ -0,0 +1,37 @@
1
+ class UnderOs::Crop < UnderOs::UI::View
2
+ class Scroll < UOS::UI::Scroll
3
+ attr_reader :image
4
+
5
+ def initialize(options={})
6
+ super options
7
+
8
+ UnderOs::App.history.current_page._.automaticallyAdjustsScrollViewInsets = false
9
+
10
+ self.minScale = 1.0
11
+ self.maxScale = 3.0
12
+ @_.decelerationRate = 0.2
13
+
14
+ append @image = UOS::UI::Image.new
15
+
16
+ self.zoomItem = @image
17
+ self.on(:zoom) { centerContent }
18
+ end
19
+
20
+ def image=(src)
21
+ @image.src = src
22
+ self.scale = 1.0 and @resetting = nil if @resetting
23
+
24
+ scale = [self.size.x / src.size.width, self.size.y / src.size.height].min
25
+ @new_size = {x: src.size.width * scale * self.scale, y: src.size.height * scale * self.scale}
26
+
27
+ @image.size = @new_size
28
+ self.contentSize = @new_size
29
+
30
+ centerContent
31
+ end
32
+
33
+ def resetting!
34
+ @resetting = true
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,77 @@
1
+ class UnderOs::Crop::Window < UnderOs::UI::View
2
+ def initialize(options={})
3
+ super class: 'window'
4
+
5
+ @_.userInteractionEnabled = false
6
+
7
+ append @over_1 = Overlay.new
8
+ append @over_2 = Overlay.new
9
+
10
+ append @bar_h1 = Bar.new('horizontal')
11
+ append @bar_h2 = Bar.new('horizontal')
12
+
13
+ append @bar_v1 = Bar.new('vertical')
14
+ append @bar_v2 = Bar.new('vertical')
15
+ end
16
+
17
+ def expand(ratio)
18
+ parent_size = parent.size
19
+ parent_ratio = parent_size.x / parent_size.y
20
+ ratio ||= parent_ratio
21
+ new_size = {x: parent_size.x, y: parent_size.y}
22
+
23
+ if ratio > parent_ratio
24
+ new_size[:y] = parent_size.x / ratio
25
+ else
26
+ new_size[:x] = parent_size.y * ratio
27
+ end
28
+
29
+ self.size = {x: new_size[:x] + 2, y: new_size[:y] + 2}
30
+ end
31
+
32
+ def size=(new_size)
33
+ super new_size
34
+
35
+ self.position = {
36
+ x: parent.size.x / 2 - size.x / 2,
37
+ y: parent.size.y / 2 - size.y / 2
38
+ }
39
+
40
+ move_bars
41
+ move_overlays
42
+ end
43
+
44
+ def move_bars
45
+ @bar_h1.position.y = size.y / 3
46
+ @bar_h2.position.y = size.y / 3 * 2
47
+ @bar_v1.position.x = size.x / 3
48
+ @bar_v2.position.x = size.x / 3 * 2
49
+
50
+ @bar_h1.size.x = size.x
51
+ @bar_h2.size.x = size.x
52
+ @bar_v1.size.y = size.y
53
+ @bar_v2.size.y = size.y
54
+ end
55
+
56
+ def move_overlays
57
+ if position.x > position.y # vertical
58
+ @over_1.style = {width: position.x, height: size.y, top: 0, left: -position.x}
59
+ @over_2.style = {width: position.x, height: size.y, top: 0, right: -position.x}
60
+ else # horizontal
61
+ @over_1.style = {width: size.x, height: position.y, left: 0, top: -position.y}
62
+ @over_2.style = {width: size.x, height: position.y, left: 0, bottom: -position.y}
63
+ end
64
+ end
65
+
66
+ class Bar < UnderOs::UI::View
67
+ def initialize(orientation)
68
+ super class: "bar #{orientation}"
69
+ end
70
+ end
71
+
72
+ class Overlay < UnderOs::UI::View
73
+ def initialize(options={})
74
+ super class: 'overlay'
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,2 @@
1
+ $stylesheets ||= []
2
+ $stylesheets << "uos-crop.css"
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = "under-os-crop"
5
+ gem.version = '1.0.0'
6
+
7
+ gem.authors = ["Nikolay Nemshilov"]
8
+ gem.email = ['nemshilov@gmail.com']
9
+ gem.description = "The images crop UI for UnderOS"
10
+ gem.summary = "The images crop UI for UnderOS. Like totes"
11
+ gem.license = 'MIT'
12
+
13
+ gem.files = `git ls-files`.split($/).reject{|f| f.slice(0,5) == 'gems/'}
14
+ gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
15
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
16
+
17
+ gem.add_runtime_dependency 'under-os-ui'
18
+
19
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: under-os-crop
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Nikolay Nemshilov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: under-os-ui
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: The images crop UI for UnderOS
28
+ email:
29
+ - nemshilov@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".gitignore"
35
+ - Gemfile
36
+ - README.md
37
+ - lib/assets/uos-crop.css
38
+ - lib/under-os-crop.rb
39
+ - lib/under_os/crop.rb
40
+ - lib/under_os/crop/processor.rb
41
+ - lib/under_os/crop/scroll.rb
42
+ - lib/under_os/crop/window.rb
43
+ - lib/under_os/styles.rb
44
+ - resources/Default-568h@2x.png
45
+ - under-os-crop.gemspec
46
+ homepage:
47
+ licenses:
48
+ - MIT
49
+ metadata: {}
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project:
66
+ rubygems_version: 2.2.2
67
+ signing_key:
68
+ specification_version: 4
69
+ summary: The images crop UI for UnderOS. Like totes
70
+ test_files: []