像素、分辨率:
“像素”是数码相机感光器件(CCD或COMS片)上的感光最小单位
分辨率=像素/英寸
照片的清晰度不是取决于像素数,而是取决于像素的“点密度”(就是图片的分辨率——用ppi表示,单位是“像素/英寸”)即:
Dpi、ppi:一英寸多少个像素点 直接反馈清晰度;
照片尺寸:
比如640×480、1024×768、1600 ×1200、2048×1536……,其每一组数字中,前一个数字表示在照片的长度方向上所含的像素点数,后一数字表示在宽的方向上所含的像素点数,两者的乘积,就是像素数。比如1600×1200=1920000≈2000000,就是200万(像素)。
例如以图片的像素是640×480,其尺寸大小是:长:3.556寸,宽:2.667寸;该图片的分辨率即为640÷3.556=180(像素/英寸),或480÷2.667=180(像素/英寸);再比如,一图片的像素是640×480,其尺寸大小为宽9寸、高6.7寸,该图片的分辨率就是:640÷8.9=72(像素/英寸)。
屏幕宽度= 宽度像素 / dpi分辨率;
分辨率dpi = 宽度像素 / 屏幕实际宽度尺寸;
说的更白话一点
一张34562048的图像指的是其总像素数为(34562048),其中长边有3456个像素,宽边有2048个像素。我们所说的手机1500万像素等概念就是源于这个乘积得出来的数值。
分辨率指的是每英寸中含有的像素点的个数,即像素密度单位为(dpi:dots per inch,像素点/英寸)或者也有称之为点密度(ppi),视网膜屏就是因为ppi较高才这样称之为的。
那么图像的一张34562048的图像指的是其总像素数为(34562048),其中长边有3456个像素,宽边有2048个像素。我们所说的手机1500万像素等概念就是源于这个乘积得出来的
这幅图像分辨率200200dpi,大小450450像素,那么就可以得到:
图像大小 = 图像大小 / 分辨率 = 450 / 200 = 2.25
所以,这幅图像的大小为2.252.25英寸
如果要求图像大小变成1.51.5英寸,但像素数仍为450*450,按照公式:
图像大小 = 图像像素数 / 图像分辨率,就得到了图像的分辨率应为:450 / 1.5 = 300dpi
那么图像像素的个数和图像占内存大小有什么关系?更加细节话的为,jpg格式的图像像素的个数和图像占内存大小有什么关系?答案:没有关系,因为jpg格式的图像是有损压缩的,你不知道压缩的算法具体是什么样的,无压缩的BMP位图的大小是相同的可以准确计算的。
两个相同尺寸1920*1200的图像,在相同色彩度(如24位真彩)情况下,无压缩的BMP位图的大小是相同的。
而你看到文件大小的不同,是因为图像压缩算法造成的(无压缩文件会非常大,一般为BMP格式),图像压缩是个比较大的课题,分为无损压缩(PNG等)和有损压缩(JPG等),具体压缩算法非常多,各有优劣。
以最常见的JPG为例,其压缩算法是有损的,压缩后的文件大小有几个重要的决定因素:原图尺寸、有损程度、图像中相邻点色差,图像中存在色彩数量等等。
再说图片压缩:它分为大小的压缩。还有质量的压缩。。。
一般oc都是png图片居多,但是其格式却大于jpg。。cgimage是图片解压后的原始数据。。数据大小来说 cgimage>png>jpg
ios提供图片本身的压缩方式有两种:
UIKIT_EXTERN NSData * __nullable UIImagePNGRepresentation(UIImage * __nonnull image); // return image as PNG. May return nil if image has no CGImageRef or invalid bitmap format
UIKIT_EXTERN NSData * __nullable UIImageJPEGRepresentation(UIImage * __nonnull image, CGFloat compressionQuality); // return image as JPEG. May return nil if image has no CGImageRef or invalid bitmap format. compression is 0(most)..1(least)
有一个问题很奇怪。就是UIImageJPEGRepresentation方法获取的data反而比原图更加大了?
看第一个方法的注释信息:return image as PNG. May return nil if image has no CGImageRef or invalid bitmap format
这里呢,它方法内部生成的data,等同于png格式大小了,这一点我们可以直接写入图片到磁盘 [UIImageJPEGRepresentation(ima,1.0) writeToFile:imagePath atomically:YES];
发现格式已经彻底转变成了png. 所以它变大了。。
系统方式压缩图片质量
(NSData *)compressOriginalImage:(UIImage *)image toMaxKB:(CGFloat)size{
NSData * data = UIImageJPEGRepresentation(image, 1.0);
CGFloat dataKBytes = data.length/1000.0;
CGFloat maxQuality = 0.9f;
CGFloat lastData = dataKBytes;
while (dataKBytes > size && maxQuality > 0.1f) {
maxQuality = maxQuality - 0.1f;
data = UIImageJPEGRepresentation(image, maxQuality);
dataKBytes = data.length / 1000.0;
if (lastData == dataKBytes) {
break;
}else{
lastData = dataKBytes;
}
}
return data;
}
通过上下文重绘方式压缩图片大小(UIImage)cpImageWithImage:(UIImage)image toSize:(CGSize)newSize
{
// Create a graphics image context
UIGraphicsBeginImageContext(newSize);
// Tell the old image to draw in this new context, with the desired
// new size
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
// Get the new image from the context
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
// End the context
UIGraphicsEndImageContext();
// Return the new image.
return newImage;
}
一段sd内部的代码重绘(注意坐标,是我们大学课本所学坐标系了。不再是oc坐标轴)-
(UIImage)sdimageWithImage:(UIImage _Nullable )image scaledToSize:(CGSize)newSize
{
CGImageRef imageRef;
if ((image.size.width / image.size.height) < 1) {
newSize.width = image.size.width;
newSize.height = image.size.width ;imageRef = CGImageCreateWithImageInRect([image CGImage], CGRectMake(0,fabs(image.size.height - newSize.height) / 2, newSize.width, newSize.height));
} else {
newSize.height = image.size.height;
newSize.width = image.size.height *1;imageRef = CGImageCreateWithImageInRect([image CGImage], CGRectMake(fabs(image.size.width - newSize.width) / 2, 0, newSize.width, newSize.height));
}
UIImage * image11 = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
return image11;
}
一般来说,我们应用的是大小+质量来达到自己的目的。。至于再高深的层次压缩。。涉及太过高大上。。么有深入研究。。摘抄个等比压缩方法
/**
压缩图片大小到1080 压缩图片质量 大小到1M以内
*/
-
(NSData )cpImageWithImage:(UIImage )sourceImage{
assert(sourceImage != nil);
//压缩大小到1080
CGFloat scaleMark = 1080;
CGSize imageSize = sourceImage.size;//取出要压缩的image尺寸
CGFloat width = imageSize.width; //图片宽度
CGFloat height = imageSize.height; //图片高度
//1.宽高大于1280(宽高比不按照2来算,按照1来算)
if (width>scaleMark||height>scaleMark) {
if (width>height) {
CGFloat scale = height/width;
width = scaleMark;
height = widthscale;
}else{
CGFloat scale = width/height;
height = scaleMark;
width = heightscale;
}
//2.宽大于1280高小于1280
}else if(width>scaleMark||height<scaleMark){
CGFloat scale = height/width;
width = scaleMark;
height = widthscale;
//3.宽小于1280高大于1280
}else if(width<scaleMark||height>scaleMark){
CGFloat scale = width/height;
height = scaleMark;
width = heightscale;
//4.宽高都小于1280
}else{
}
UIGraphicsBeginImageContext(CGSizeMake(width, height));
[sourceImage drawInRect:CGRectMake(0,0,width,height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();//压缩质量
CGFloat aimSizeKB = 1024;
NSData * data = UIImageJPEGRepresentation(newImage, 1.0);
CGFloat dataKBytes = data.length/1000.0;
CGFloat maxQuality = 0.9f;
CGFloat lastData = dataKBytes;
while (dataKBytes > aimSizeKB && maxQuality > 0.1f) {
maxQuality = maxQuality - 0.1f;
data = UIImageJPEGRepresentation(newImage, maxQuality);
dataKBytes = data.length / 1000.0;
// NSLog(@"==lastdata:%fkb ==dataKBytes:%fkb maxQuality: %f",lastData,dataKBytes,maxQuality);
if (lastData == dataKBytes) {
break;
}else{
lastData = dataKBytes;
}
}
if (DEBUG) {
NSLog(@"\n CpImage cpImageWithImage \n newSize is %@ \n imageLen is %fKB\n \n",NSStringFromCGSize(newImage.size),lastData);
}
return data;
}
CGBitmapContextCreate还支持位图的修改。。这部分有兴趣可以后期翻越翻越。。
根据CGBitmapContextCreate的需要的参数以及获取对应的参数,并传入
CGContextRef contextRef = CGBitmapContextCreate(pixelBuf, width, height, bits, bitsPerRow, colorSpace, alphaInfo);
CGBitmapContextCreate参数的获取
CGImageRef imageRef = self.image.CGImage;//获取位图
size_t width = CGImageGetWidth(imageRef);//位图宽度
size_t height = CGImageGetHeight(imageRef);//位图高度
size_t bits = CGImageGetBitsPerComponent(imageRef);//一个通道占用了多少bit
size_t bitsPerRow = CGImageGetBytesPerRow(imageRef);//每行有多少字节
CGColorSpaceRef colorSpace = CGImageGetColorSpace(imageRef);
int alphaInfo = CGImageGetAlphaInfo(imageRef);//获取色彩空间,抽象概念。色图空间有灰色和彩色
CGDataProviderRef provideRef = CGImageGetDataProvider(imageRef);//位图转化为数据
CFDataRef dataRef = CGDataProviderCopyData(provideRef);//把数据转化为CFDataRef格式
UInt8 *pixelBuf = (UInt8 *)CFDataGetMutableBytePtr((CFMutableDataRef)dataRef);//把CFDataRef数据转化为指针形式
int lenght = (int)CFDataGetLength(dataRef);//获取数据的长度,用于处理每一个RGBA通道
根据上述代码的图片数据指针pixelBuf传入一个for循环,结合一定的图形学知识实现像素点处理,简单示例如下:
//rgba有四个通道,代表每次偏移一个像素点
for (int i = 0; i < lenght; i+=4) {
//////修改原始像素RGB数据
[self eocImageFilterFormBuf:pixelBuf offset:i];
}
//遍历完后开始绘制
CGContextRef contextRef = CGBitmapContextCreate(pixelBuf, width, height, bits, bitsPerRow, colorSpace, alphaInfo);
图片变黑白
// 对像素点进行加工
-
(void)eocImageFilterFormBuf:(UInt8*)pixelBuf offset:(int)offset{
//不处理alpha通道
int offsetR = offset;
int offsetG = offset + 1;
int offsetB = offset + 2;int red = pixelBuf[offsetR];
int green = pixelBuf[offsetG];
int blue = pixelBuf[offsetB];int gray = (red + green + blue)/3;
pixelBuf[offsetR] = gray;
pixelBuf[offsetG] = gray;
pixelBuf[offsetB] = gray;
}
图片变蓝
// 对像素点进行加工 -
(void)eocImageFilterFormBuf:(UInt8*)pixelBuf offset:(int)offset{
//不处理alpha通道
int offsetR = offset;
int offsetG = offset + 1;
int offsetB = offset + 2;int red = pixelBuf[offsetR];
int green = pixelBuf[offsetG];
int blue = pixelBuf[offsetB];pixelBuf[offsetR] = red * 0.3;
pixelBuf[offsetG] = green * 0.3;
pixelBuf[offsetB] = blue * 0.3;
}
- 效果浏览
blue.png
grey.png
调用context的系统api渲染像素点
以渲染为红色例
- (UIImage)imageWithColor:(UIColor)color{
UIImage *rendImage = [UIImage imageNamed:@"3.jpg"];
// 1 创建上下文
UIGraphicsBeginImageContext(rendImage.size);
// 2 获取上下文
CGContextRef context = UIGraphicsGetCurrentContext();
// 3 把图片渲染到上下文
[rendImage drawInRect:CGRectMake(0, 0, rendImage.size.width, rendImage.size.height)];
UIColor *redColor = [UIColor colorWithRed:1 green:0 blue:0 alpha:0.5];
CGContextSetFillColorWithColor(context, redColor.CGColor);
// 3 把颜色渲染到上下文
CGContextSetBlendMode(context, kCGBlendModeNormal);
// 4 颜色渲染区域
CGContextFillRect(context, CGRectMake(0, 0,rendImage.size.width, rendImage.size.height));
// 5 生成图片
CGImageRef imageRef = CGBitmapContextCreateImage(context);
UIImage *redImage = [UIImage imageWithCGImage:imageRef];
CFRelease(imageRef);
UIGraphicsEndImageContext();
// 裁剪
return redImage;
}
内存管理相关
有copy create都要CFRelease
CFRelease(dataRef);
CFRelease(contextRef);
CFRelease(backImageRef);
3.自定义局部截屏
本质都是操作上下文context
3.1 全屏截屏与局部矩形截屏
截屏
-
(UIImage)imageFromView:(UIView)view{
CGRect rect = view.frame;
// 1 创建上下文
UIGraphicsBeginImageContext(view.frame.size);
// 2 获取上下文
CGContextRef context = UIGraphicsGetCurrentContext();
// 3 把view 渲染到上下吻
[view.layer renderInContext:context];
// 4 把上下文中生成图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
局部矩形截屏 -
(UIImage)imageFromView:(UIView)view{
CGRect rect = view.frame;
UIGraphicsBeginImageContext(view.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[view.layer renderInContext:context];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
//上面和截屏是一样,下面的方法是截取当前上下文的某个位置
image = [self getSubImage:CGRectMake(100, 100, _eocImageV.frame.size.width, _eocImageV.frame.size.height) image:image.CGImage];
UIGraphicsEndImageContext();
return image;
}
-
(UIImage*)getSubImage:(CGRect)rect image:(CGImageRef)cgImage{
CGImageRef imageRef = CGImageCreateWithImageInRect(cgImage, rect);
UIImage *subImage = [UIImage imageWithCGImage:imageRef];
return subImage;
}
局部圆形截屏(如上传头像)
通过路径绘制(闭环)实现上下文自定义区域与形状的截取
-
(UIImage)circleImage:(UIImage)image{
// 1 创建上下文
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
// 2 获取上下文
CGContextRef context = UIGraphicsGetCurrentContext();// 3 操作上下文
CGContextSetLineWidth(context, 1);
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGRect rect = CGRectMake(0, 0, 200, 200);
CGContextAddEllipseInRect(context, rect);
// 截剪上下文为圆形 闭环的
CGContextClip(context);// 4 把image 渲染到相应的上下文中
[image drawInRect:CGRectMake(0, 0, 200, 200)];UIImage *backImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return backImage;
}
缩放
ScorllView自带的系统实现
设置缩放系数
_scrollview.maximumZoomScale = 5;
_scrollview.minimumZoomScale = 0.5;
设置代理方法
-
(UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView{
return _imageView;
}
捏合手势实现
-
(void)pinchHandel:(UIPinchGestureRecognizer*)gesture{
NSLog(@"%f", gesture.scale);
if (gesture.state == UIGestureRecognizerStateBegan) {
_preScal = 1;
}
float scaleF = gesture.scale - _preScal + 1;
_preScal = gesture.scale;
_imageView.transform = CGAffineTransformScale(_imageView.transform, scaleF, scaleF);
[_scrollview setContentSize:_imageView.frame.size];
}