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.
@@ -67,10 +67,10 @@ class Symbol
67
67
  blue: 0x0000ff,
68
68
  brown: 0x996633,
69
69
  cyan: 0x00ffff,
70
- darkgray: 0x555555,
70
+ dark_gray: 0x555555,
71
71
  gray: 0x808080,
72
72
  green: 0x00ff00,
73
- lightgray: 0xaaaaaa,
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 = (self.alpha + color.alpha) / 2
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
- _sugarcube_colors && _sugarcube_colors[:red]
98
+ _sugarcube_rgb_colors && _sugarcube_rgb_colors[:red]
87
99
  end
88
100
 
89
101
  def green
90
- _sugarcube_colors && _sugarcube_colors[:green]
102
+ _sugarcube_rgb_colors && _sugarcube_rgb_colors[:green]
91
103
  end
92
104
 
93
105
  def blue
94
- _sugarcube_colors && _sugarcube_colors[:blue]
106
+ _sugarcube_rgb_colors && _sugarcube_rgb_colors[:blue]
95
107
  end
96
108
 
97
109
  def alpha
98
- _sugarcube_colors && _sugarcube_colors[:alpha]
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 _sugarcube_colors
165
- @color ||= begin
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).miles
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.1
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 || on_default
137
+ handler = on_destructive
138
138
  elsif index == alert.cancelButtonIndex && on_cancel
139
- handler = on_cancel || on_default
140
- else
141
- handler = on_success || on_default
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 = cancel_handler
90
+ handler = on_cancel
94
91
  else
95
- handler = success_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 && orientation
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
- ##| REALLY HANDY STUFF!
4
- ##| many of these methods are translated from:
5
- ##| <http://www.catamount.com/blog/uiimage-extensions-for-cutting-scaling-and-rotating-uiimages/>
6
- ##| <http://www.catamount.com/forums/viewtopic.php?f=21&t=967>
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
- size = self.size
14
- if image.size.width > size.width
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
- UIGraphicsBeginImageContextWithOptions(size, false, self.scale)
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
- self_position = CGPoint.new((size.width - self.size.width) / 2, (size.width - self.size.width) / 2)
24
- self.drawAtPoint(self_position)
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
- image_position = CGPoint.new((size.width - image.size.width) / 2, (size.width - image.size.width) / 2)
27
- image.drawAtPoint(image_position)
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
- new_image = UIGraphicsGetImageFromCurrentImageContext()
30
- UIGraphicsEndImageContext()
31
- return new_image
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
- if self.scale > 1.0
37
- rect = CGRectMake(rect.origin.x * self.scale,
38
- rect.origin.y * self.scale,
39
- rect.size.width * self.scale,
40
- rect.size.height * self.scale)
41
- end
42
-
43
- cgimage = CGImageCreateWithImageInRect(self.CGImage, rect)
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
- return result
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
- UIGraphicsBeginImageContextWithOptions(new_size, false, scale)
169
- thumbnail_rect = CGRectZero
170
- thumbnail_rect.origin = [thumbnail_x, thumbnail_y]
171
- thumbnail_rect.size = my_size
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
- self.drawInRect(thumbnail_rect)
174
-
175
- new_image = UIGraphicsGetImageFromCurrentImageContext()
176
- UIGraphicsEndImageContext()
264
+ self.drawInRect(thumbnail_rect)
265
+ end
177
266
 
178
- raise "could not scale image" unless new_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
- UIGraphicsBeginImageContextWithOptions(new_size, false, self.scale)
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
- if background
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
- thumbnail_rect = CGRectZero
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
- UIGraphicsBeginImageContextWithOptions(size, false, self.scale)
304
- path = UIBezierPath.bezierPathWithRoundedRect([[0, 0], size], cornerRadius:corner_radius)
305
- path.addClip
306
- self.drawInRect([[0, 0], size])
307
- new_image = UIGraphicsGetImageFromCurrentImageContext()
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
- # Create the bitmap context
381
- UIGraphicsBeginImageContextWithOptions(new_size, false, self.scale)
382
- bitmap = UIGraphicsGetCurrentContext()
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
- # Rotate the image context
388
- CGContextRotateCTM(bitmap, radian)
488
+ # Rotate the image context
489
+ CGContextRotateCTM(context, radian)
389
490
 
390
- # otherwise it'll be upside down:
391
- CGContextScaleCTM(bitmap, 1.0, -1.0)
392
- # Now, draw the rotated/scaled image into the context
393
- CGContextDrawImage(bitmap, CGRectMake(-new_size.width / 2, -new_size.height / 2, new_size.width, new_size.height), self.CGImage)
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
- UIGraphicsBeginImageContextWithOptions(new_size, false, scale)
489
- thumbnail_rect = CGRect.new([0, 0], new_size)
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
- self.drawInRect(thumbnail_rect)
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
- new_image = UIGraphicsGetImageFromCurrentImageContext()
494
- UIGraphicsEndImageContext()
495
- return new_image
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