under-os-crop 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []