Skip to content

Commit

Permalink
[ASImageNode] Add documentation for image effects #trivial (TextureGr…
Browse files Browse the repository at this point in the history
…oup#263)

* Add documentation for doing image effects

* Link to image modification documentation in the image node documentation
  • Loading branch information
maicki authored and bernieperez committed Apr 25, 2018
1 parent b695725 commit 5bcb1cd
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 41 deletions.
109 changes: 104 additions & 5 deletions docs/_docs/image-modification-block.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ By assigning an `imageModificationBlock` to your imageNode, you can define a set
<div class = "code">
<pre lang="objc" class="objcCode">
_backgroundImageNode.imageModificationBlock = ^(UIImage *image) {
UIImage *newImage = [image applyBlurWithRadius:30
tintColor:[UIColor colorWithWhite:0.5 alpha:0.3]
saturationDeltaFactor:1.8
UIImage *newImage = [image applyBlurWithRadius:30
tintColor:[UIColor colorWithWhite:0.5 alpha:0.3]
saturationDeltaFactor:1.8
maskImage:nil];
return newImage ? newImage : image;
};
Expand All @@ -30,8 +30,8 @@ _backgroundImageNode.image = someImage;

<pre lang="swift" class = "swiftCode hidden">
backgroundImageNode.imageModificationBlock = { image in
let newImage = image.applyBlurWithRadius(30, tintColor: UIColor(white: 0.5, alpha: 0.3),
saturationDeltaFactor: 1.8,
let newImage = image.applyBlurWithRadius(30, tintColor: UIColor(white: 0.5, alpha: 0.3),
saturationDeltaFactor: 1.8,
maskImage: nil)
return (newImage != nil) ? newImage : image
}
Expand All @@ -45,3 +45,102 @@ backgroundImageNode.image = someImage

The image named "someImage" will now be blurred asynchronously before being assigned to the imageNode to be displayed.

### Adding image effects

The most efficient way to add image effects is by leveraging the `imageModificationBlock` block. If a block is provided it can perform drawing operations on the image during the display phase. As display is happening on a background thread it will not block the main thread.

In the following example we assume we have an avatar node that will be setup in `init` of a supernode and the image of the node should be rounded. We provide the `imageModificationBlock` and in there we call a convenience method that transforms the image passed in into a circular image and return it.

<div class = "code">
<pre lang="objc" class="objcCode">
- (instancetype)init
{
// ...
_userAvatarImageNode.imageModificationBlock = ^UIImage *(UIImage *image) {
CGSize profileImageSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT);
return [image makeCircularImageWithSize:profileImageSize];
};
// ...
}
</pre>

<pre lang="swift" class = "swiftCode hidden">
init() {
// ...
_userAvatarImageNode?.imageModificationBlock = { image in
return image.makeCircularImage(size: CGSize(width: USER_IMAGE_HEIGHT, height: USER_IMAGE_HEIGHT))
}
</pre>
</div>
</div>

The actual drawing code is nicely abstracted away in an UIImage category and looks like the following:

<div class = "code">
<pre lang="objc" class="objcCode">

@implementation UIImage (Additions)
- (UIImage *)makeCircularImageWithSize:(CGSize)size
{
// make a CGRect with the image's size
CGRect circleRect = (CGRect) {CGPointZero, size};

// begin the image context since we're not in a drawRect:
UIGraphicsBeginImageContextWithOptions(circleRect.size, NO, 0);

// create a UIBezierPath circle
UIBezierPath *circle = [UIBezierPath bezierPathWithRoundedRect:circleRect cornerRadius:circleRect.size.width/2];

// clip to the circle
[circle addClip];

// draw the image in the circleRect *AFTER* the context is clipped
[self drawInRect:circleRect];

// get an image from the image context
UIImage *roundedImage = UIGraphicsGetImageFromCurrentImageContext();

// end the image context since we're not in a drawRect:
UIGraphicsEndImageContext();

return roundedImage;
}
@end
</pre>

<pre lang="swift" class = "swiftCode hidden">
extension UIImage {

func makeCircularImage(size: CGSize) -> UIImage {
// make a CGRect with the image's size
let circleRect = CGRect(origin: .zero, size: size)

// begin the image context since we're not in a drawRect:
UIGraphicsBeginImageContextWithOptions(circleRect.size, false, 0)

// create a UIBezierPath circle
let circle = UIBezierPath(roundedRect: circleRect, cornerRadius: circleRect.size.width * 0.5)

// clip to the circle
circle.addClip()

UIColor.white.set()
circle.fill()

// draw the image in the circleRect *AFTER* the context is clipped
self.draw(in: circleRect)

// get an image from the image context
let roundedImage = UIGraphicsGetImageFromCurrentImageContext()

// end the image context since we're not in a drawRect:
UIGraphicsEndImageContext()

return roundedImage ?? self
}
}
</pre>
</div>
</div>

The imageModificationBlock is very handy and can be used to add all kind of image effects, such as rounding, adding borders, or other pattern overlays, without extraneous display calls.
41 changes: 5 additions & 36 deletions docs/_docs/image-node.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,44 +33,13 @@ imageNode.contentMode = .scaleAspectFill
</div>


### Image Modification Block
### Image transformations and effects

Many times, operations that would affect the appearance of an image you're displaying are big sources of main thread work. Naturally, you want to move these to a background thread. By assigning an `imageModificationBlock` to your `imageNode`, you can define a set of transformations that need to happen asynchronously to any image that gets set on the `imageNode`.
Many times, operations that would affect the appearance of an image you're displaying are big sources of main thread work. Naturally, you want to move these to a background thread.

<div class = "highlight-group">
<span class="language-toggle"><a data-lang="swift" class="swiftButton">Swift</a><a data-lang="objective-c" class = "active objcButton">Objective-C</a></span>

<div class = "code">
<pre lang="objc" class="objcCode">
_backgroundImageNode.imageModificationBlock = ^(UIImage *image) {
UIImage *newImage = [image applyBlurWithRadius:30
tintColor:[UIColor colorWithWhite:0.5 alpha:0.3]
saturationDeltaFactor:1.8
maskImage:nil];
return newImage ? newImage : image;
};

//some time later...

_backgroundImageNode.image = someImage;
</pre>

<pre lang="swift" class = "swiftCode hidden">
backgroundImageNode.imageModificationBlock = { image in
let newImage = image.applyBlurWithRadius(30, tintColor: UIColor(white: 0.5, alpha: 0.3),
saturationDeltaFactor: 1.8,
maskImage: nil)
return (newImage != nil) ? newImage : image
}

//some time later...

backgroundImageNode.image = someImage
</pre>
</div>
</div>
By assigning an `imageModificationBlock` to your `imageNode`, you can define a set of transformations that need to happen asynchronously to any image that gets set on the `imageNode`, including image effects such as rounding, adding borders, or other pattern overlays, without extraneous display calls.

The image named "someImage" will now be blurred asynchronously before being assigned to the `imageNode` to be displayed.
You can read more about it at <a href="image-modification-block.html">Image Modification Blocks</a>.

### Image Cropping

Expand All @@ -82,7 +51,7 @@ By default, the expanded image will be centered within the bounds of the view. T

That's messed up. To fix it, you can set the `cropRect` property to move the image over. By default it is set to `CGRectMake(0.5, 0.5, 0.0, 0.0)`.

The rectangle is specified as a "unit rectangle," using percentages of the source image's width and height. To show the image starting at the left side, you can set the `cropRect`'s `x` value to be `0.0`, meaning the image's origin should start at `{0, 0}` as opposed to the default.
The rectangle is specified as a "unit rectangle," using percentages of the source image's width and height. To show the image starting at the left side, you can set the `cropRect`'s `x` value to be `0.0`, meaning the image's origin should start at `{0, 0}` as opposed to the default.

<div class = "highlight-group">
<span class="language-toggle"><a data-lang="swift" class="swiftButton">Swift</a><a data-lang="objective-c" class = "active objcButton">Objective-C</a></span>
Expand Down

0 comments on commit 5bcb1cd

Please sign in to comment.