class UIImage
| | REALLY HANDY STUFF! | many of these methods are translated from: | <www.catamount.com/blog/uiimage-extensions-for-cutting-scaling-and-rotating-uiimages/> | <www.catamount.com/forums/viewtopic.php?f=21&t=967> |
Constants
- SugarCube1024_TallSuffix
- SugarCube568_TallSuffix
- SugarCube667_TallSuffix
- SugarCube736_TallSuffix
Public Class Methods
Easily create a UIImage
by using this factory method, and do your drawing in a block. The core graphics context will be passed to the block you provide. To create a canvas based on an image, use the instance method.
@example
white_square = UIImage.canvas([100, 100]) do |context| :white.uicolor.set CGContextAddRect(context, [[0, 0], [100, 100]]) CGContextDrawPath(context, KCGPathFill) end
:size is required, :scale defaults to the screen scale, and :opaque is false by default.
The first argument can be a size, or an options dict
# File lib/ios/sugarcube-image/uiimage.rb, line 25 def canvas(options_or_size={}, more_options={}, &block) if options_or_size.is_a?(NSDictionary) options = options_or_size size = options[:size] else options = more_options size = options_or_size end raise ":size is required in #{self.name}##canvas" unless size scale = options.fetch(:scale, 0.0) # not a typo, 0.0 indicates "use default" opaque = options.fetch(:opaque, false) UIGraphicsBeginImageContextWithOptions(size, opaque, scale) block.call(UIGraphicsGetCurrentContext()) if block new_image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return new_image end
now we've got sugarcube568_imageNamed
and imageNamed_old
to load the respective versions
# File lib/ios/sugarcube-568/uiimage.rb, line 40 def imageNamed(name) sugarcube568_imageNamed(name) || imageNamed_old(name) end
# File lib/ios/sugarcube-568/uiimage.rb, line 8 def sugarcube568_imageNamed(name) case UIScreen.mainScreen.bounds.size.height when 568 taller_image_ext = SugarCube568_TallSuffix when 667 taller_image_ext = SugarCube667_TallSuffix when 736 taller_image_ext = SugarCube736_TallSuffix when 1024 taller_image_ext = SugarCube1024_TallSuffix else taller_image_ext = false end if ( taller_image_ext && name.length > 0 && name.rangeOfString(taller_image_ext).location == NSNotFound ) # Check if is there a path extension or not test_name = name if test_name.pathExtension.length > 0 test_name = test_name.stringByDeletingPathExtension.stringByAppendingString(taller_image_ext).stringByAppendingPathExtension(name.pathExtension) else test_name = test_name.stringByAppendingString(taller_image_ext) end return imageNamed_old(test_name) end return nil end
Public Instance Methods
Merges the two images. The target is drawn first, `image` is drawn on top. The two images are centered, and the maximum size is used so that both images fit on the canvas.
# File lib/ios/sugarcube-image/uiimage.rb, line 49 def <<(image) self.merge(image, at: :center, stretch: true) end
| | imageWithAlignmentRectInsets |
# File lib/ios/sugarcube-image/uiimage.rb, line 517 def alignment_rect(insets=UIEdgeInsetsZero) imageWithAlignmentRectInsets(insets) end
Returns a CIImage
with the filter applied to the receiver. The return value is a CIImage
object, which also defines `|` to work the same way, so filters can be chained.
@example
image = 'test'.uiimage new_image = image.apply_filter(CIFilter.gaussian_blur)
# File lib/ios/sugarcube-image/uiimage.rb, line 389 def apply_filter(filter) filter.setValue(self.ciimage, forKey: 'inputImage') return filter.valueForKey('outputImage') end
# File lib/ios/sugarcube-image/uiimage.rb, line 600 def at_scale(scale) if scale == self.scale return self end new_size = self.size new_size.width = new_size.width * self.scale / scale new_size.height = new_size.height * self.scale / scale return self.canvas(size: new_size, scale: scale) do thumbnail_rect = CGRect.new([0, 0], new_size) self.drawInRect(thumbnail_rect) end end
# File lib/ios/sugarcube-image/uiimage.rb, line 578 def avg_color colorSpace = CGColorSpaceCreateDeviceRGB() rgba = Pointer.new(:uchar, 4) context = CGBitmapContextCreate(rgba, 1, 1, 8, 4, colorSpace, KCGImageAlphaPremultipliedLast | KCGBitmapByteOrder32Big) CGContextDrawImage(context, CGRectMake(0, 0, 1, 1), self.CGImage) if rgba[3] > 0 alpha = rgba[3] / 255.0 multiplier = alpha / 255.0 return UIColor.colorWithRed(rgba[0] * multiplier, green:rgba[1] * multiplier, blue:rgba[2] * multiplier, alpha:alpha) else return UIColor.colorWithRed(rgba[0] / 255.0, green:rgba[1] / 255.0, blue:rgba[2] / 255.0, alpha:rgba[3] / 255.0) end end
the first argument can be a size, or an options dict
# File lib/ios/sugarcube-image/uiimage.rb, line 626 def canvas(options_or_size={}, more_options={}, &block) if options_or_size.is_a?(NSDictionary) options = options_or_size else options = more_options options[:size] = options_or_size end unless options[:size] options[:size] = self.size end unless options[:scale] options[:scale] = self.scale end self.class.canvas(options) do |context| block.call(context) if block end end
# File lib/ios/sugarcube-color/uiimage.rb, line 13 def cgcolor(alpha=nil) uicolor(alpha).CGColor end
Returns a CGImageRef. Alias for `CGImage`.
# File lib/ios/sugarcube-image/uiimage.rb, line 395 def cgimage return self.CGImage end
Returns a CIImage
.
# File lib/ios/sugarcube-image/uiimage.rb, line 400 def ciimage return CIImage.imageWithCGImage(self.CGImage) end
Oddly enough, this method doesn't seem to have retina support
# File lib/ios/sugarcube-image/uiimage.rb, line 547 def color_at(point) point = SugarCube::CoreGraphics::Point(point) point.x *= self.scale point.y *= self.scale # First get the image into your data buffer cgimage = self.CGImage width = CGImageGetWidth(cgimage) height = CGImageGetHeight(cgimage) bytes_per_pixel = 4 bits_per_component = 8 bytes_per_row = bytes_per_pixel * width @raw_data || begin color_space = CGColorSpaceCreateDeviceRGB() @raw_data = Pointer.new(:uchar, height * width * 4) context = CGBitmapContextCreate(@raw_data, width, height, bits_per_component, bytes_per_row, color_space, KCGImageAlphaPremultipliedLast | KCGBitmapByteOrder32Big) CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgimage) end # Now @raw_data contains the image data in the RGBA8888 pixel format. xx = point.x.round yy = point.y.round byte_index = (bytes_per_row * yy) + xx * bytes_per_pixel red = @raw_data[byte_index] green = @raw_data[byte_index + 1] blue = @raw_data[byte_index + 2] alpha = @raw_data[byte_index + 3] return [red, green, blue].uicolor(alpha / 255.0) end
Returns a cropped UIImage
. The easiest way is to check for a CGImage backing, but if this image uses a CIImage
backing, we draw a new (cropped) image.
# File lib/ios/sugarcube-image/uiimage.rb, line 121 def crop(rect) rect = SugarCube::CoreGraphics::Rect(rect) if self.CGImage if self.scale > 1.0 rect = CGRectMake(rect.origin.x * self.scale, rect.origin.y * self.scale, rect.size.width * self.scale, rect.size.height * self.scale) end cgimage = CGImageCreateWithImageInRect(self.CGImage, rect) return UIImage.imageWithCGImage(cgimage, scale:self.scale, orientation:self.imageOrientation) else return self.canvas(size: rect.size) do |context| self.drawAtPoint(CGPoint.new(-rect.origin.x, -rect.origin.y)) end end end
Accepts two options: brightness (default: 0.0) and saturation (default: 0.0) Returns a darkened version of the image.
# File lib/ios/sugarcube-image/uiimage.rb, line 406 def darken(options={}) filter_options = { 'inputSaturation' => options[:saturation] || 0, 'inputBrightness' => options[:brightness] || 0, } darken_filter = CIFilter.color_controls(filter_options) output = self.apply_filter(darken_filter) return UIImage.imageWithCIImage(output, scale:self.scale, orientation:self.imageOrientation) end
Using the image as the background, you can use this method to draw anything on top, like text or other images.
# File lib/ios/sugarcube-image/uiimage.rb, line 617 def draw(options={}, &block) at = options[:at] || [0, 0] return self.canvas(options) do |context| self.drawAtPoint(at) block.call(context) if block end end
Apply a gaussian filter @options radius, default: 10
@example
image.gaussian_blur(radius: 5) image.gaussian_blur(5) # :radius is the default option
# File lib/ios/sugarcube-image/uiimage.rb, line 435 def gaussian_blur(*args) output = self.apply_filter(CIFilter.gaussian_blur(*args)) return UIImage.imageWithCIImage(output, scale:self.scale, orientation:self.imageOrientation) end
# File lib/ios/sugarcube-ui/uiimage.rb, line 13 def height self.size.height end
This method is used to crop an image. Scale (retina or non-retina) is preserved.
@param rect [CGRect] the portion of the image to return @return [UIImage]
# File lib/ios/sugarcube-image/uiimage.rb, line 148 def in_rect(rect) imageRef = CGImageCreateWithImageInRect(self.CGImage, rect) sub_image = UIImage.imageWithCGImage(imageRef, scale:self.scale, orientation:self.imageOrientation) return sub_image end
Invert the image @options - none
@example
image.inverted
similar to (image | CIFilter.color_invert
).uiimage
# File lib/ios/sugarcube-image/uiimage.rb, line 424 def inverted output = self.apply_filter(CIFilter.color_invert) return UIImage.imageWithCIImage(output, scale:self.scale, orientation:self.imageOrientation) end
| | CGImageCreateWithMask | The mask image cannot have ANY transparency. Instead, transparent areas must be white or some value between black and white. The more white a pixel is the more transparent it becomes.
black .. white opaque .. transparent
# File lib/ios/sugarcube-image/uiimage.rb, line 529 def masked(mask_image) mask_image = mask_image.CGImage width = CGImageGetWidth(mask_image) height = CGImageGetHeight(mask_image) component_bits = CGImageGetBitsPerComponent(mask_image) pixel_bits = CGImageGetBitsPerPixel(mask_image) row_bytes = CGImageGetBytesPerRow(mask_image) data_provider = CGImageGetDataProvider(mask_image) mask = CGImageMaskCreate(width, height, component_bits, pixel_bits, row_bytes, data_provider,nil, false) masked = CGImageCreateWithMask(self.CGImage, mask) UIImage.imageWithCGImage(masked, scale:self.scale, orientation:self.imageOrientation) end
Draw an image on top of the receiver. The `:at` option provides either an absolute location (Array
or CGPoint) or relative location (Symbol
, one of :top_left, :top, :top_right, :left, :center (default), :right, :bottom_left, :bottom, :bottom_right). The `:stretch` option increases the canvas so there is room for both images, otherwise the target image's size is used.
# File lib/ios/sugarcube-image/uiimage.rb, line 58 def merge(image, options={}) image_position = options.fetch(:at, :center) stretch = options.fetch(:stretch, false) size = self.size if stretch if image.size.width > size.width size.width = image.size.width end if image.size.height > size.height size.height = image.size.height end end my_left = image_left = 0 my_top = image_top = 0 my_right = (size.width - self.size.width) my_bottom = (size.height - self.size.height) image_right = (size.width - image.size.width) image_bottom = (size.height - image.size.height) my_cx = my_right / 2.0 my_cy = my_bottom / 2.0 image_cx = image_right / 2.0 image_cy = image_bottom / 2.0 case image_position when :top_left, :topleft, :tl my_position = [my_right, my_bottom] image_position = [image_left, image_top] when :top, :t my_position = [my_cx, my_bottom] image_position = [image_cx, image_top] when :top_right, :topright, :tr my_position = [my_left, my_bottom] image_position = [image_right, image_top] when :left, :l my_position = [my_right, my_cy] image_position = [image_left, image_cy] when :center, :c my_position = [my_cx, my_cy] image_position = [image_cx, image_cy] when :right, :r my_position = [my_left, my_cy] image_position = [image_right, image_cy] when :bottom_left, :bottomleft, :bl my_position = [my_right, my_top] image_position = [image_left, image_bottom] when :bottom, :b my_position = [my_cx, my_top] image_position = [image_cx, image_bottom] when :bottom_right, :bottomright, :br my_position = [my_left, my_top] image_position = [image_right, image_bottom] end return self.draw(size: size, at: my_position) do image.drawAtPoint(image_position) end end
@return [NSData] an NSData
object in PNG or JPG format
# File lib/ios/sugarcube-nsdata/uiimage.rb, line 4 def nsdata(format=:png, compression=0.9) case format when :png, :PNG UIImagePNGRepresentation(self) when :jpg, :JPG UIImageJPEGRepresentation(self, compression) end end
Apply a color overlay to the image (very practical with PNG button images)
@example
image.overlay(UIColor.redColor)
# File lib/ios/sugarcube-image/uiimage.rb, line 444 def overlay(color) image_rect = CGRectMake(0, 0, self.size.width, self.size.height) new_image = nil UIImage.canvas(size: self.size, scale: self.scale) do |ctx| self.drawInRect(image_rect) CGContextSetFillColorWithColor(ctx, color.uicolor.CGColor) CGContextSetAlpha(ctx, 1) CGContextSetBlendMode(ctx, KCGBlendModeSourceAtop) CGContextFillRect(ctx, image_rect) image_ref = CGBitmapContextCreateImage(ctx) new_image = UIImage.imageWithCGImage(image_ref, scale:self.scale, orientation:self.imageOrientation) end return new_image end
| | rotate images |
# File lib/ios/sugarcube-image/uiimage.rb, line 466 def rotate(angle_or_direction) case angle_or_direction when :left radian = -90.degrees when :right radian = 90.degrees when :flip radian = 180.degrees when Numeric radian = angle_or_direction else raise "Unknown angle/direction #{angle_or_direction.inspect}" end w = (self.size.width * Math.cos(radian)).abs + (self.size.height * Math.sin(radian)).abs h = (self.size.height * Math.cos(radian)).abs + (self.size.width * Math.sin(radian)).abs new_size = CGSize.new(w, h) new_size = self.size return self.canvas(size: new_size) do |context| # Move the origin to the middle of the image so we will rotate and scale around the center. CGContextTranslateCTM(context, new_size.width / 2, new_size.height / 2) # Rotate the image context CGContextRotateCTM(context, radian) # otherwise it'll be upside down: CGContextScaleCTM(context, 1.0, -1.0) # Now, draw the rotated/scaled image into the context CGContextDrawImage(context, CGRectMake(-new_size.width / 2, -new_size.height / 2, new_size.width, new_size.height), self.CGImage) end end
| | image modifications |
# File lib/ios/sugarcube-image/uiimage.rb, line 374 def rounded(corner_radius=5) return self.canvas do path = UIBezierPath.bezierPathWithRoundedRect([[0, 0], size], cornerRadius:corner_radius) path.addClip self.drawInRect([[0, 0], size]) end end
Delegates to scale_to
(background:), specifying background color of `nil`
# File lib/ios/sugarcube-image/uiimage.rb, line 294 def scale_to(new_size) scale_to(new_size, background:nil) end
Scales an image to fit within the given size, stretching one or both dimensions so that it completely fills the area. The current aspect ratio is maintained. If you want to place an image inside a container image, this is the method to use.
You can specify a `position` property, which can be a symbol or a point. It specifies where you want the image located if it has to be cropped. Specifying the top-left corner will display the top-left corner of the image, likewise specifing the bottom-right corner will display that corner. If you want the image centered, you can use the 'position-less' version of this method (`scale_to_fit()`) or specify the point at the center of the image (`scale_to_fit(size, position:[w/2, h/2])`), or use a symbol (`scale_to_fit(size, position: :center)`).
@param new_size [CGSize] Minimum dimensions of desired image. The returned image is
guaranteed to fit within these dimensions.
@param position [Symbol, CGPoint] Where to position the resized image. Valid symbols
are: `[:top_left, :top, :top_right, :left, :center, :right, :bottom_left, :bottom, :bottom_right]` (if you forget the underscore, like `topleft`, that'll work, too) @param scale [Numeric] image scale
@return [UIImage]
# File lib/ios/sugarcube-image/uiimage.rb, line 177 def scale_to_fill(new_size, options={}) new_size = SugarCube::CoreGraphics::Size(new_size) position = options[:position] || :center scale = options[:scale] || self.scale my_size = self.size if new_size.width == my_size.width && new_size.height == my_size.height && self.scale == scale return self end # first, scale down; then we'll scale back up if we went too far if my_size.width > new_size.width my_size.height *= new_size.width / my_size.width my_size.width = new_size.width end if my_size.height > new_size.height my_size.width *= new_size.height / my_size.height my_size.height = new_size.height end if my_size.width < new_size.width my_size.height *= new_size.width / my_size.width my_size.width = new_size.width end if my_size.height < new_size.height my_size.width *= new_size.height / my_size.height my_size.height = new_size.height end if self.size.width == my_size.width && self.size.height == my_size.height return self end if position.is_a?(Symbol) min_x = 0 min_y = 0 max_x = my_size.width max_y = my_size.height mid_x = max_x / 2 mid_y = max_y / 2 case position when :top_left, :topleft, :tl position = CGPoint.new(min_x, min_y) when :top, :t position = CGPoint.new(mid_x, min_y) when :top_right, :topright, :tr position = CGPoint.new(max_x, min_y) when :left, :l position = CGPoint.new(min_x, mid_x) when :center, :c position = CGPoint.new(mid_x, mid_x) when :right, :r position = CGPoint.new(max_x, mid_x) when :bottom_left, :bottomleft, :bl position = CGPoint.new(min_x, max_y) when :bottom, :b position = CGPoint.new(mid_x, max_y) when :bottom_right, :bottomright, :br position = CGPoint.new(max_x, max_y) else raise "Unknown position #{position.inspect}" end else position = SugarCube::CoreGraphics::Point(position) end thumbnail_x = position.x * (new_size.width - my_size.width) / my_size.width thumbnail_y = position.y * (new_size.height - my_size.height) / my_size.height new_image = self.canvas(size: new_size) do thumbnail_rect = CGRect.new([0, 0], [0, 0]) thumbnail_rect.origin = [thumbnail_x, thumbnail_y] thumbnail_rect.size = my_size self.drawInRect(thumbnail_rect) end raise "could not scale image" unless new_image return new_image end
This method is similar to `scale_to`, except it doesn't pad the image, it just scales the image so that it will fit inside the new bounds.
# File lib/ios/sugarcube-image/uiimage.rb, line 262 def scale_within(new_size) target_size = SugarCube::CoreGraphics::Size(new_size) image_size = self.size if CGSizeEqualToSize(target_size, self.size) return self end width = image_size.width height = image_size.height target_width = target_size.width target_height = target_size.height width_factor = target_width / width height_factor = target_height / height if width_factor < height_factor scale_factor = width_factor else scale_factor = height_factor end if scale_factor == 1 return self end scaled_size = CGSize.new(width * scale_factor, height * scale_factor) return scale_to(scaled_size) end
# File lib/ios/sugarcube-color/uiimage.rb, line 17 def skcolor(alpha=nil) uicolor(alpha) end
# File lib/ios/sugarcube-image/uiimage.rb, line 508 def stretchable(insets=UIEdgeInsetsZero) # not necessary, since we don't modify/examine the insets # insets = SugarCube::CoreGraphics::EdgeInsets(insets) resizableImageWithCapInsets(insets, resizingMode:UIImageResizingModeStretch) end
| | resizableImageWithCapInsets |
# File lib/ios/sugarcube-image/uiimage.rb, line 502 def tileable(insets=UIEdgeInsetsZero) # not necessary, since we don't modify/examine the insets # insets = SugarCube::CoreGraphics::EdgeInsets(insets) resizableImageWithCapInsets(insets, resizingMode:UIImageResizingModeTile) end
@return [UIColor]
# File lib/ios/sugarcube-color/uiimage.rb, line 4 def uicolor(alpha=nil) color = UIColor.colorWithPatternImage(self) if alpha color = color.colorWithAlphaComponent(alpha.to_f) end color end
# File lib/ios/sugarcube-ui/uiimage.rb, line 2 def uiimage ; self ; end
@return [UIImageView]
# File lib/ios/sugarcube-ui/uiimage.rb, line 5 def uiimageview UIImageView.alloc.initWithImage(self) end
# File lib/ios/sugarcube-ui/uiimage.rb, line 9 def width self.size.width end
Operator shorthand for `apply_filter`, or coerces the image into other formats.
@example
image = 'test'.uiimage new_image = image | CIFilter.gaussian_blur # => returns a CIImage new_image = (image | CIFilter.gaussian_blur).uiimage # => coerce to UIImage new_image = image | CIFilter.gaussian_blur | UIImage # => coerce to UIImage via chaining
# File lib/ios/sugarcube-pipes/pipes.rb, line 11 def |(filter) if CIFilter === filter apply_filter(filter) elsif filter == UIImage self elsif filter == UIView || filter == UIImageView self.uiimageview elsif filter == CIImage ciimage elsif filter == UIColor uicolor elsif filter == NSData nsdata else raise "The `|` operator is not supported for the #{filter.is_a?(Class) ? filter.name : filter.class.to_s} class" end end