效果处理(内阴影、外阴影、外发光、内发光、投影)

简介: 效果处理(内阴影、外阴影、外发光、内发光、投影)

前言


最近在做效果处理,其中遇见了一些问题,写篇文章记录一下之前遇见的问题,这里提供两种思路来处理

1.png

1.png


第一种:这种思路主要是采用Layer方式处理,采用偏移的方式达到内外阴影效果


内阴影:通过偏移XY

内发光:采用两个layer偏移处理

外发光、外阴影:采用4个layer向4个方向偏移


拷贝效果


- (instancetype)copyWithZone:(NSZone *)zone {
    KJShadowLayer *layer = [[KJShadowLayer allocWithZone:zone] init];
    layer.frame = self.frame;
    layer.kj_path = self.kj_path;
    layer.kj_color = self.kj_color;
    layer.kj_offset = self.kj_offset;
    layer.kj_radius = self.kj_radius;
    layer.kj_opacity = self.kj_opacity;
    layer.kj_shadowType = self.kj_shadowType;
    return layer;
}


绘制Layer


- (void)drawInContext:(CGContextRef)context {
    CGRect rect = self.bounds;
    if (self.borderWidth != 0) {
        rect = CGRectInset(rect, self.borderWidth, self.borderWidth);
    }
    CGContextSaveGState(context);
    if (self.kj_shadowType == KJShadowTypeInner || 
        self.kj_shadowType == KJShadowTypeInnerShine) {
        CGContextAddPath(context, self.kj_path.CGPath);
        CGContextClip(context);
        CGMutablePathRef outer = CGPathCreateMutable();
        CGPathAddRect(outer, NULL, CGRectInset(rect, -1 * rect.size.width, -1 * rect.size.height));
        CGPathAddPath(outer, NULL, self.kj_path.CGPath);
        CGPathCloseSubpath(outer);
        CGContextAddPath(context, outer);
        CGPathRelease(outer);
    } else {
        CGContextAddPath(context, self.kj_path.CGPath);
    }
    UIColor *color = [self.kj_color colorWithAlphaComponent:self.kj_opacity];
    CGContextSetShadowWithColor(context, self.kj_offset, self.kj_radius, color.CGColor);
    if (self.kj_shadowType == KJShadowTypeOuterShine || 
        self.kj_shadowType == KJShadowTypeOuter) {
        CGContextDrawPath(context, kCGPathEOFill);
    } else {
        CGContextDrawPath(context, kCGPathEOFillStroke);
    }
    CGContextRestoreGState(context);
}


设置属性


self.kj_path = self.kj_shadowPath;
self.kj_color = self.kj_shadowColor;
self.kj_radius = self.kj_shadowRadius;
self.kj_opacity = self.kj_shadowOpacity;
self.kj_offset = CGSizeMake(self.kj_shadowDiffuse, self.kj_shadowDiffuse);
[self setNeedsDisplay];

1.png

1.png


第二种:主要操作图片的方式来处理,新建ImageView来承载投影、阴影等效果


1、投影 - 核心思路


1.1 - 归档复制要投影的视图(因为我只需要上面的图片,所以采用归档的方式复制一份再截图处理)


/// 复制UIView
- (UIView *)kj_copyView:(UIView *)view{
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:view];
    return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}


1.2 - 截图并修改图片颜色


