一张图像是像素点的集合,每一个像素都是一个独立,明了的颜色RGBA。
当成百上千万的像素集合到一起后,就构成了图像。
就像下图这样:
图片压缩
- 采用系统的方式进行压缩
//PNG格式 文件属性格式并不会被压缩,压缩的是图片内容(像素)
//压缩的时候,最好不要采取这种方式,而是去用上下文重新生成一个图片,这样的图片才是最小的
//png这类文件本身已经是图像压缩格式了,再用压缩格式去压缩,很可能会出现压缩不了的情况,再加上压缩文件本身是有文件结构信息的(也就是压缩文件头之类的),所以体积自然会增大。所以对于png、mp3等等这类格式,压缩它们往往只是出于打包到一起的目的,并不是为了减少体积。
NSData *pngData = UIImagePNGRepresentation(_albumImage); // 这种压缩耗时长,体积大,但是质量高
NSData *jpgData = UIImageJPEGRepresentation(_albumImage, 0.1); // 耗时短,体积小,质量低
_pngImageView.image = [UIImage imageWithData:pngData];
_jpgImageView.image = [UIImage imageWithData:jpgData];
最好不要采取这种方式压缩,除了有可能压缩下来,体积反而会增大。而且当压缩的时候,只能压缩图片的内容,会装换成bitmap格式,这样所占用的内存会非常大。比如下图:
大小虽然只有836KB,但是当转成bitmap格式进行压缩的时候,所占用的内存至少达:
4288 * 2848 * 4 / 1024 / 1024 = 46.6MB
4288 和 2848 是 图片宽高,4 是 一个像素的字节数。
- 采用上下文,生成一个新的图片
// 因为生成一个bitmap,所以压缩的很小
- (UIImage *)scaleImage:(UIImage *)image size:(CGSize)imageSize {
UIGraphicsBeginImageContext(imageSize);
[image drawInRect:CGRectMake(0, 0, imageSize.width, imageSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
推荐使用这种方式,因为不会占内存,而且也压缩的很小。
图片过滤
我们先要明白一个东西,就是如果要对data类型的数据进行修改,我们需要将它转换成字符数组类型,然后取出数组里面的每个值,修改过后再放回原来的位置。就好像下面这样:
- (void)demoDataC {
//NSString *testStr = ;//@"RGBARGBA" ;//@"255255255255"
NSData *data = [@"0123456789" dataUsingEncoding:NSUTF8StringEncoding];
NSInteger testDataLength = data.length;
Byte *testC = (Byte *)[data bytes]; // 字符数组 testC = "0123456789"
for (NSInteger i = 0; i < testDataLength; i++) {
Byte testChar = (Byte)testC[i]; // 获取每一个元素,进行修改 // 当i=0 testChar=0
printf("%c", testChar);
testChar = testChar + 1;
testC[i] = testChar; // 修改完成后,再赋值回去
}
printf("\n");
for (NSInteger i = 0; i < testDataLength; i++) {
Byte testChar = (Byte)testC[i]; // 修改完成后 当i=0 testChar=1
printf("%c", testChar);
}
}
所以我们要过滤图片,即是要修改图片的data。我们也要先将它转换成字符数组,修改完成过后,再按照原来的样子通过上下文,生成一个bitmap,一一还原。原来的样子,就包括它的宽,高,每一行的字节数和位数,颜色空间和AlphaInfo等。
/*
从图片文件把 图片数据 的像素拿出来(RGBA), 对像素进行操作, 进行一个转换(Bitmap (GPU))
修改完之后,还原(图片的属性 RGBA,RGBA (宽度,高度,色值空间,拿到宽度和高度,每一个画多少个像素,画多少行))
//256(11111111)
*/
- (void)filterImage {
CGImageRef imageRef = self.image.CGImage;
// 一个字节 = 8bit(位) 每行有 17152 个字节,即每行有 17152 * 8 个位 一个像素有 4 个字节
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
size_t bits = CGImageGetBitsPerComponent(imageRef); // 8
size_t bitsPerrow = CGImageGetBytesPerRow(imageRef); // width * 4 (每一行的字节 = 宽度 * 4(即RGBA各对应一个字节))
// 颜色空间
CGColorSpaceRef colorSpace = CGImageGetColorSpace(imageRef);
// AlphaInfo : RGBA AGBR RGB
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef);
// 把图片转化为 bitmap 的数据
CGDataProviderRef providerRef = CGImageGetDataProvider(imageRef);
CFDataRef bitmapData = CGDataProviderCopyData(providerRef);
NSInteger pixLength = CFDataGetLength(bitmapData);
// 像素byte数组 要将data类型的装换成为C字符的数组类型才能进行操作
Byte *pixbuff = CFDataGetMutableBytePtr((CFMutableDataRef)bitmapData);
// 处理像素 RGBA为一个单元
for (int i = 0; i < pixLength; i += 4) {
// 在这里可以计算出每个像素的具体位置。比如:int pixX = i / width(类似这样,但是肯定这不是正确的表达式);
[self eocImageFiletPixBuf:pixbuff offset:i];
}
// 处理好数据过后,就要准备用处理完的数组进行绘制图片了
// bitmap 生成一个上下文,然后用上下文生成一个过滤后的图片 (用上前面保存的图片数据和属性)
CGContextRef contextRef = CGBitmapContextCreate(pixbuff, width, height, bits, bitsPerrow, colorSpace, alphaInfo);
CGImageRef filterImageRef = CGBitmapContextCreateImage(contextRef); // 用上下文生成图片
UIImage *filterImage = [UIImage imageWithCGImage:filterImageRef];
_filterImageV.image = filterImage;
}
// RGBA 为一个单元 彩色照变黑白照 pixBuf是一个数组 offset是每个RGBA的起点
- (void)eocImageFiletPixBuf:(Byte*)pixBuf offset:(int)offset{
int offsetR = offset;
int offsetG = offset + 1;
int offsetB = offset + 2;
int offsetA = offset + 3;
int red = pixBuf[offsetR];
int gre = pixBuf[offsetG];
int blu = pixBuf[offsetB];
// int alp = pixBuf[offsetA];
int gray = (red + gre + blu)/3;
pixBuf[offsetR] = gray;
pixBuf[offsetG] = gray;
pixBuf[offsetB] = gray;
}
这里取红绿蓝的平均值,并且将新的红绿蓝都赋值成这一个平均值,就能达到灰白的效果。
截图
- 规则截图
开启一个图像上下文,并且获取这个上下文,在将规则的path添加到上下文中,再截出上下文,最后将图片画到上下文中,最后再从上下文中获取图片,再最后结束上下文。
// 规则 截图
- (void)shotScreen {
// 开启上下文
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
// 获取上下文
CGContextRef context = UIGraphicsGetCurrentContext();
// 先 clip context ,才能限制后面上下文操作的范围,使图片画在一个圆内
CGRect rect = CGRectMake(0, 0, 200, 200);
CGContextAddEllipseInRect(context, rect); // path 加一个圆到上下文中
CGContextClip(context); // 截出上下文,对于后面进行的上下文操作起限制作用
UIImage *image = [UIImage imageNamed:@"3.jpg"];
// 将图片滑到上下文中
[image drawInRect:rect];
// 从上下文中获取图片,这里的上下文还是 (0, 0, 200, 200) 大小的上下文
UIImage *clipImage = UIGraphicsGetImageFromCurrentImageContext();
// 结束上下文
UIGraphicsEndImageContext();
_eocImageV.image = clipImage;
}
- 非规则截图
道理同上,只是path是一个点的数组,这里点构成的图形可以是不规则的。
// 非规则的截图 (画点,画线)
- (void)noRectClip{
// 开启上下文
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
// 获取上下文
CGContextRef context = UIGraphicsGetCurrentContext();
// 非规则的path
CGMutablePathRef pathRef = CGPathCreateMutable();
// 必须是一个闭环,如果不是闭环,会自己回到最初的点
CGPoint lines[] = {
CGPointMake(0, 0),
CGPointMake(150, 70),
CGPointMake(200, 200),
CGPointMake(50, 120),
CGPointMake(30, 30)
};
CGPathAddLines(pathRef, NULL, lines, 5);
CGContextAddPath(context, pathRef);
CGContextClip(context);
UIImage *imageTwo = [UIImage imageNamed:@"3.jpg"];
[imageTwo drawInRect:CGRectMake(0, 0, 200, 200)];
UIImage *clipImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
_eocImageV.image = clipImage;
}
- 红色渲染
// 红色渲染
- (void)blend {
UIImage *imageTwo = [UIImage imageNamed:@"3.jpg"];
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
CGContextRef context = UIGraphicsGetCurrentContext();
[imageTwo drawInRect:CGRectMake(0, 0, 200, 200)];
// 这里要将不透明度设置为0.5,才是覆盖在上面的效果,如果为1,就直接只能看到红色,不能看到红色下面的图片
UIColor *redColor = [UIColor colorWithRed:1 green:0 blue:0 alpha:0.5];
// 给上下文设置颜色
CGContextSetFillColorWithColor(context, redColor.CGColor);
// 设置渲染模式
CGContextSetBlendMode(context, kCGBlendModeNormal);
// 填充上下文
CGContextFillRect(context, CGRectMake(0, 0, 200, 200));
CGImageRef imageRef = CGBitmapContextCreateImage(context);
_eocImageV.image = [UIImage imageWithCGImage:imageRef];
UIGraphicsEndImageContext();
}
- 截屏
// 截屏
- (void)imageFromFullView{
// 这里要是屏幕的大小
UIGraphicsBeginImageContext(self.view.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[self.view.layer renderInContext:context];
CGImageRef imageRef = CGBitmapContextCreateImage(context);
_eocImageV.image = [UIImage imageWithCGImage:imageRef];
UIGraphicsEndImageContext();
}