iOS Developer

Gif、PNG、jpg

2018-06-26  本文已影响23人  C_HPY

在前端开发过程中,我们知道使用的图片一般全是.png格式的图片,偶尔使用.jpg图片甚至会出现问题,为什么我们开发过程中要使用.png格式的图片?不同格式的图片具体有哪些区别?是什么东西造成了这些区别?本文尽力去试图解释这些问题。

本文主要涉及以下五个问题:

iOS中如何判断一张图片的格式

我们如何用代码去判断一张图片的具体真实类型?这里有一篇文章介绍的很好iOS里面判断图片类型
可能会有部分小伙伴考虑直接用图片后缀进行判断?这个问题我做了一个尝试:
对一个原生是.png格式的图片修改后缀为.jpg后,查看图片的二进制数据文件,发现,二进制数据文件仍然是.png格式的。所以,通过后缀方式去判断方式是不准确的,并且通过修改后缀方式去修改一个图片的格式也是错误的

Gif、png与jpg的区别

GIF(Graphics Interchange Format)

简介
GIF图形交换格式是一种位图图形文件格式,以8位色(即256种颜色)重现真彩色的图像。它实际上是一种压缩文档,采用LZW压缩算法进行编码,有效地减少了图像文件在网络上传输的时间。它是目前应用于网络传输的图像格式之一。
优点
优秀的压缩算法使其在一定程度上保证图像质量的同时将体积变得很小。
可插入多帧,从而实现动画效果。
由于有一个二进制位来控制透明或者不透明,可设置透明色以产生对象浮现于背景之上的效果。
缺点
由于采用了8位压缩,最多只能处理256种颜色,故不宜应用于真彩图像。