/// 获取截图
- (UIImage *)kj_captureView:(UIView *)view{
    UIGraphicsBeginImageContext(view.bounds.size);
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    [view.layer renderInContext:ctx];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}
/// 改变图片颜色
- (UIImage *)kj_changeColor:(UIColor *)color image:(UIImage *)image{
    UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(context, 0, image.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGContextSetBlendMode(context, kCGBlendModeNormal);
    CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
    CGContextClipToMask(context, rect, image.CGImage);
    [color setFill];
    CGContextFillRect(context, rect);
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}


1.3 - 实现效果


距离和角度:采用偏移坐标的方式处理

CGFloat x = info.diffuse * sin(info.angle);
CGFloat y = info.diffuse * cos(info.angle);
self.frame = CGRectMake(self.originX+x, self.originY+y, self.width, self.height);

模糊:这里采用 Accelerate 框架里面的模糊滤镜处理,

主要函数 box滤镜vImageBoxConvolve_ARGB8888和交换像素通道vImagePermuteChannels_ARGB8888

这里有个细节需要注意:CGImageAlphaInfo 需要使用kCGImageAlphaPremultipliedLast枚举,从而保留透明区域(不变黑)

/// box滤镜(模糊滤镜)
error = vImageBoxConvolve_ARGB8888(&inBuffer,&outBuffer,NULL,0,0,boxSize,boxSize,NULL,kvImageEdgeExtend);
/// 交换像素通道从BGRA到RGBA
const uint8_t permuteMap[] = {2, 1, 0, 3};
vImagePermuteChannels_ARGB8888(&outBuffer,&rgbOutBuffer,permuteMap,kvImageNoFlags);
/// kCGImageAlphaPremultipliedLast 保留透明区域
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef ctx = CGBitmapContextCreate(rgbOutBuffer.data,
                                         rgbOutBuffer.width,
                                         rgbOutBuffer.height,
                                         8,
                                         rgbOutBuffer.rowBytes,
                                         colorSpace,
                                         kCGImageAlphaPremultipliedLast);

1.png


2、阴影(其实阴影和发光根本原理一样)


2.1 - 生成路径图


/// 生成路径图
- (UIImage *)kj_pathImageWithExtend:(CGFloat)extend color:(UIColor *)color{
    UIGraphicsBeginImageContext(self.superview.size);
    UIBezierPath *path = self.outsidePath;
    path.lineWidth = extend;
    path.lineCapStyle = kCGLineCapRound;
    path.lineJoinStyle = kCGLineCapRound;
    [color set];
    [path stroke];
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return img;
}


2.2 - 模糊处理(处理方式和投影一致)


这里需要注意的就是生成的路径图是包含内外阴影,单独使用的话需要做裁剪处理


2.3 - 裁剪处理


外阴影:将路径内部的裁剪掉

/// 路径内部裁剪,保留路径以外区域
- (UIImage *)kj_outerCaptureImage:(UIImage *)image{
    UIGraphicsBeginImageContextWithOptions(self.superview.bounds.size, NO, image.scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetBlendMode(context, kCGBlendModeClear);/// kCGBlendModeClear 裁剪部分透明
    [image drawInRect:self.superview.bounds];
    CGContextAddPath(context, self.outsidePath.CGPath);
    CGContextDrawPath(context, kCGPathEOFill);
    UIImage *newimage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newimage;
}

内阴影:同理,将路径以外部分裁剪掉

/// 裁剪掉路径以外区域
- (UIImage *)kj_innerCaptureImage:(UIImage *)image{
    UIGraphicsBeginImageContextWithOptions(self.superview.bounds.size, NO, image.scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetBlendMode(context, kCGBlendModeClear);
    [image drawInRect:self.superview.bounds];
    UIBezierPath *path = ({ /// 镂空
        UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.superview.bounds];
        path.usesEvenOddFillRule = YES;
        [path appendPath:self.outsidePath];
        path;
    });
    CGContextAddPath(context, path.CGPath);
    CGContextDrawPath(context, kCGPathEOFill);
    UIImage *newimage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newimage;
}

1.png


阴影发光都可以是一个独立的图层(UIImageView),因此他们是可以互相共同存在。


备注:本文用到的部分函数方法和Demo,均来自三方库KJCategories


内阴影,外阴影,外发光,内发光,投影介绍就到此完毕,后面有相关再补充,写文章不容易,还请点个**小星星**传送门

相关文章
|
9月前
|
图形学 开发者
3D模型如何设置凹凸贴图?
3D模型如何设置凹凸贴图?
96 0
|
9月前
|
搜索推荐 图形学
如何为3D模型设置自发光材质?
自发光贴图对模型的增效效果主要体现在增加真实感、强调细节、创造氛围、区分材质等方面。适当运用自发光贴图可以提升模型的视觉表现力,并为观众带来更加丰富、吸引人的视觉体验。
86 0
VS2019显示器颜色调整成护眼模式的颜色
工具 -> 选项 -> 环境 -> 字体和颜色,显示项为“纯文本”,设置项背景色:色调=85,饱和度=123,亮度=205,或者 rgb为199,238,206
|
8月前
颜色拾取器
颜色拾取器
34 0
|
10月前
如何在 kitten 里生成动态个数个立方体(水平方向平铺)以及避免闪烁问题
如何在 kitten 里生成动态个数个立方体(水平方向平铺)以及避免闪烁问题
|
10月前
|
图形学 流计算
GLTF编辑器教你区分自发光贴图和光照贴图
自发光贴图主要用于模拟物体表面的发光效果,突出特定区域或绘制发光字体等。而光照贴图则用于模拟场景中光源的间接照明效果,提高渲染效率和实现真实的光照效果
319 0
|
11月前
|
编译器 Windows
<1>[QTCN]颜色拾取器
桌面置顶 获取全局鼠标坐标和颜色(Web值、RGB) 新增: 按下鼠标右键停止/开启
74 1
【Three.js入门】灯光与阴影、平行光阴影属性、聚光灯的属性和应用
【Three.js入门】灯光与阴影、平行光阴影属性、聚光灯的属性和应用
214 1
如何在 kitten 里生成动态个数个立方体(水平方向平铺)以及避免闪烁问题(2)
如何在 kitten 里生成动态个数个立方体(水平方向平铺)以及避免闪烁问题
如何在 kitten 里生成动态个数个立方体(水平方向平铺)以及避免闪烁问题(2)