(十七)WebP 的测试与使用

版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。

一、WebP

1.什么是 WebP

WebP(发音 weppy),是一种同时提供了有损压缩与无损压缩(可逆压缩)的图片文件格式,派生自视频编码格式 VP8,被认为是 WebM 多媒体格式的姊妹项目,是由 Google 在购买 On2 Technologies 后发展出来,以 BSD 授权条款发布。

WebP 最初在2010年发布,目标是减少文件大小,但达到和 JPEG 格式相同的图片质量,希望能够减少图片档在网络上的发送时间。 2011年11月8日,Google 开始让 WebP 支持无损压缩和透明色(alpha 通道)的功能,而在 2012 年 8 月 16 日的参考实做 libwebp 0.2.0 中正式支持。根据 Google 较早的测试,WebP 的无损压缩比网络上找到的 PNG 档少了 45% 的文件大小,即使这些 PNG 档在使用 pngcrush 和 PNGOUT 处理过,WebP 还是可以减少 28% 的文件大小。

WebP 支持的像素最大数量是16383x16383。有损压缩的 WebP 仅支持 8-bit 的 YUV 4:2:0 格式。而无损压缩(可逆压缩)的 WebP 支持 VP8L 编码与 8-bit 之 ARGB 色彩空间。又无论是有损或无损压缩皆支持 Alpha 透明通道、ICC 色彩配置、XMP 诠释数据。

这是一段来自维基百科对 WebP 的介绍,简单的说,就是 WebP 比 PNG 等其他图片小得多
在这里插入图片描述

2.WebP 图片的生成

使用 AS 进行转换:

在 Android Studio 中,右键图片或文件夹,选择 Convert to WebP ,即可生成 WebP 图片。在 Android Studio 中,要使用 WebP ,需要 Android API 在 18 以上。
在这里插入图片描述
在这里插入图片描述
原先 6.6KB 的 JPG 图片转换成 WEBP 的图片,只有1.6 KB。这是非常可观的。

使用工具生成

使用 iSparta 进行转换,点击进行下载使用。(个人使用 win 10 笔记本,不知道什么原因,不能选择图片。所以就不再这边进行使用讲解。)

3.WebP 性能

这边使用 WEBP 格式与 JPEG 格式进行比较。

对同一张 png 图片进行编码生成不同 quality 的 WEBP 和 JPEG 图片,记录时间,然后把生成的图片在进行解码,记录时间,进行对比。
MainActivity:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.xiaoyue_png);

        compress(bitmap, Bitmap.CompressFormat.JPEG, 100);
        compress(bitmap, Bitmap.CompressFormat.JPEG, 70);
        compress(bitmap, Bitmap.CompressFormat.JPEG, 50);
        compress(bitmap, Bitmap.CompressFormat.JPEG, 30);
        compress(bitmap, Bitmap.CompressFormat.JPEG, 0);

        compress(bitmap, Bitmap.CompressFormat.WEBP, 100);
        compress(bitmap, Bitmap.CompressFormat.WEBP, 70);
        compress(bitmap, Bitmap.CompressFormat.WEBP, 50);
        compress(bitmap, Bitmap.CompressFormat.WEBP, 30);
        compress(bitmap, Bitmap.CompressFormat.WEBP, 0);
    }
    
    /**
     * 压缩图片到文件
     * @param bitmap 待压缩图片
     * @param format 压缩的格式
     * @param quality   质量
     */
    private void compress(Bitmap bitmap, Bitmap.CompressFormat format, int quality){

        String path;
        if (format == Bitmap.CompressFormat.WEBP){
            path = Environment.getExternalStorageDirectory().getPath()
                    + "/xiaoyue_png" + quality + ".webp";
        }else {
            path = Environment.getExternalStorageDirectory().getPath()
                    + "/xiaoyue_png" + quality + ".jpeg";
        }

        long time = System.currentTimeMillis();
        FileOutputStream outputStream;
        try{
            outputStream = new FileOutputStream(path);
            bitmap.compress(format, quality, outputStream);
            outputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        Log.e(TAG, "111 zx 编码 " + format + " 图片, 质量 " + quality
                + " 耗时:" + (System.currentTimeMillis() - time));

        time = System.currentTimeMillis();
        BitmapFactory.decodeFile(path);
        Log.e(TAG, "111 zx 解码 " + format + " 图片, 质量 " + quality
                + " 耗时:" + (System.currentTimeMillis() - time));
    }
}

在 AndroidManifest.xml 中配置文件读写权限。

AndroidManifest.xml:

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

结果:
在这里插入图片描述
生成的图片文件:
在这里插入图片描述
结论: 相同的 quality 下:

编码 :WEBP 比 JPEG 耗时多很多	

