聊一聊前端开发中既熟悉又陌生的图片

简介: 聊一聊前端开发中既熟悉又陌生的图片

640.jpg

最近在研究webAssembly在图片处理方面的一些应用,对图片的相关知识进行了一些总结。


图片的分类


图片一共可以分为两类,一类是以svg为代表的矢量图形,是用点、直线或者多边形等基于数学方程的几何图元表示图像。矢量图不论显示画面的大小,由于其记录的是“画出图片的步骤”,所以不会失真。更进一步的内容,本文就不再深入了。

另一类图片,就是位图(也叫点阵图),使用像素排列形成的图片。我们常见的png,jpg等图片就是【位图】。本文的内容均围绕位图展开


图片中究竟存了什么


我们看到的图片,其实是由一个一个的点组成的。这些点,被称为像素点,每一个像素点一种颜色。像下面这样

640.png


随便找一张图片进行放大,可以看出来。


5f948005da720928e84e9ade692423e0.png


如果一张图片长10英寸 ,宽10英寸,那这种图片包含多少个像素点呢?这还和图片的分辨率(dpi)有关。dpi,即图像每英寸长度内的像素点数。


像素 = 尺寸 * 分辨率


互联网中的图片一般是72dpi和96dpi。10英寸 * 10英寸的图片,如果dpi是72,那就是720 * 720个像素点。使用操作系统的图片查看器,就能看到图片的这些信息了。


09f9675e727eafeb2b54b8d370386cbc.png


从上面的图片信息里面,我们还可以看到一个【深度】。这里的深度指的是图片的位深度


图片的位深度表示的是用多少个二进制位来记录像素点的颜色。例如位深度为8,即表示用8个bit来表示一个像素点的颜色。一共可以表示 2^8 = 256种颜色。

常见的位深度有:


  • 1位。黑白照片,只有黑色和白色
  • 4位。有2的4次幂种颜色,16种颜色
  • 8位。2的8次幂表示,它含有256种颜色(VGA)
  • 24位。2的24次幂种颜色,16777216种颜色(真彩色, SVGA)
  • 32位。32位在24位基础上加了透明度,颜色数量和24位是一样的.

说到这里,就不得不先提到颜色。最常用的颜色空间就是RGB了。RGB颜色空间中, 我们认为所有的颜色都是由红、绿、蓝 ( RGB ) 三基色组成的,任何一种颜色都可以转换成这三种基本颜色的混合,只是各基色的比例不同而已。使用RGB颜色空间来表示颜色时,每一种基色都用8个bit来表示,3种基色一共需要8*3=24个bit,也就是需要用24位才能表示一种颜色。例如


0000 0000 0000 0000 0000 0000 - 黑色 #0000001111 1111 1111 1111 1111 1111 - 白色 #ffffff

那么按照这种方式的话,如果想要完整描述一张100像素 * 100像素的图片,那岂不是需要:


8 * 3 * 100 * 100 = 240000 bit = 30000 Bytes = 29.296875 KB


一张 100 * 100的图片会有这么大么?这就涉及到图片的格式了。不同格式的图片,会采用不同的编码方式对原始图片数据进行编码、压缩等操作。最终我们看到的图片,如jpg,png,gif等,都是经过一系列处理后得到的产物。至于怎么进行压缩,每一种图片格式有什么区别,网上有很多不错的文章已经讲得挺清楚了,这里就不展开了。


前端js代码操作图片


判断图片类型


前端如何判断图片的类型呢?根据后缀是不靠谱的。因为后缀是可以随意更改的,但是更改了后缀后,并不能改变图片本身的格式。比如我把一张png图片直接改成了jpg后缀,查看其类型,依然是png类型。

6e98bf4c951db8f66f3960e286ddd8a2.png



每一种格式的图片,都是以二进制形式存储的。下面就是一张图片中的二进制数据(以16进制表示的)。


184bc23fb5832dbd0a3af24e661e59bb.png


二进制数据的头几个字节,标识了图片的元数据信息,这其中,就包括了图片的格式。每一种图片格式对应的元数据是固定的(可以称之为“魔数”),我们可以通过这个点来判断图片格式。

比如,png图片的魔数为:[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A],jpg图片的魔数为:[0xff, 0xd8, 0xff, 0xe0]。以png为例,通过<input type="file">选择一张图片后,判断该图片是否是png的代码可以参考:


// 读取指定长度字节function readBytes(sBuffer, begin, length) {  return Array.prototype.slice.call(new Uint8Array(sBuffer, begin, begin + length));}
const reader = new FileReader();reader.onload = (e) => {  const buffer = e.target.result;      const header = readBytes(buffer, 0, 8);   // png的魔数  const magicNumber = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];  // 图片的头8个字节,就可以判断是否为png图片  let isPng = true;  for (let i = 0; i <= 7; i++) {    if(header[i] !== magicNumber[i]) {      isPng = false;    }  }};// file是通过input的onChange拿到的图片file对象,通过readAsArrayBuffer可以将file对象转化为ArrayBufferreader.readAsArrayBuffer(file);


这里的ArrayBuffer 就是原始的二进制数据了。但是我们没法直接操作它,需要使用Uint8Array建立一个类数组试图才可以。


对于直接给出http链接的图片,可以直接使用以下代码,也能拿到其原始二进制数据:


fetch(url).then(function(res){return res.arrayBuffer();});


这里有一个点需要注意,通过 FileReader.readAsDataURL 可以将图片文件读取为base64,而base64的开头也有图片格式信息。这个信息不能准确表示该图片的格式:

dfc5753874189ad78c5873ec48694ee4.png


大多数情况下,我们只需要使用文件后缀来识别图片格式即可。某些情况下(比如需要在前端对图片进行压缩,采用不同算法的时候),或许需要采用读取二进制魔数的方式来判断图片格式了,npm上有不少的库可以使用。


canvas读取图片


前端开发中,在对图片进行处理的时候,常常使用canvas来获取图片的原始数据。


ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, image.width, image.height); ctx.getImageData(0, 0, image.width, image.height);


