Add blur effect to RCTImageView
Summary: This diff introduces a blur radius property to the Image component on ios. If the radius specified is greater then 0 then native will apply a blur filter to the image Reviewed By: nicklockwood Differential Revision: D3054671 fb-gh-sync-id: d7a81ce5a08a3a2091c583f5053c6a86638b21b2 shipit-source-id: d7a81ce5a08a3a2091c583f5053c6a86638b21b2
This commit is contained in:
parent
8d7b419ed7
commit
9cb3ec9424
|
@ -94,6 +94,11 @@ var Image = React.createClass({
|
|||
* @platform ios
|
||||
*/
|
||||
accessibilityLabel: PropTypes.string,
|
||||
/**
|
||||
* blurRadius: the blur radius of the blur filter added to the image
|
||||
* @platform ios
|
||||
*/
|
||||
blurRadius: PropTypes.number,
|
||||
/**
|
||||
* When the image is resized, the corners of the size specified
|
||||
* by capInsets will stay a fixed size, but the center content and borders
|
||||
|
@ -206,7 +211,7 @@ var Image = React.createClass({
|
|||
|
||||
// This is a workaround for #8243665. RCTNetworkImageView does not support tintColor
|
||||
// TODO: Remove this hack once we have one image implementation #8389274
|
||||
if (isNetwork && tintColor) {
|
||||
if (isNetwork && (tintColor || this.props.blurRadius)) {
|
||||
RawImage = RCTImageView;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879371AAD32A300F088A5 /* RCTImageLoader.m */; };
|
||||
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */; };
|
||||
354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 354631671B69857700AA0B86 /* RCTImageEditingManager.m */; };
|
||||
EEF314721C9B0DD30049118E /* RCTImageBlurUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = EEF314711C9B0DD30049118E /* RCTImageBlurUtils.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
|
@ -56,6 +57,8 @@
|
|||
354631661B69857700AA0B86 /* RCTImageEditingManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageEditingManager.h; sourceTree = "<group>"; };
|
||||
354631671B69857700AA0B86 /* RCTImageEditingManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageEditingManager.m; sourceTree = "<group>"; };
|
||||
58B5115D1A9E6B3D00147676 /* libRCTImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTImage.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
EEF314701C9B0DD30049118E /* RCTImageBlurUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageBlurUtils.h; sourceTree = "<group>"; };
|
||||
EEF314711C9B0DD30049118E /* RCTImageBlurUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageBlurUtils.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -72,6 +75,8 @@
|
|||
58B511541A9E6B3D00147676 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EEF314701C9B0DD30049118E /* RCTImageBlurUtils.h */,
|
||||
EEF314711C9B0DD30049118E /* RCTImageBlurUtils.m */,
|
||||
139A38821C4D57AD00862840 /* RCTResizeMode.h */,
|
||||
139A38831C4D587C00862840 /* RCTResizeMode.m */,
|
||||
13EF7F7D1BC825B1003F47DD /* RCTXCAssetImageLoader.h */,
|
||||
|
@ -172,6 +177,7 @@
|
|||
354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */,
|
||||
139A38841C4D587C00862840 /* RCTResizeMode.m in Sources */,
|
||||
1304D5AB1AA8C4A30002E2BE /* RCTImageView.m in Sources */,
|
||||
EEF314721C9B0DD30049118E /* RCTImageBlurUtils.m in Sources */,
|
||||
13EF7F0B1BC42D4E003F47DD /* RCTShadowVirtualImage.m in Sources */,
|
||||
13EF7F7F1BC825B1003F47DD /* RCTXCAssetImageLoader.m in Sources */,
|
||||
134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */,
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright (c) 2013, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <Accelerate/Accelerate.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTDefines.h"
|
||||
|
||||
RCT_EXTERN UIImage *RCTBlurredImageWithRadius(UIImage *inputImage, CGFloat radius);
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "RCTImageBlurUtils.h"
|
||||
|
||||
UIImage *RCTBlurredImageWithRadius(UIImage *inputImage, CGFloat radius)
|
||||
{
|
||||
CGImageRef imageRef = inputImage.CGImage;
|
||||
CGFloat imageScale = inputImage.scale;
|
||||
UIImageOrientation imageOrientation = inputImage.imageOrientation;
|
||||
|
||||
// Image must be nonzero size
|
||||
if (CGImageGetWidth(imageRef) * CGImageGetHeight(imageRef) == 0) {
|
||||
return inputImage;
|
||||
}
|
||||
|
||||
//convert to ARGB if it isn't
|
||||
if (CGImageGetBitsPerPixel(imageRef) != 32 ||
|
||||
CGImageGetBitsPerComponent(imageRef) != 8 ||
|
||||
!((CGImageGetBitmapInfo(imageRef) & kCGBitmapAlphaInfoMask))) {
|
||||
UIGraphicsBeginImageContextWithOptions(inputImage.size, NO, inputImage.scale);
|
||||
[inputImage drawAtPoint:CGPointZero];
|
||||
imageRef = UIGraphicsGetImageFromCurrentImageContext().CGImage;
|
||||
UIGraphicsEndImageContext();
|
||||
}
|
||||
|
||||
vImage_Buffer buffer1, buffer2;
|
||||
buffer1.width = buffer2.width = CGImageGetWidth(imageRef);
|
||||
buffer1.height = buffer2.height = CGImageGetHeight(imageRef);
|
||||
buffer1.rowBytes = buffer2.rowBytes = CGImageGetBytesPerRow(imageRef);
|
||||
size_t bytes = buffer1.rowBytes * buffer1.height;
|
||||
buffer1.data = malloc(bytes);
|
||||
buffer2.data = malloc(bytes);
|
||||
|
||||
// A description of how to compute the box kernel width from the Gaussian
|
||||
// radius (aka standard deviation) appears in the SVG spec:
|
||||
// http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
|
||||
uint32_t boxSize = floor((radius * imageScale * 3 * sqrt(2 * M_PI) / 4 + 0.5) / 2);
|
||||
boxSize |= 1; // Ensure boxSize is odd
|
||||
|
||||
//create temp buffer
|
||||
void *tempBuffer = malloc((size_t)vImageBoxConvolve_ARGB8888(&buffer1, &buffer2, NULL, 0, 0, boxSize, boxSize,
|
||||
NULL, kvImageEdgeExtend + kvImageGetTempBufferSize));
|
||||
|
||||
//copy image data
|
||||
CFDataRef dataSource = CGDataProviderCopyData(CGImageGetDataProvider(imageRef));
|
||||
memcpy(buffer1.data, CFDataGetBytePtr(dataSource), bytes);
|
||||
CFRelease(dataSource);
|
||||
|
||||
//perform blur
|
||||
vImageBoxConvolve_ARGB8888(&buffer1, &buffer2, tempBuffer, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
|
||||
vImageBoxConvolve_ARGB8888(&buffer2, &buffer1, tempBuffer, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
|
||||
vImageBoxConvolve_ARGB8888(&buffer1, &buffer2, tempBuffer, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
|
||||
|
||||
//free buffers
|
||||
free(buffer2.data);
|
||||
free(tempBuffer);
|
||||
|
||||
//create image context from buffer
|
||||
CGContextRef ctx = CGBitmapContextCreate(buffer1.data, buffer1.width, buffer1.height,
|
||||
8, buffer1.rowBytes, CGImageGetColorSpace(imageRef),
|
||||
CGImageGetBitmapInfo(imageRef));
|
||||
|
||||
//create image from context
|
||||
imageRef = CGBitmapContextCreateImage(ctx);
|
||||
UIImage *outputImage = [UIImage imageWithCGImage:imageRef scale:imageScale orientation:imageOrientation];
|
||||
CGImageRelease(imageRef);
|
||||
CGContextRelease(ctx);
|
||||
free(buffer1.data);
|
||||
return outputImage;
|
||||
}
|
|
@ -22,5 +22,6 @@
|
|||
@property (nonatomic, strong) UIImage *defaultImage;
|
||||
@property (nonatomic, assign) UIImageRenderingMode renderingMode;
|
||||
@property (nonatomic, strong) RCTImageSource *source;
|
||||
@property (nonatomic, assign) CGFloat blurRadius;
|
||||
|
||||
@end
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#import "RCTImageSource.h"
|
||||
#import "RCTImageUtils.h"
|
||||
#import "RCTUtils.h"
|
||||
#import "RCTImageBlurUtils.h"
|
||||
|
||||
#import "UIView+React.h"
|
||||
|
||||
|
@ -100,6 +101,14 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
}
|
||||
}
|
||||
|
||||
- (void)setBlurRadius:(CGFloat)blurRadius
|
||||
{
|
||||
if (blurRadius != _blurRadius) {
|
||||
_blurRadius = blurRadius;
|
||||
[self reloadImage];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setCapInsets:(UIEdgeInsets)capInsets
|
||||
{
|
||||
if (!UIEdgeInsetsEqualToEdgeInsets(_capInsets, capInsets)) {
|
||||
|
@ -191,6 +200,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
}
|
||||
|
||||
RCTImageSource *source = _source;
|
||||
CGFloat blurRadius = _blurRadius;
|
||||
__weak RCTImageView *weakSelf = self;
|
||||
_reloadImageCancellationBlock = [_bridge.imageLoader loadImageWithoutClipping:_source.imageURL.absoluteString
|
||||
size:imageSize
|
||||
|
@ -198,8 +208,12 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
resizeMode:(RCTResizeMode)self.contentMode
|
||||
progressBlock:progressHandler
|
||||
completionBlock:^(NSError *error, UIImage *image) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
RCTImageView *strongSelf = weakSelf;
|
||||
if (blurRadius > __FLT_EPSILON__) {
|
||||
// Do this on the background thread to avoid blocking interaction
|
||||
image = RCTBlurredImageWithRadius(image, blurRadius);
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (![source isEqual:strongSelf.source]) {
|
||||
// Bail out if source has changed since we started loading
|
||||
return;
|
||||
|
|
|
@ -25,6 +25,7 @@ RCT_EXPORT_MODULE()
|
|||
return [[RCTImageView alloc] initWithBridge:self.bridge];
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(blurRadius, CGFloat)
|
||||
RCT_EXPORT_VIEW_PROPERTY(capInsets, UIEdgeInsets)
|
||||
RCT_REMAP_VIEW_PROPERTY(defaultSource, defaultImage, UIImage)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onLoadStart, RCTDirectEventBlock)
|
||||
|
|
Loading…
Reference in New Issue