under-os-crop 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile +3 -0
- data/README.md +75 -0
- data/lib/assets/uos-crop.css +30 -0
- data/lib/under-os-crop.rb +1 -0
- data/lib/under_os/crop.rb +96 -0
- data/lib/under_os/crop/processor.rb +94 -0
- data/lib/under_os/crop/scroll.rb +37 -0
- data/lib/under_os/crop/window.rb +77 -0
- data/lib/under_os/styles.rb +2 -0
- data/resources/Default-568h@2x.png +0 -0
- data/under-os-crop.gemspec +19 -0
- metadata +70 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -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
|
Binary file
|
@@ -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: []
|