PNG(Portable Network Graphics

便携式网络图片(Portable Network Graphics),简称PNG,是一种无损数据压缩位图图形文件格式。PNG格式是无损数据压缩的,允许使用类似于GIF格式的调色板技术,支持真彩色图像,并具备Alpha(半透明)等特性。
PNG可以为原图像定义256个透明层次,使得彩色图像的边缘能与任何背景平滑地融合,从而彻底地消除锯齿边缘。这种功能是GIF和JPEG没有的。
和JPEG2000有点像,PNG图像在浏览器上采用流式浏览,即图像会在完全下载之前提供浏览者一个基本的图像内容,然后再逐渐清晰起来。它允许连续读出和写入图像数据,这个特性很适合于在通信过程中显示和生成图像。

PNG8 PNG24

浅谈下PNG8和PNG24的区别:

png-8 和 gif 有一些相似之处,模式都是索引颜色,只支持像素级的纯透明,不支持 alpha 透明。

我们通常说的“IE6 不支持 PNG 透明”,是指不支持 PNG-24 的透明。但是 IE6 支持 PNG-8 的透明,就像支持gif 的透明一样。

JPG(Joint Photographic Experts Group)

JPEG是一种针对相片影像而广泛使用的一种失真压缩标准方法。JPEG的压缩方式通常是破坏性资料压缩(lossy compression),意即在压缩过程中图像的品质会遭受到可见的破坏。
优点:由于JPEG压缩比例大,文件小,因此在网络传输图片非常流行。
缺点:Jpg图片其实一生成出来就已经是有损的了,他是把一些不打算存储的数据抹擦掉,
,会引起失真,所以不适合存储高端的重要的照片,比如艺术照。
JPEG与jpg格式相似,经常在拷相片的时候看到得,但我们制作图片的时候一般是保存jpg,该种图片格式是文件相对jpg大,因为里面存储了很多相机里的拍摄参数,像色相、饱和度、对比度等
由于JPEG有损压缩,因此砖家组又搞了个新版本,JPEG2000,这个版本支持有损和无损压缩。
JPEG2000有一个极其重要的特征:
能实现渐进传输,即先传输图像的轮廓,然后逐步传输数据,不断提高图像质量,让图像由朦胧到清晰显示。

位图(bitmap)是什么东西

位图和矢量图

位图和矢量图是计算机图形中的两大概念,这两种图形都被广泛应用到出版,印刷,互联网[如flash和svg]等各个方面,他们各有优缺点,两者各自的好处几乎是无法相互替代的,所以,长久以来,矢量跟位图在应用中一直是平分秋色。

位图[bitmap],也叫做点阵图,删格图象,像素图,简单的说,就是最小单位由象素构成的图,缩放会失真。构成位图的最小单位是象素,位图就是由象素阵列的排列来实现其显示效果的,每个象素有自己的颜色信息,在对位图图像进行编辑操作的时候,可操作的对象是每个象素,我们可以改变图像的色相、饱和度、明度,从而改变图像的显示效果。举个例子来说,位图图像就好比在巨大的沙盘上画好的画,当你从远处看的时候,画面细腻多彩,但是当你靠的非常近的时候,你就能看到组成画面的每粒沙子以及每个沙粒单纯的不可变化颜色。

矢量图[vector],也叫做向量图,简单的说,就是缩放不失真的图像格式。矢量图是通过多个对象的组合生成的,对其中的每一个对象的纪录方式,都是以数学函数来实现的,也就是说,矢量图实际上并不是象位图那样纪录画面上每一点的信息,而是纪录了元素形状及颜色的算法,当你打开一付矢量图的时候,软件对图形象对应的函数进行运算,将运算结果[图形的形状和颜色]显示给你看。无论显示画面是大还是小,画面上的对象对应的算法是不变的,所以,即使对画面进行倍数相当大的缩放,其显示效果仍然相同[不失真]。举例来说,矢量图就好比画在质量非常好的橡胶膜上的图,不管对橡胶膜怎样的常宽等比成倍拉伸,画面依然清晰,不管你离得多么近去看,也不会看到图形的最小单位。

位图的好处是,色彩变化丰富,编辑上,可以改变任何形状的区域的色彩显示效果,相应的,要实现的效果越复杂,需要的象素数越多,图像文件的大小[长宽]和体积[存储空间]越大。

矢量的好处是,轮廓的形状更容易修改和控制,但是对于单独的对象,色彩上变化的实现不如位图来的方便直接。另外,支持矢量格式的应用程序也远远没有支持位图的多,很多矢量图形都需要专门设计的程序才能打开浏览和编辑。

iOS 中图片的大小是如何计算的

要计算图片的大小首先要明确几个概念

像素

像素是构成位图的基本单元,当位图图像放大到一定程度时,所看到的一个一个的马赛克色块就是像素且像素色块的大小不是绝对的。在位图图像所包含的所有像素总量称为图像的像素大小。

分辨率

分辨率是指图像在水平和垂直方向上的所容纳的最大像素数。例如分辨率为1024*768的意思是水平像素数为1024个,垂直像素数768个,其像素大小为1024×768=786432,约80万像素。ppi表示的是每英寸所拥有的像素数目,即在一个对角线长度为1英寸的正方形内所拥有的像素数。像素色块越小或者分辨率越高则ppi越大。屏幕分辨率大小决定图像显示的细腻程度。iPhone的分辨率点击这里

计算内存大小

在iOS上,图片会被自动缩放到2的N次方大小,比如一张1024 x 1025的图片,占用的内存与一张1024 x 2048的图片是一致的。图片占用内存的计算公式为: 长(像素点) x 宽(像素点) x 4(RGBA的原因) = 结果(单位是字节)
这样一张512 x 512图片占用的内存就是 512x512x4/1024/1024=1M
附上byte、bit、B的关系:

bit就是位,也叫比特位是计算机表示数据最小的单位
byte就是字节
1byte=8bit
1byte就是1B
一个字符=2字节
1KB=1024B
字节就是Byte,也是B
位就是bit也是b

iOS 中位图图像处理

上面我们已经了解像素和分辨率、以及如何计算图片的大小。不难猜测处理位图图像就是处理每一个像素点上RGBA的数值变化。R、G、B、A分别代表red,green,blue和alpha,也就是颜色组成的三原色与透明度值。
实际操作图片的像素点之前,这里有位图CGImageRef Api的相应说明
so,万事俱备只欠东风,是时候见识一下位图处理的魅力了。
原图如下:

原图.png
对原图进行位图处理:
- (void)viewDidLoad {
    [super viewDidLoad];
    
    __weak typeof (self) weakSelf = self;
    [self handlerImage:self.imageView.image complite:^(UIImage *effectedImage) {
        weakSelf.imageView.image = effectedImage;
    }];
}

- (void)handlerImage:(UIImage *)image complite:(void(^)(UIImage *effectedImage))complite
{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //获取图片位图
        CGImageRef imageref = image.CGImage;
        //图片宽度像素
        size_t width = CGImageGetWidth(imageref);
        //图片高度像素
        size_t height = CGImageGetHeight(imageref);
        //每个颜色的比特数,例如在rgba-32模式下为8
        size_t bitsPerComponent = CGImageGetBitsPerComponent(imageref);
        //每个像素的总比特数
        size_t bitsPerPixel = CGImageGetBitsPerPixel(imageref);
        //每一行占用的字节数,注意这里的单位是字节
        size_t bytesPerRow = CGImageGetBytesPerRow(imageref);
        //颜色空间,比如rgb
        CGColorSpaceRef space = CGImageGetColorSpace(imageref);
        //位图像素布局
        CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageref);
        //数据源提供者
        CGDataProviderRef provider = CGImageGetDataProvider(imageref);
        //解码渲染数组
        const CGFloat * decode = CGImageGetDecode(imageref);
        //是否抗锯齿
        bool shouldInterpolate = CGImageGetShouldInterpolate(imageref);
        //图片相关参数
        CGColorRenderingIntent intent = CGImageGetRenderingIntent(imageref);
       
        
        CFDataRef data = CGDataProviderCopyData(provider);
        //CFData地址
        UInt8 *buffer = (UInt8 *)CFDataGetBytePtr(data);
        NSUInteger x ,y;
        //遍历像素矩阵
        for (y = 0; y < height; y ++) {
            for (x = 0; x < width; x++) {
                
                UInt8 *tmp;
                //每一个像素点的地址
                tmp = buffer + y * bytesPerRow + x * 4;
                UInt8 alpha;
                //默认按照RGBA的方式计算,每一个像素点的第四个字节是存储的alpha内容
                //每一个像素点 alpha通道的地址
                alpha = *(tmp + 3);
                UInt8 temp = *tmp;// 取red字节的值
                *(tmp + 1) = temp;// 设置green字节
                *(tmp + 2) = temp;// 设置blue字节
            }
        }
        
        CFDataRef effectedData = CFDataCreate(NULL, buffer, CFDataGetLength(data));
        CGDataProviderRef effectedDataProvider = CGDataProviderCreateWithCFData(effectedData);
        //生成一张新的位图
        CGImageRef effectedCgImage = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, space, bitmapInfo, effectedDataProvider, decode, shouldInterpolate, intent);
        UIImage *effectedImage = [[UIImage alloc] initWithCGImage:effectedCgImage];
        
        CGImageRelease(effectedCgImage);
        CFRelease(effectedDataProvider);
        CFRelease(effectedData);
        CFRelease(data);
        
        dispatch_async(dispatch_get_main_queue(), ^{
            if (complite) {
                complite(effectedImage);
            }
        });
    });
}

