关于4.0以下版本无法使用webp的问题,我们可以通过libwebp的decode与encode解决
在实现libWebP的编码与解码的同时,我们顺便验证一下webP与jpg和png的编码解码时间消耗情况
webp和jpeg的解码时间对比
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);
ImageView test = findViewById(R.id.test);
long l = System.currentTimeMillis();
//ARGB
BitmapFactory.decodeResource(getResources(), R.drawable.splash_bg);
Log.e(TAG, "解码webp图片耗时:" + (System.currentTimeMillis() - l));
l = System.currentTimeMillis();
BitmapFactory.decodeResource(getResources(), R.drawable.splash_bg_jpeg);
Log.e(TAG, "解码jpeg图片耗时:" + (System.currentTimeMillis() - l));
}
}
结果:
解码webp图片耗时:48
解码jpeg图片耗时:50
webp和jpeg的编码时间对比
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);
ImageView test = findViewById(R.id.test);
long l = System.currentTimeMillis();
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.splash_bg_png);
l = System.currentTimeMillis();
compressBitmap(bitmap, Bitmap.CompressFormat.WEBP, Environment
.getExternalStorageDirectory() + "/test.webp");
Log.e(TAG, "编码webp图片耗时:" + (System.currentTimeMillis() - l));
l = System.currentTimeMillis();
compressBitmap(bitmap, Bitmap.CompressFormat.JPEG, Environment
.getExternalStorageDirectory() + "/test.jpeg");
Log.e(TAG, "编码jpeg图片耗时:" + (System.currentTimeMillis() - l));
}
//编码速度webP比jpeg慢了10倍,解码差不多
private void compressBitmap(Bitmap bitmap, Bitmap.CompressFormat format, String file) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
bitmap.compress(format, 75, fos);
if (null != fos) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
结果:
编码webp图片耗时:501
编码jpeg图片耗时:52
编码方面webp被jpeg完虐,但是需要的情况比较少,所以不是问题
实现libwebp的解码,针对4.0一下机型用这种方式
libwebp解码webp
//针对4.0以下设备可以使用这种方式
/**
* libwebp解码webp图片
*/
private Bitmap decodeWebp() {
InputStream is = getResources().openRawResource(R.drawable.splash_bg);
byte[] bytes = stream2Bytes(is);
try {
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;
}
byte[] stream2Bytes(InputStream is) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[2048];
int len;
try {
while ((len = is.read(buffer)) != -1) {
bos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}
return bos.toByteArray();
}
实现libwebp的编码
/**
* 将bitmap 使用libwebp编码为 webp图片
*
* @param bitmap
*/
private void encodeWebp(Bitmap bitmap) {
//获取bitmap 宽高
int width = bitmap.getWidth();
int height = bitmap.getHeight();
//获得bitmap中的 ARGB 数据
ByteBuffer buffer = ByteBuffer.allocate(bitmap.getByteCount());
bitmap.copyPixelsToBuffer(buffer);
//编码 获得 webp格式文件数据,width*4是因为ARGB占四个字节
byte[] bytes = libwebp.WebPEncodeRGBA(buffer.array(), width, height, width * 4, 75);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(Environment
.getExternalStorageDirectory() + "/libwebp.webp");
fos.write(bytes);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != fos) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
完整代码
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
static {
System.loadLibrary("webp");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView test = findViewById(R.id.test);
long l = System.currentTimeMillis();
//ARGB
BitmapFactory.decodeResource(getResources(), R.drawable.splash_bg);
Log.e(TAG, "解码webp图片耗时:" + (System.currentTimeMillis() - l));
l = System.currentTimeMillis();
BitmapFactory.decodeResource(getResources(), R.drawable.splash_bg_jpeg);
Log.e(TAG, "解码jpeg图片耗时:" + (System.currentTimeMillis() - l));
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.splash_bg_png);
l = System.currentTimeMillis();
compressBitmap(bitmap, Bitmap.CompressFormat.WEBP, Environment
.getExternalStorageDirectory() + "/test.webp");
Log.e(TAG, "编码webp图片耗时:" + (System.currentTimeMillis() - l));
l = System.currentTimeMillis();
compressBitmap(bitmap, Bitmap.CompressFormat.JPEG, Environment
.getExternalStorageDirectory() + "/test.jpeg");
Log.e(TAG, "编码jpeg图片耗时:" + (System.currentTimeMillis() - l));
l = System.currentTimeMillis();
encodeWebp(bitmap);
Log.e(TAG, "libwebp编码图片耗时:" + (System.currentTimeMillis() - l));
test.setImageBitmap(decodeWebp());
}
byte[] stream2Bytes(InputStream is) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[2048];
int len;
try {
while ((len = is.read(buffer)) != -1) {
bos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}
return bos.toByteArray();
}
//针对4.0以下设备可以使用这种方式
/**
* libwebp解码webp图片
*/
private Bitmap decodeWebp() {
InputStream is = getResources().openRawResource(R.drawable.splash_bg);
byte[] bytes = stream2Bytes(is);
try {
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) {
//获取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, 75);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(Environment
.getExternalStorageDirectory() + "/libwebp.webp");
fos.write(bytes);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != fos) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//编码速度webP比jpeg慢了10倍,解码差不多
private void compressBitmap(Bitmap bitmap, Bitmap.CompressFormat format, String file) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
bitmap.compress(format, 75, fos);
if (null != fos) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
上述执行需要用到上一章节编译的libwebp.jar和libwebp.so,如图
提供依据编译好的so包:
https://pan.baidu.com/s/1hl1f2V2D1Ivf3-etnOhNow
https://pan.baidu.com/s/1906GsbPy1lp_T4BevThq5A