先将图片画到canvas上,然后通过getImageData来获取canvas上图片的像素信息。getImageData返回的是ImageData对象:


- ImageData.data 只读

Uint8ClampedArray 描述了一个一维数组,包含以 RGBA 顺序的数据,数据使用  0 至 255(包含)的整数表示。

- ImageData.height 只读

无符号长整型(unsigned long),使用像素描述 ImageData 的实际高度。

- ImageData.width 只读

无符号长整型(unsigned long),使用像素描述 ImageData 的实际宽度。


104378a0ed78e8ad743de821c8658728.png


需要注意的是,这里的data与图片的原始数据arrayBuffer是不一样的。任意格式的图片在画到canvas上后,都会变成相同的数据格式。浏览器在通过canvas画出图像时进行了一些处理(比如解析图片等),具体的细节,欢迎知道的小伙伴指教。


ImageData.data里面的一维数组,每四个元素表示一个像素点。

91d5c1705fd6818df1391522957e4263.png

获取到图片每一个像素点信息后,就可以很灵活的进行操作了,比如反转颜色、图片置灰等等。


小结


图片(这里特指位图)的本质就是一个一个的像素点的拼接,我们可以通过其二进制数据,并结合不同的图片格式来深入了解。而当前端需要对图片进行处理时,往往需要canvas来作为辅助。当需要对图片进行更进一步复杂的操作、涉及到大量计算时(比如压缩),还需要借助webAssembly。最近花了不少的时间,大概跑通了将图片压缩算法编译到webAssembly的过程,后面再进行总结吧。


推荐阅读

浏览器什么时候会发起网络请求,去加载一张图片?

相关文章
|
2月前
|
前端开发
后端返回图片二进制流,前端转base64
本文介绍了如何将后端返回的图片二进制流转换为Base64格式,以便在前端使用。通过在axios请求中设置`responseType`为`arraybuffer`,然后使用`btoa`和`Uint8Array`进行转换。
231 5
|
1月前
|
存储 前端开发 JavaScript
🚀前端轻松实现网页内容转换:一键复制、保存图片及生成 Markdown
在现代前端开发中,提升用户的交互体验至关重要。本文将详细介绍如何使用 HTML2Canvas 和 Turndown 两个强大的 JavaScript 库,实现将网页选中文本转化为图片并保存或复制到剪贴板,或将内容转换为 Markdown 格式。文章包含核心代码实现、技术细节和功能拓展方向,为开发者提供了一个轻量级的解决方案,提升用户体验。
131 68
|
1月前
|
JavaScript 前端开发 Python
django接收前端vue传输的formData图片数据
django接收前端vue传输的formData图片数据
33 4
|
1月前
|
前端开发 小程序 Java
java基础:map遍历使用;java使用 Patten 和Matches 进行正则匹配;后端传到前端展示图片三种情况,并保存到手机
这篇文章介绍了Java中Map的遍历方法、使用Pattern和matches进行正则表达式匹配,以及后端向前端传输图片并保存到手机的三种情况。
19 1
|
1月前
|
前端开发 JavaScript 编译器
不走弯路,纯前端如何把图片导出成视频!
【10月更文挑战第3天】不走弯路,纯前端如何把图片导出成视频!
42 3
|
1月前
|
JavaScript 前端开发 编译器
吐血整理:纯前端如何实现批量dom转图片,并下载成压缩包
【10月更文挑战第2天】吐血整理:纯前端如何实现批量dom转图片,并下载成压缩包
48 2
|
2月前
|
前端开发
前端之图片操作
前端之图片操作
|
2月前
|
前端开发 Windows
【前端web入门第一天】02 HTML图片标签 超链接标签 音频标签 视频标签
本文档详细介绍了HTML中的图片、超链接、音频和视频标签的使用方法。首先讲解了`&lt;img&gt;`标签的基本用法及其属性,包括如何使用相对路径和绝对路径。接着介绍了`&lt;a&gt;`标签,用于创建超链接,并展示了如何设置目标页面打开方式。最后,文档还涵盖了如何在网页中嵌入音频和视频文件,包括简化写法及常用属性。
50 13
|
2月前
|
前端开发 JavaScript
node接收前端上传的图片,单文件、多文件同name、多文件不同name
本文介绍了在Node.js中使用multer模块接收前端上传的图片,包括单文件上传、多文件上传(同name和不同name)以及任意类型文件上传的方法。
53 0
|
3月前
|
JavaScript 前端开发 编译器
纯前端如何实现批量dom转图片,并下载成压缩包
【8月更文挑战第22天】纯前端如何实现批量dom转图片,并下载成压缩包
96 0