sugarcube 1.1.0 → 1.3.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.
- data/Gemfile.lock +1 -1
- data/README.md +73 -10
- data/lib/sugarcube-animations/caanimation.rb +31 -0
- data/lib/sugarcube-animations/calayer.rb +7 -0
- data/lib/sugarcube-animations/uiview.rb +59 -34
- data/lib/sugarcube-attributedstring/nsattributedstring.rb +7 -0
- data/lib/sugarcube-color/nsstring.rb +1 -1
- data/lib/sugarcube-color/symbol.rb +2 -2
- data/lib/sugarcube-color/uicolor.rb +56 -10
- data/lib/sugarcube-corelocation/core_location.rb +12 -3
- data/lib/sugarcube-factories/uiactionsheet.rb +5 -4
- data/lib/sugarcube-factories/uialertview.rb +4 -5
- data/lib/sugarcube-image/ciimage.rb +2 -3
- data/lib/sugarcube-image/uiimage.rb +245 -97
- data/lib/sugarcube-nsdate/date_parser.rb +14 -12
- data/lib/sugarcube-nsdate/nsstring.rb +5 -0
- data/lib/sugarcube-nsdate/{fixnum.rb → numeric.rb} +12 -3
- data/lib/sugarcube-numbers/numeric.rb +7 -0
- data/lib/sugarcube-to_s/uilabel.rb +1 -1
- data/lib/sugarcube-to_s/uiview.rb +1 -2
- data/lib/sugarcube-uikit/uiview.rb +75 -5
- data/lib/sugarcube/version.rb +1 -1
- data/spec/nsattributedstring_spec.rb +4 -0
- data/spec/nsstring_files_spec.rb +18 -15
- data/spec/uiactionsheet_spec.rb +40 -2
- data/spec/uialertview_spec.rb +10 -3
- data/spec/uicolor_spec.rb +39 -0
- data/spec/uiimage_spec.rb +471 -0
- data/spec/uiview_spec.rb +90 -22
- metadata +9 -3
@@ -67,10 +67,10 @@ class Symbol
|
|
67
67
|
blue: 0x0000ff,
|
68
68
|
brown: 0x996633,
|
69
69
|
cyan: 0x00ffff,
|
70
|
-
|
70
|
+
dark_gray: 0x555555,
|
71
71
|
gray: 0x808080,
|
72
72
|
green: 0x00ff00,
|
73
|
-
|
73
|
+
light_gray: 0xaaaaaa,
|
74
74
|
magenta: 0xff00ff,
|
75
75
|
orange: 0xff8000,
|
76
76
|
purple: 0x800080,
|
@@ -51,7 +51,7 @@ class UIColor
|
|
51
51
|
r = (self.red + color.red) / 2
|
52
52
|
g = (self.green + color.green) / 2
|
53
53
|
b = (self.blue + color.blue) / 2
|
54
|
-
a =
|
54
|
+
a = self.alpha
|
55
55
|
UIColor.colorWithRed(r, green:g, blue:b, alpha:a)
|
56
56
|
else
|
57
57
|
a = (color.alpha - self.alpha) * amount + self.alpha
|
@@ -82,23 +82,40 @@ class UIColor
|
|
82
82
|
UIColor.colorWithRed(r, green:g, blue:b, alpha:a)
|
83
83
|
end
|
84
84
|
|
85
|
+
def hue
|
86
|
+
_sugarcube_hsb_colors && _sugarcube_hsb_colors[:hue]
|
87
|
+
end
|
88
|
+
|
89
|
+
def saturation
|
90
|
+
_sugarcube_hsb_colors && _sugarcube_hsb_colors[:saturation]
|
91
|
+
end
|
92
|
+
|
93
|
+
def brightness
|
94
|
+
_sugarcube_hsb_colors && _sugarcube_hsb_colors[:brightness]
|
95
|
+
end
|
96
|
+
|
85
97
|
def red
|
86
|
-
|
98
|
+
_sugarcube_rgb_colors && _sugarcube_rgb_colors[:red]
|
87
99
|
end
|
88
100
|
|
89
101
|
def green
|
90
|
-
|
102
|
+
_sugarcube_rgb_colors && _sugarcube_rgb_colors[:green]
|
91
103
|
end
|
92
104
|
|
93
105
|
def blue
|
94
|
-
|
106
|
+
_sugarcube_rgb_colors && _sugarcube_rgb_colors[:blue]
|
95
107
|
end
|
96
108
|
|
97
109
|
def alpha
|
98
|
-
|
110
|
+
if @sugarcube_hsb_colors
|
111
|
+
@sugarcube_hsb_colors[:alpha]
|
112
|
+
elsif _sugarcube_rgb_colors
|
113
|
+
_sugarcube_rgb_colors[:alpha]
|
114
|
+
end
|
99
115
|
end
|
100
116
|
|
101
|
-
# returns the components OR'd together, as 32 bit RGB integer.
|
117
|
+
# returns the components OR'd together, as 32 bit RGB integer. alpha channel
|
118
|
+
# is dropped
|
102
119
|
def to_i
|
103
120
|
if self.red && self.green && self.blue
|
104
121
|
red = (self.red * 255).round << 16
|
@@ -110,7 +127,8 @@ class UIColor
|
|
110
127
|
end
|
111
128
|
end
|
112
129
|
|
113
|
-
# returns the components as an array of 32 bit RGB values
|
130
|
+
# returns the components as an array of 32 bit RGB values. alpha channel is
|
131
|
+
# dropped
|
114
132
|
def to_a
|
115
133
|
if self.red && self.green && self.blue
|
116
134
|
red = (self.red * 255).round
|
@@ -161,13 +179,41 @@ private
|
|
161
179
|
return (c1.red - c2.red).abs + (c1.green - c2.green).abs + (c1.blue - c2.blue).abs
|
162
180
|
end
|
163
181
|
|
164
|
-
def
|
165
|
-
@
|
182
|
+
def _sugarcube_hsb_colors
|
183
|
+
@sugarcube_hsb_colors ||= begin
|
184
|
+
hue = Pointer.new(:float)
|
185
|
+
saturation = Pointer.new(:float)
|
186
|
+
brightness = Pointer.new(:float)
|
187
|
+
alpha = Pointer.new(:float)
|
188
|
+
white = Pointer.new(:float)
|
189
|
+
|
190
|
+
if self.getHue(hue, saturation:saturation, brightness:brightness, alpha:alpha)
|
191
|
+
{
|
192
|
+
hue: hue[0],
|
193
|
+
saturation: saturation[0],
|
194
|
+
brightness: brightness[0],
|
195
|
+
alpha: alpha[0],
|
196
|
+
}
|
197
|
+
elsif self.getWhite(white, alpha:alpha)
|
198
|
+
{
|
199
|
+
hue: 0,
|
200
|
+
saturation: 0,
|
201
|
+
brightness: white[0],
|
202
|
+
alpha: alpha[0],
|
203
|
+
}
|
204
|
+
else
|
205
|
+
nil
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def _sugarcube_rgb_colors
|
211
|
+
@sugarcube_rgb_colors ||= begin
|
166
212
|
red = Pointer.new(:float)
|
167
213
|
green = Pointer.new(:float)
|
168
214
|
blue = Pointer.new(:float)
|
169
|
-
white = Pointer.new(:float)
|
170
215
|
alpha = Pointer.new(:float)
|
216
|
+
white = Pointer.new(:float)
|
171
217
|
if self.getRed(red, green:green, blue:blue, alpha:alpha)
|
172
218
|
{
|
173
219
|
red: red[0],
|
@@ -1,12 +1,20 @@
|
|
1
1
|
# denver = CLLocationCoordinate2D.new(39.764032,-104.963112)
|
2
2
|
# cincinnati = CLLocationCoordinate2D.new(39.267024,-84.251736)
|
3
|
-
# denver.distance_to(cincinnati).
|
4
|
-
# denver.delta_miles()
|
3
|
+
# denver.distance_to(cincinnati).in_miles
|
4
|
+
# denver.delta_miles(1101.6, -32.556) # go east 1,101.6 miles, and south 32.556 miles
|
5
5
|
class CLLocationCoordinate2D
|
6
6
|
|
7
7
|
def delta_miles(dx, dy)
|
8
|
-
earth_radius = 3963.
|
8
|
+
earth_radius = 3963.19
|
9
|
+
_sugarcube_delta(dx, dy, earth_radius)
|
10
|
+
end
|
11
|
+
|
12
|
+
def delta_kilometers(dx, dy)
|
13
|
+
earth_radius = 6378.137
|
14
|
+
_sugarcube_delta(dx, dy, earth_radius)
|
15
|
+
end
|
9
16
|
|
17
|
+
def _sugarcube_delta(dx, dy, earth_radius)
|
10
18
|
radius_at_latitude = earth_radius * Math.cos(self.latitude * Math::PI / 180.0)
|
11
19
|
if radius_at_latitude > 0
|
12
20
|
delta_lng = dx / radius_at_latitude
|
@@ -19,6 +27,7 @@ class CLLocationCoordinate2D
|
|
19
27
|
delta_lat *= 180.0 / Math::PI
|
20
28
|
CLLocationCoordinate2D.new(self.latitude + delta_lat, self.longitude + delta_lng)
|
21
29
|
end
|
30
|
+
private :_sugarcube_delta
|
22
31
|
|
23
32
|
def distance_to(cl_location_2d)
|
24
33
|
my_location = CLLocation.alloc.initWithLatitude(self.latitude, longitude:self.longitude)
|
@@ -134,12 +134,13 @@ module SugarCube
|
|
134
134
|
def actionSheet(alert, didDismissWithButtonIndex:index)
|
135
135
|
handler = nil
|
136
136
|
if index == alert.destructiveButtonIndex && on_destructive
|
137
|
-
handler = on_destructive
|
137
|
+
handler = on_destructive
|
138
138
|
elsif index == alert.cancelButtonIndex && on_cancel
|
139
|
-
handler = on_cancel
|
140
|
-
|
141
|
-
handler = on_success
|
139
|
+
handler = on_cancel
|
140
|
+
elsif index != alert.destructiveButtonIndex && index != alert.cancelButtonIndex && on_success
|
141
|
+
handler = on_success
|
142
142
|
end
|
143
|
+
handler ||= on_default
|
143
144
|
|
144
145
|
if handler
|
145
146
|
if handler.arity == 0
|
@@ -85,16 +85,15 @@ module SugarCube
|
|
85
85
|
attr_accessor :on_default
|
86
86
|
|
87
87
|
def alertView(alert, didDismissWithButtonIndex:index)
|
88
|
-
cancel_handler = on_cancel || on_default
|
89
|
-
success_handler = on_success || on_default
|
90
88
|
handler = nil
|
91
|
-
|
92
89
|
if index == alert.cancelButtonIndex
|
93
|
-
handler =
|
90
|
+
handler = on_cancel
|
94
91
|
else
|
95
|
-
handler =
|
92
|
+
handler = on_success
|
96
93
|
end
|
94
|
+
handler ||= on_default
|
97
95
|
|
96
|
+
args = nil
|
98
97
|
if handler
|
99
98
|
if handler.arity == 0
|
100
99
|
args = []
|
@@ -1,10 +1,9 @@
|
|
1
1
|
class CIImage
|
2
2
|
|
3
3
|
def uiimage(scale=nil, orientation=nil)
|
4
|
-
if scale
|
4
|
+
if scale
|
5
|
+
orientation ||= UIImageOrientationUp
|
5
6
|
return UIImage.imageWithCIImage(self, scale: scale, orientation: orientation)
|
6
|
-
elsif scale && scale.is_a?(UIImage)
|
7
|
-
return UIImage.imageWithCIImage(self, scale: scale.scale, orientation: scale.imageOrientation)
|
8
7
|
else
|
9
8
|
return UIImage.imageWithCIImage(self)
|
10
9
|
end
|
@@ -1,49 +1,140 @@
|
|
1
|
+
##|
|
2
|
+
##| REALLY HANDY STUFF!
|
3
|
+
##| many of these methods are translated from:
|
4
|
+
##| <http://www.catamount.com/blog/uiimage-extensions-for-cutting-scaling-and-rotating-uiimages/>
|
5
|
+
##| <http://www.catamount.com/forums/viewtopic.php?f=21&t=967>
|
6
|
+
##|
|
1
7
|
class UIImage
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
# Easily create a UIImage by using this factory method, and do your drawing
|
12
|
+
# in a block. The core graphics context will be passed to the block you
|
13
|
+
# provide. To create a canvas based on an image, use the instance method.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# white_square = UIImage.canvas([100, 100]) do |context|
|
17
|
+
# :white.uicolor.set
|
18
|
+
# CGContextAddRect(context, [[0, 0], [100, 100]])
|
19
|
+
# CGContextDrawPath(context, KCGPathFill)
|
20
|
+
# end
|
21
|
+
# :size is required, :scale defaults to the screen scale, and :opaque is
|
22
|
+
# false by default.
|
23
|
+
#
|
24
|
+
# The first argument can be a size, or an options dict
|
25
|
+
def canvas(options_or_size={}, more_options={}, &block)
|
26
|
+
if options_or_size.is_a?(NSDictionary)
|
27
|
+
options = options_or_size
|
28
|
+
size = options[:size]
|
29
|
+
else
|
30
|
+
options = more_options
|
31
|
+
size = options_or_size
|
32
|
+
end
|
33
|
+
raise ":size is required in #{self.name}##canvas" unless size
|
34
|
+
scale = options[:scale] || UIScreen.mainScreen.scale
|
35
|
+
opaque = options.fetch(:opaque, false)
|
36
|
+
|
37
|
+
UIGraphicsBeginImageContextWithOptions(size, opaque, scale)
|
38
|
+
block.call(UIGraphicsGetCurrentContext()) if block
|
39
|
+
new_image = UIGraphicsGetImageFromCurrentImageContext()
|
40
|
+
UIGraphicsEndImageContext()
|
41
|
+
return new_image
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
8
45
|
|
9
46
|
# Merges the two images. The target is drawn first, `image` is drawn on top.
|
10
47
|
# The two images are centered, and the maximum size is used so that both
|
11
48
|
# images fit on the canvas.
|
12
49
|
def <<(image)
|
13
|
-
|
14
|
-
|
15
|
-
size.width = image.size.width
|
16
|
-
end
|
17
|
-
if image.size.height > size.height
|
18
|
-
size.height = image.size.height
|
19
|
-
end
|
50
|
+
self.merge(image, at: :center, stretch: true)
|
51
|
+
end
|
20
52
|
|
21
|
-
|
53
|
+
# Draw an image on top of the receiver. The `:at` option provides either an
|
54
|
+
# absolute location (Array or CGPoint) or relative location (Symbol, one of
|
55
|
+
# :top_left, :top, :top_right, :left, :center (default), :right, :bottom_left,
|
56
|
+
# :bottom, :bottom_right). The `:stretch` option increases the canvas so
|
57
|
+
# there is room for both images, otherwise the target image's size is used.
|
58
|
+
def merge(image, options={})
|
59
|
+
image_position = options.fetch(:at, :center)
|
60
|
+
stretch = options.fetch(:stretch, false)
|
22
61
|
|
23
|
-
|
24
|
-
|
62
|
+
size = self.size
|
63
|
+
if stretch
|
64
|
+
if image.size.width > size.width
|
65
|
+
size.width = image.size.width
|
66
|
+
end
|
67
|
+
if image.size.height > size.height
|
68
|
+
size.height = image.size.height
|
69
|
+
end
|
70
|
+
end
|
25
71
|
|
26
|
-
|
27
|
-
|
72
|
+
my_left = image_left = 0
|
73
|
+
my_top = image_top = 0
|
74
|
+
my_right = (size.width - self.size.width)
|
75
|
+
my_bottom = (size.height - self.size.height)
|
76
|
+
image_right = (size.width - image.size.width)
|
77
|
+
image_bottom = (size.height - image.size.height)
|
78
|
+
my_cx = my_right / 2.0
|
79
|
+
my_cy = my_bottom / 2.0
|
80
|
+
image_cx = image_right / 2.0
|
81
|
+
image_cy = image_bottom / 2.0
|
82
|
+
|
83
|
+
case image_position
|
84
|
+
when :top_left, :topleft, :tl
|
85
|
+
my_position = [my_right, my_bottom]
|
86
|
+
image_position = [image_left, image_top]
|
87
|
+
when :top, :t
|
88
|
+
my_position = [my_cx, my_bottom]
|
89
|
+
image_position = [image_cx, image_top]
|
90
|
+
when :top_right, :topright, :tr
|
91
|
+
my_position = [my_left, my_bottom]
|
92
|
+
image_position = [image_right, image_top]
|
93
|
+
when :left, :l
|
94
|
+
my_position = [my_right, my_cy]
|
95
|
+
image_position = [image_left, image_cy]
|
96
|
+
when :center, :c
|
97
|
+
my_position = [my_cx, my_cy]
|
98
|
+
image_position = [image_cx, image_cy]
|
99
|
+
when :right, :r
|
100
|
+
my_position = [my_left, my_cy]
|
101
|
+
image_position = [image_right, image_cy]
|
102
|
+
when :bottom_left, :bottomleft, :bl
|
103
|
+
my_position = [my_right, my_top]
|
104
|
+
image_position = [image_left, image_bottom]
|
105
|
+
when :bottom, :b
|
106
|
+
my_position = [my_cx, my_top]
|
107
|
+
image_position = [image_cx, image_bottom]
|
108
|
+
when :bottom_right, :bottomright, :br
|
109
|
+
my_position = [my_left, my_top]
|
110
|
+
image_position = [image_right, image_bottom]
|
111
|
+
end
|
28
112
|
|
29
|
-
|
30
|
-
|
31
|
-
|
113
|
+
return self.draw(size: size, at: my_position) do
|
114
|
+
image.drawAtPoint(image_position)
|
115
|
+
end
|
32
116
|
end
|
33
117
|
|
34
|
-
# Returns a cropped UIImage
|
118
|
+
# Returns a cropped UIImage. The easiest way is to check for a CGImage
|
119
|
+
# backing, but if this image uses a CIImage backing, we draw a new (cropped)
|
120
|
+
# image.
|
35
121
|
def crop(rect)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
result = UIImage.imageWithCGImage(cgimage, scale:self.scale, orientation:self.imageOrientation)
|
122
|
+
rect = SugarCube::CoreGraphics::Rect(rect)
|
123
|
+
if self.CGImage
|
124
|
+
if self.scale > 1.0
|
125
|
+
rect = CGRectMake(rect.origin.x * self.scale,
|
126
|
+
rect.origin.y * self.scale,
|
127
|
+
rect.size.width * self.scale,
|
128
|
+
rect.size.height * self.scale)
|
129
|
+
end
|
45
130
|
|
46
|
-
|
131
|
+
cgimage = CGImageCreateWithImageInRect(self.CGImage, rect)
|
132
|
+
return UIImage.imageWithCGImage(cgimage, scale:self.scale, orientation:self.imageOrientation)
|
133
|
+
else
|
134
|
+
return self.canvas(size: rect.size) do |context|
|
135
|
+
self.drawAtPoint(CGPoint.new(-rect.origin.x, -rect.origin.y))
|
136
|
+
end
|
137
|
+
end
|
47
138
|
end
|
48
139
|
|
49
140
|
##|
|
@@ -138,23 +229,23 @@ class UIImage
|
|
138
229
|
mid_x = max_x / 2
|
139
230
|
mid_y = max_y / 2
|
140
231
|
case position
|
141
|
-
when :top_left, :topleft
|
232
|
+
when :top_left, :topleft, :tl
|
142
233
|
position = CGPoint.new(min_x, min_y)
|
143
|
-
when :top
|
234
|
+
when :top, :t
|
144
235
|
position = CGPoint.new(mid_x, min_y)
|
145
|
-
when :top_right, :topright
|
236
|
+
when :top_right, :topright, :tr
|
146
237
|
position = CGPoint.new(max_x, min_y)
|
147
|
-
when :left
|
238
|
+
when :left, :l
|
148
239
|
position = CGPoint.new(min_x, mid_x)
|
149
|
-
when :center
|
240
|
+
when :center, :c
|
150
241
|
position = CGPoint.new(mid_x, mid_x)
|
151
|
-
when :right
|
242
|
+
when :right, :r
|
152
243
|
position = CGPoint.new(max_x, mid_x)
|
153
|
-
when :bottom_left, :bottomleft
|
244
|
+
when :bottom_left, :bottomleft, :bl
|
154
245
|
position = CGPoint.new(min_x, max_y)
|
155
|
-
when :bottom
|
246
|
+
when :bottom, :b
|
156
247
|
position = CGPoint.new(mid_x, max_y)
|
157
|
-
when :bottom_right, :bottomright
|
248
|
+
when :bottom_right, :bottomright, :br
|
158
249
|
position = CGPoint.new(max_x, max_y)
|
159
250
|
else
|
160
251
|
raise "Unknown position #{position.inspect}"
|
@@ -165,17 +256,15 @@ class UIImage
|
|
165
256
|
thumbnail_x = position.x * (new_size.width - my_size.width) / my_size.width
|
166
257
|
thumbnail_y = position.y * (new_size.height - my_size.height) / my_size.height
|
167
258
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
259
|
+
new_image = self.canvas(size: new_size) do
|
260
|
+
thumbnail_rect = CGRectZero
|
261
|
+
thumbnail_rect.origin = [thumbnail_x, thumbnail_y]
|
262
|
+
thumbnail_rect.size = my_size
|
172
263
|
|
173
|
-
|
174
|
-
|
175
|
-
new_image = UIGraphicsGetImageFromCurrentImageContext()
|
176
|
-
UIGraphicsEndImageContext()
|
264
|
+
self.drawInRect(thumbnail_rect)
|
265
|
+
end
|
177
266
|
|
178
|
-
raise "could not scale image"
|
267
|
+
raise "could not scale image" unless new_image
|
179
268
|
|
180
269
|
return new_image
|
181
270
|
end
|
@@ -270,28 +359,23 @@ class UIImage
|
|
270
359
|
end
|
271
360
|
|
272
361
|
# this is actually the interesting part:
|
362
|
+
new_image = self.canvas(size: new_size) do |context|
|
363
|
+
if background
|
364
|
+
background = background.uicolor
|
365
|
+
background.setFill
|
366
|
+
CGContextAddRect(context, [[0, 0], new_size])
|
367
|
+
CGContextDrawPath(context, KCGPathFill)
|
368
|
+
end
|
273
369
|
|
274
|
-
|
370
|
+
thumbnail_rect = CGRectZero
|
371
|
+
thumbnail_rect.origin = thumbnail_point
|
372
|
+
thumbnail_rect.size.width = scaled_width
|
373
|
+
thumbnail_rect.size.height = scaled_height
|
275
374
|
|
276
|
-
|
277
|
-
background = background.uicolor
|
278
|
-
context = UIGraphicsGetCurrentContext()
|
279
|
-
background.setFill
|
280
|
-
CGContextAddRect(context, [[0, 0], new_size])
|
281
|
-
CGContextDrawPath(context, KCGPathFill)
|
375
|
+
self.drawInRect(thumbnail_rect)
|
282
376
|
end
|
283
377
|
|
284
|
-
|
285
|
-
thumbnail_rect.origin = thumbnail_point
|
286
|
-
thumbnail_rect.size.width = scaled_width
|
287
|
-
thumbnail_rect.size.height = scaled_height
|
288
|
-
|
289
|
-
self.drawInRect(thumbnail_rect)
|
290
|
-
|
291
|
-
new_image = UIGraphicsGetImageFromCurrentImageContext()
|
292
|
-
UIGraphicsEndImageContext()
|
293
|
-
|
294
|
-
raise "could not scale image" unless new_image
|
378
|
+
raise "could not scale image" unless new_image
|
295
379
|
|
296
380
|
return new_image
|
297
381
|
end
|
@@ -300,13 +384,11 @@ class UIImage
|
|
300
384
|
##| image modifications
|
301
385
|
##|
|
302
386
|
def rounded(corner_radius=5)
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
UIGraphicsEndImageContext()
|
309
|
-
return new_image
|
387
|
+
return self.canvas do
|
388
|
+
path = UIBezierPath.bezierPathWithRoundedRect([[0, 0], size], cornerRadius:corner_radius)
|
389
|
+
path.addClip
|
390
|
+
self.drawInRect([[0, 0], size])
|
391
|
+
end
|
310
392
|
end
|
311
393
|
|
312
394
|
# Returns a CIImage with the filter applied to the receiver. The return value
|
@@ -355,6 +437,28 @@ class UIImage
|
|
355
437
|
return UIImage.imageWithCIImage(output, scale:self.scale, orientation:self.imageOrientation)
|
356
438
|
end
|
357
439
|
|
440
|
+
# Apply a color overlay to the image (very practical with PNG button images)
|
441
|
+
#
|
442
|
+
# @example
|
443
|
+
# image.overlay(UIColor.redColor)
|
444
|
+
def overlay(color)
|
445
|
+
image_rect = CGRectMake(0, 0, self.size.width, self.size.height)
|
446
|
+
|
447
|
+
UIImage.canvas(size: self.size, scale: self.scale) do |ctx|
|
448
|
+
self.drawInRect(image_rect)
|
449
|
+
|
450
|
+
CGContextSetFillColorWithColor(ctx, color.uicolor.CGColor)
|
451
|
+
CGContextSetAlpha(ctx, 1)
|
452
|
+
CGContextSetBlendMode(ctx, KCGBlendModeSourceAtop)
|
453
|
+
CGContextFillRect(ctx, image_rect)
|
454
|
+
|
455
|
+
image_ref = CGBitmapContextCreateImage(ctx)
|
456
|
+
new_image = UIImage.imageWithCGImage(image_ref, scale:self.scale, orientation:self.imageOrientation)
|
457
|
+
end
|
458
|
+
|
459
|
+
return new_image
|
460
|
+
end
|
461
|
+
|
358
462
|
##|
|
359
463
|
##| rotate images
|
360
464
|
##|
|
@@ -377,24 +481,18 @@ class UIImage
|
|
377
481
|
new_size = CGSize.new(w, h)
|
378
482
|
new_size = self.size
|
379
483
|
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
# Move the origin to the middle of the image so we will rotate and scale around the center.
|
385
|
-
CGContextTranslateCTM(bitmap, new_size.width / 2, new_size.height / 2)
|
484
|
+
return self.canvas(size: new_size) do |context|
|
485
|
+
# Move the origin to the middle of the image so we will rotate and scale around the center.
|
486
|
+
CGContextTranslateCTM(context, new_size.width / 2, new_size.height / 2)
|
386
487
|
|
387
|
-
|
388
|
-
|
488
|
+
# Rotate the image context
|
489
|
+
CGContextRotateCTM(context, radian)
|
389
490
|
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
new_image = UIGraphicsGetImageFromCurrentImageContext()
|
396
|
-
UIGraphicsEndImageContext()
|
397
|
-
return new_image
|
491
|
+
# otherwise it'll be upside down:
|
492
|
+
CGContextScaleCTM(context, 1.0, -1.0)
|
493
|
+
# Now, draw the rotated/scaled image into the context
|
494
|
+
CGContextDrawImage(context, CGRectMake(-new_size.width / 2, -new_size.height / 2, new_size.width, new_size.height), self.CGImage)
|
495
|
+
end
|
398
496
|
end
|
399
497
|
|
400
498
|
##|
|
@@ -476,6 +574,28 @@ class UIImage
|
|
476
574
|
return [red, green, blue].uicolor(alpha / 255.0)
|
477
575
|
end
|
478
576
|
|
577
|
+
def avg_color
|
578
|
+
colorSpace = CGColorSpaceCreateDeviceRGB()
|
579
|
+
rgba = Pointer.new(:uchar, 4)
|
580
|
+
context = CGBitmapContextCreate(rgba, 1, 1, 8, 4, colorSpace, KCGImageAlphaPremultipliedLast | KCGBitmapByteOrder32Big)
|
581
|
+
|
582
|
+
CGContextDrawImage(context, CGRectMake(0, 0, 1, 1), self.CGImage)
|
583
|
+
|
584
|
+
if rgba[3] > 0
|
585
|
+
alpha = rgba[3] / 255.0
|
586
|
+
multiplier = alpha / 255.0
|
587
|
+
return UIColor.colorWithRed(rgba[0] * multiplier,
|
588
|
+
green:rgba[1] * multiplier,
|
589
|
+
blue:rgba[2] * multiplier,
|
590
|
+
alpha:alpha)
|
591
|
+
else
|
592
|
+
return UIColor.colorWithRed(rgba[0] / 255.0,
|
593
|
+
green:rgba[1] / 255.0,
|
594
|
+
blue:rgba[2] / 255.0,
|
595
|
+
alpha:rgba[3] / 255.0)
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
479
599
|
def at_scale(scale)
|
480
600
|
if scale == self.scale
|
481
601
|
return self
|
@@ -485,14 +605,42 @@ class UIImage
|
|
485
605
|
new_size.width = new_size.width * self.scale / scale
|
486
606
|
new_size.height = new_size.height * self.scale / scale
|
487
607
|
|
488
|
-
|
489
|
-
|
608
|
+
return self.canvas(size: new_size, scale: scale) do
|
609
|
+
thumbnail_rect = CGRect.new([0, 0], new_size)
|
610
|
+
self.drawInRect(thumbnail_rect)
|
611
|
+
end
|
612
|
+
end
|
490
613
|
|
491
|
-
|
614
|
+
# Using the image as the background, you can use this method to draw anything
|
615
|
+
# on top, like text or other images.
|
616
|
+
def draw(options={}, &block)
|
617
|
+
at = options[:at] || [0, 0]
|
618
|
+
return self.canvas(options) do |context|
|
619
|
+
self.drawAtPoint(at)
|
620
|
+
block.call(context) if block
|
621
|
+
end
|
622
|
+
end
|
492
623
|
|
493
|
-
|
494
|
-
|
495
|
-
|
624
|
+
# the first argument can be a size, or an options dict
|
625
|
+
def canvas(options_or_size={}, more_options={}, &block)
|
626
|
+
if options_or_size.is_a?(NSDictionary)
|
627
|
+
options = options_or_size
|
628
|
+
else
|
629
|
+
options = more_options
|
630
|
+
options[:size] = options_or_size
|
631
|
+
end
|
632
|
+
|
633
|
+
unless options[:size]
|
634
|
+
options[:size] = self.size
|
635
|
+
end
|
636
|
+
|
637
|
+
unless options[:scale]
|
638
|
+
options = options.merge(scale: self.scale)
|
639
|
+
end
|
640
|
+
|
641
|
+
self.class.canvas(options) do |context|
|
642
|
+
block.call(context) if block
|
643
|
+
end
|
496
644
|
end
|
497
645
|
|
498
646
|
end
|