解码:耗时差不多,JPEG 稍微快一点点,可忽略不计

内存:WEBP 的图片内存比 JPEG 的图片内存小很多

注: 我们把 drawable 下的图片换成 WEBP,这时候生成图片耗时对应用是没有影响的,但是可以缩小图片的大小。其他情况自行选择。

4.WebP 的兼容现状

安卓中国中有明确的说明:https://developer.android.google.cn/guide/topics/media/media-formats.html
在这里插入图片描述

4.2.1 + 对于 WEBP 的d ecode、encode 是完全支持的(包含半透明的webp图)

4.0 + 到 4.2.1 ,只支持完全不透明的 decode、encode 的 WEBP 图

4.0  以下,应该是默认不支持 WEBP了

从上面可以发现,对 WEBP 完全支持必须是 4.2.1 以上,目前大部分安卓手机应该都在这个范围。如果说要在安卓 4.0 以下使用 WEBP 的话,需要使用 libwebp 进行支持。

二、libwebp

1.libwebp 的配置

1 下载

在谷歌中国中进行 libweb 源码下载
在这里插入图片描述
解压出来,可以发现压缩包中提供多种编码方式,这边直接采用 mk 方式进行编码。

2 修改 Android.mk
Android.mk 文件中进行修改,添加:

ENABLE_SHARED := 1

在这里插入图片描述

在 libwebp 下面配置中添加一个源文件:

swig/libwebp_java_wrap.c \

在这里插入图片描述

3 新建 Application.mk
在 libwebp 目录下新建 Application.mk 文件。
在这里插入图片描述
指定支持的 CPU 架构以及安卓版本,根据实际情况进行调整:

APP_ABI := armeabi-v7a x86
APP_PLATFORM = android-14

4 编译
修改目录名为 jni,在父级目录开启命令行。
在这里插入图片描述
执行 ndk-build.cmd 命令。
在这里插入图片描述
等待编译成功,会生成一个 jni 同级目录 libs,在这个目录下有我们需要的 CPU 架构对应的库文件。
在这里插入图片描述

5 导入 so
在 main 下面新建 jnilibs 文件夹,把上面编译生成的 libwebp.so 拷贝到项目中。
在这里插入图片描述
6 添加 jar
把 libwebp 下的 libwebp.jar 这个包添加到项目中。
在这里插入图片描述

二、libwebp 的使用