运行结果:

位图处理.png
果然是很神奇...
更多的位图处理参考
iOS中图形图像处理第一部分:位图图像原图修改
Bitmap Images and Image Masks;

iOS 中图片是如何加载渲染的,几个加载图片的api的区别。

掉帧

通常来说,计算机系统中 CPU、GPU、显示器是以下面这种方式协同工作的。CPU 计算好显示内容提交到 GPU,GPU 渲染完成后将渲染结果放入帧缓冲区,随后视频控制器会按照 VSync 信号,逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器显示。

image.png

在 VSync 信号到来后,系统图形服务会通过 CADisplayLink 等机制通知 App,App 主线程开始在 CPU 中计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等。随后 CPU 会将计算好的内容提交到 GPU 去,由 GPU 进行变换、合成、渲染。随后 GPU 会把渲染结果提交到帧缓冲区去,等待下一次 VSync 信号到来时显示到屏幕上。由于垂直同步的机制,如果在一个 VSync 时间内,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。从上图中可以看到,CPU 和 GPU 不论哪个阻碍了显示流程,都会造成掉帧现象。所以开发时,也需要分别对 CPU 和 GPU 压力进行评估和优化。

离屏渲染

当使用圆角,阴影,遮罩的时候,图层属性的混合体被指定为在未预合成之前不能直接在屏幕中绘制,所以就需要屏幕外渲染被唤起。
屏幕外渲染并不意味着软件绘制,但是它意味着图层必须在被显示之前在一个屏幕外上下文中被渲染(不论CPU还是GPU)。
所以当使用离屏渲染的时候会很容易造成性能消耗,因为在OPENGL里离屏渲染会单独在内存中创建一个屏幕外缓冲区并进行渲染,而屏幕外缓冲区跟当前屏幕缓冲区上下文切换是很耗性能的。

关于iOS中的图片是如何渲染到屏幕上的,更多的请参考
绘制像素到屏幕上
iOS图形原理与离屏渲染

未完待续... UI的异步绘制

上一篇下一篇

猜你喜欢

热点阅读