1.编码

	/**
	 * 将 bitmap 使用 libwebp 编码为 webp 图片
	 *
	 * @param bitmap
	 */
	private void encodeWebp(Bitmap bitmap, int quality) {
		long time = System.currentTimeMillis();
		//获取bitmap 宽高
		int width = bitmap.getWidth();
		int height = bitmap.getHeight();
		//获得bitmap中的 ARGB 数据
		ByteBuffer buffer = ByteBuffer.allocate(bitmap.getByteCount());
		bitmap.copyPixelsToBuffer(buffer);
		//编码 获得 webp格式文件数据
		byte[] bytes = libwebp.WebPEncodeRGBA(buffer.array(), width, height, width * 4, quality);
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(Environment
					.getExternalStorageDirectory() + "/libwebp" + quality +".webp");
			fos.write(bytes);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (null != fos) {
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		Log.e(TAG, "111 zx libwebp 编码图片, 质量 " + quality
				+ " 耗时:" + (System.currentTimeMillis() - time));
	}

libwebp.WebPEncodeRGBA 需要五个参数,分别是图片数据、宽、高、一行数据的字节数、图片质量。(这边 bitmap 默认·是 ARGB_8888,所以是 width * 4)。

2.解码

	/**
	 * libwebp 解码 webp图片
	 */
	private Bitmap decodeWebp(String path) {

		byte[] bytes = null;
		InputStream is = null;
		try {
			is = new FileInputStream(path);
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			byte[] buffer = new byte[2048];
			int len;
			while ((len = is.read(buffer)) != -1) {
				bos.write(buffer,0,len);
			}
			bytes = bos.toByteArray();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			try {
				if (is != null){
					is.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		//将webp格式的数据转成 argb
		int[] width = new int[1];
		int[] height = new int[1];
		byte[] argb = libwebp.WebPDecodeARGB(bytes, bytes.length, width, height);
		//将argb byte数组转成 int数组
		int[] pixels = new int[argb.length/4];
		ByteBuffer.wrap(argb).asIntBuffer().get(pixels);
		//获得bitmap
		Bitmap bitmap = Bitmap.createBitmap(pixels, width[0], height[0], Bitmap.Config.ARGB_8888);
		return bitmap;
	}

3.效率
在这里插入图片描述

跟上面的 webp 编解码对比,在编码速度上相对会快一些,解码速度差不多。(不同手机结果可能有差异)

三、完整代码

MainActivity :

package com.xiaoyue.libwebp;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import com.google.webp.libwebp;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;

public class MainActivity extends AppCompatActivity {

	static {
		System.loadLibrary("webp");
	}

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.xiaoyue_png);

        compress(bitmap, Bitmap.CompressFormat.JPEG, 100);
        compress(bitmap, Bitmap.CompressFormat.JPEG, 70);
        compress(bitmap, Bitmap.CompressFormat.JPEG, 50);
        compress(bitmap, Bitmap.CompressFormat.JPEG, 30);
        compress(bitmap, Bitmap.CompressFormat.JPEG, 0);

        compress(bitmap, Bitmap.CompressFormat.WEBP, 100);
        compress(bitmap, Bitmap.CompressFormat.WEBP, 70);
        compress(bitmap, Bitmap.CompressFormat.WEBP, 50);
        compress(bitmap, Bitmap.CompressFormat.WEBP, 30);
        compress(bitmap, Bitmap.CompressFormat.WEBP, 0);

		encodeWebp(bitmap, 100);
		encodeWebp(bitmap, 70);
		encodeWebp(bitmap, 50);
		encodeWebp(bitmap, 30);
		encodeWebp(bitmap, 0);
    }

    /**
     * 压缩图片到文件
     * @param bitmap 待压缩图片
     * @param format 压缩的格式
     * @param quality   质量
     */
    private void compress(Bitmap bitmap, Bitmap.CompressFormat format, int quality){

        String path;
        if (format == Bitmap.CompressFormat.WEBP){
            path = Environment.getExternalStorageDirectory().getPath()
                    + "/xiaoyue_png" + quality + ".webp";
        }else {
            path = Environment.getExternalStorageDirectory().getPath()
                    + "/xiaoyue_png" + quality + ".jpeg";
        }

        long time = System.currentTimeMillis();
        FileOutputStream outputStream;
        try{
            outputStream = new FileOutputStream(path);
            bitmap.compress(format, quality, outputStream);
            outputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        Log.e(TAG, "111 zx 编码 " + format + " 图片, 质量 " + quality
                + " 耗时:" + (System.currentTimeMillis() - time));

        time = System.currentTimeMillis();
        BitmapFactory.decodeFile(path);
        Log.e(TAG, "111 zx 解码 " + format + " 图片, 质量 " + quality
                + " 耗时:" + (System.currentTimeMillis() - time));
    }

	/**
	 * libwebp 解码 webp图片
	 */
	private Bitmap decodeWebp(String path) {

		byte[] bytes = null;
		InputStream is = null;
		try {
			is = new FileInputStream(path);
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			byte[] buffer = new byte[2048];
			int len;
			while ((len = is.read(buffer)) != -1) {
				bos.write(buffer,0,len);
			}
			bytes = bos.toByteArray();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			try {
				if (is != null){
					is.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		//将webp格式的数据转成 argb
		int[] width = new int[1];
		int[] height = new int[1];
		byte[] argb = libwebp.WebPDecodeARGB(bytes, bytes.length, width, height);
		//将argb byte数组转成 int数组
		int[] pixels = new int[argb.length/4];
		ByteBuffer.wrap(argb).asIntBuffer().get(pixels);
		//获得bitmap
		Bitmap bitmap = Bitmap.createBitmap(pixels, width[0], height[0], Bitmap.Config.ARGB_8888);
		return bitmap;
	}

	/**
	 * 将 bitmap 使用 libwebp 编码为 webp 图片
	 *
	 * @param bitmap
	 */
	private void encodeWebp(Bitmap bitmap, int quality) {
		long time = System.currentTimeMillis();
		//获取bitmap 宽高
		int width = bitmap.getWidth();
		int height = bitmap.getHeight();
		//获得bitmap中的 ARGB 数据
		ByteBuffer buffer = ByteBuffer.allocate(bitmap.getByteCount());
		bitmap.copyPixelsToBuffer(buffer);
		//编码 获得 webp格式文件数据
		byte[] bytes = libwebp.WebPEncodeRGBA(buffer.array(), width, height, width * 4, quality);
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(Environment
					.getExternalStorageDirectory() + "/libwebp" + quality +".webp");
			fos.write(bytes);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (null != fos) {
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		Log.e(TAG, "111 zx libwebp 编码图片, 质量 " + quality
				+ " 耗时:" + (System.currentTimeMillis() - time));

		time = System.currentTimeMillis();
		decodeWebp(Environment
				.getExternalStorageDirectory() + "/libwebp" + quality +".webp");
		Log.e(TAG, "111 zx libwebp 解码图片, 质量 " + quality
				+ " 耗时:" + (System.currentTimeMillis() - time));
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值